diff --git a/.gitignore b/.gitignore index 3348c23..37cf9d1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,11 @@ .idea/ # vscode related +.vscode .vscode/* !.vscode/tasks.json !.vscode/settings.json +devtools_options.yaml # Flutter/Dart/Pub related # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..38762f0 --- /dev/null +++ b/.metadata @@ -0,0 +1,33 @@ +# 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: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + - platform: android + create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + - platform: ios + create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + + # 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/AUTHORS b/AUTHORS new file mode 100644 index 0000000..ab4f32d --- /dev/null +++ b/AUTHORS @@ -0,0 +1,14 @@ +# 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. +Codemate Ltd. +Joonas Kerttula +Joona Petrell +Jukka Alavesa +Ville Välimaa +Vesse Saastamoinen +Mikko Katajaharju +Timo Pieti diff --git a/CHANGELOG.md b/CHANGELOG.md index a90a8c9..5497cf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,59 @@ -## 0.1.0-pre1 +## 0.2.0-beta -This is the first pre-release of the Google Maps Navigation package for Flutter. It is an early look at the package and is intended for testing and feedback collection. The functionalities and APIs in this version are subject to change. +This is the beta release of the Google Maps Navigation package for Flutter. It is an early look at the package and is intended for testing and feedback collection. The functionalities and APIs in this version are subject to change. + +**Key Features:** +- `setDestinations` now supports a `RouteTokenOptions` parameter. +- Added support for custom marker icons: + - `registerBitmapImage` to register `ImageDescriptor`. + - `unregisterImage` to unregister `ImageDescriptor`. + - Registered `ImageDescriptor` can be used as icon for `MarkerOptions`. +- Added following event listeners: + - `onNavigationUIEnabledChanged` + - `onMyLocationClicked` + - `onMyLocationButtonClicked` + - `onCameraMoveStarted` + - `onCameraMove` + - `onCameraIdle` + - `onCameraStartedFollowingLocation` + - `onCameraStoppedFollowingLocation` +- Added `setConsumeMyLocationButtonClickEventsEnabled` method to control if the default my location button click event should be consumed by the plugin or not. +- Added the following methods to control zoom level preferences of the map: `setMinZoomPreference`, `setMaxZoomPreference`, +`getMinZoomPreference`, `getMaxZoomPreference` and `resetMinMaxZoomPreference`. +- Added `GoogleMapsNavigator.getNavSDKVersion()` method to fetch the Navigation SDK version +- `GoogleMapsNavigator.initializeNavigationSession()` now accepts optional parameter `abnormalTerminationReportingEnabled` to enable/disable reporting abnormal SDK terminations such as app crashes +- Improved error handling and reporting. + +**BREAKING CHANGES:** +- **GoogleNavigationViewController** Following gesture and UI option setters have been renamed: + - `enableNavigationUI({required bool})` to `setNavigationUIEnabled(bool)` + - `enableNavigationHeader({required bool})` to `setNavigationHeaderEnabled(bool)` + - `enableNavigationFooter({required bool})` to `setNavigationFooterEnabled(bool)` + - `enableNavigationTripProgressBar({required bool})` to `setNavigationTripProgressBarEnabled(bool)` + - `enableSpeedLimitIcon({required bool})` to `setSpeedLimitIconEnabled(bool)` + - `enableSpeedometer({required bool})` to `setSpeedometerEnabled(bool)` + - `enableMyLocation({required bool})` to `setMyLocationEnabled(bool)` + - `enableMyLocationButton({required bool})` to `setMyLocationButtonEnabled(bool)` + - `enableRecenterButton({required bool})` to `setRecenterButtonEnabled(bool)` + - `enableZoomGestures({required bool}) setZoomGesturesEnabled(bool)` + - `enableZoomControls({required bool})` to `setZoomControlsEnabled(bool)` + - `enableCompass({required bool})` to `setCompassEnabled(bool)` + - `enableRotateGestures({required bool})` to `setRotateGesturesEnabled(bool)` + - `enableScrollGestures({required bool})` to `setScrollGesturesEnabled(bool)` + - `enableScrollGesturesDuringRotateOrZoom({required bool})` to `setScrollGesturesDuringRotateOrZoomEnabled(bool)` + - `enableTiltGestures({required bool})` to `setTiltGesturesEnabled(bool)` + - `enableTraffic({required bool})` to `setTrafficEnabled(bool)` + - `enableMapToolbar({required bool})` to `setMapToolbarEnabled(bool)` + - `enableIncidentCards({required bool})` to `setTrafficIncidentCardsEnabled(bool)` + - `isIncidentCardsEnabled()` to `isTrafficIncidentCardsEnabled` + +- **GoogleMapsNavigationView:** + - `initialNavigationUiEnabled` boolean has been renamed to `initialNavigationUIEnabledPreference` enumeration + - Initial camera position defaults to zoom level 3.0 instead of 0.0 + +## 0.1.0-beta + +This is the beta release of the Google Maps Navigation package for Flutter. It is an early look at the package and is intended for testing and feedback collection. The functionalities and APIs in this version are subject to change. **Key Features:** - Integration of Google Maps Navigation with Flutter. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27900f2..8998cac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,159 +2,133 @@ _See also: [Flutter's code of conduct](https://flutter.io/design-principles/#code-of-conduct)_ -## 1. Things you will need +## 1. Essential Setup for Contributors -- Linux, Mac OS X, or Windows. -- [git](https://git-scm.com) (used for source version control). -- An IDE such as [Android Studio](https://developer.android.com/studio) or [Visual Studio Code](https://code.visualstudio.com/). -- [`swiftformat`](https://github.com/nicklockwood/SwiftFormat) (available via brew on macOS, on Windows install Swift toolchain and build SwiftFormat from git sources) - -## 2. Forking & cloning the repository - -- Ensure all the dependencies described in the previous section are installed. -- Fork `https://github.com/googlemaps/flutter-navigation-sdk` into your own GitHub account. If - you already have a fork, and are now installing a development environment on - a new machine, make sure you've updated your fork so that you don't use stale - configuration options from long ago. -- If you haven't configured your machine with an SSH key that's known to github, then - follow [GitHub's directions](https://help.github.com/articles/generating-ssh-keys/) - to generate an SSH key. -- `git clone git@github.com:/googlemaps/flutter-navigation-sdk.git` -- `git remote add upstream git@github.com:googlemaps/flutter-navigation-sdk.git` (So that you - fetch from the master repository, not your clone, when running `git fetch` - et al.) - -## 3. Environment Setup - -This project uses [Melos](https://github.com/invertase/melos) to manage the project and dependencies. -Melos is a tool that optimizes the workflow around managing multi-package repositories with git and Pub. - -To install Melos, run the following command from your terminal: +- **Operating System:** Linux, macOS, or Windows. +- **Version Control:** [git](https://git-scm.com). +- **Development Environment:** An IDE such as [Android Studio](https://developer.android.com/studio) or [Visual Studio Code](https://code.visualstudio.com/). +- **Code Formatting:** [`swiftformat`](https://github.com/nicklockwood/SwiftFormat) (available via brew on macOS, on Windows install Swift toolchain and build SwiftFormat from git sources). +### 1.1. Installing swiftformat +The CI is locked to swiftformat 0.53 version which you can install with the command below: ```bash -dart pub global activate melos +curl -O https://raw.githubusercontent.com/Homebrew/homebrew-core/86f85aaa82beba49f8a5aabf3a22508c9249f188/Formula/s/swiftformat.rb +brew install swiftformat.rb ``` -Next, at the root of your locally cloned repository bootstrap the projects dependencies: +## 2. Setting Up Your Local Repository -```bash -melos bootstrap -``` +- **Preparation:** Before starting, make sure you have all dependencies installed as mentioned in the prior section. +- **Fork the Repository:** Navigate to `https://github.com/googlemaps/flutter-navigation-sdk` and create a fork in your GitHub account. +- **SSH Key Configuration:** If your machine doesn't have an SSH key registered with GitHub, generate one following the instructions at [Generating SSH Keys on GitHub.](https://help.github.com/articles/generating-ssh-keys/). +- **Clone Your Fork:** Use the command `git clone git@github.com:/google_maps_flutter_navigation.git` to clone the repository to your local machine. +- **Add remote upstream:** Establish a link to the main repository for updates using command `git remote add upstream git@github.com:googlemaps/flutter-navigation-sdk.git` This ensures you pull changes from the original source, not just your clone, when using git fetch and similar commands. -The bootstrap command locally links all dependencies within the project without having to -provide manual [`dependency_overrides`](https://dart.dev/tools/pub/pubspec). This allows all -plugins, examples and tests to build from the local clone project. +## 3. Install Melos -> You do not need to run `flutter pub get` once bootstrap has been completed. +This project leverages [Melos](https://github.com/invertase/melos) to manage the project and its dependencies. -> If you're using [fvm](https://fvm.app/) you might need to specify the sdk-path: `melos bs --sdk-path=/Users/user/fvm/default/` +Run the following command to install Melos: -The [Melos extension]([https://marketplace.visualstudio.com/items?itemName=blaugold.melos-code]) integrates Melos with VS Code +```bash +dart pub global activate melos +``` ## 4. Automatically generated MethodChannel with Pigeon -### Use - -Google Maps Navigation Flutter Plugins uses [pigeon](https://github.com/flutter/packages/tree/main/packages/pigeon) to generate the `MethodChannel` API layer between Dart and the native platforms. -To modify the messages sent with Pigeon (i.e. API code between Dart and native platforms), you can modify the `pigeons/messages.dart` file in the corresponding folder and regenerate the code running the below noted melos command. +### Using pigeon +Google Maps Navigation Flutter Plugins utilizes [pigeon](https://github.com/flutter/packages/tree/main/packages/pigeon) to generate the `MethodChannel` API layer between Dart and the native platforms. +To modify the messages sent with Pigeon (i.e., the API code between Dart and native platforms), you can edit the `pigeons/messages.dart` file in the corresponding folder and regenerate the code by running the following melos command: ``` melos run generate:pigeon ``` -Don't forget to run the formatter on the generated files. -Note: melos script runs formatter automatically after pigeon generation. - -### Tests - -To tests the created interface, you can mock the interface directly with: +Remember to format the generated files using the formatter. -```dart -TestNAMEHostApi.setup(MockNAMEApp()); -``` +> [!NOTE] +> The melos script automatically runs the formatter after pigeon generation. -How to add a unit test to a new method: +### Testing pigeon generated code -1. In the mock Platform API pass the method calls to the respective Mock API generated by Mockito. +To test the created interface, you can mock the interface directly with: ```dart -@override -Future myMethod() async { - return mockTestApi.myMethod(); -} +late MockTestNAMEHostApi mockApi; +TestNAMEHostApi.setup(mockApi); ``` -2. Mock the return value (if the function has one). +Add a unit test to a new method: + +1. Mock the return value (if the function has one). ```dart -when(mockApi.myMethod(any)) .thenAnswer((Invocation _) async => returnValueIn); +when(mockApi.newMethod(any)).thenReturn(returnValueIn); ``` 3. Call the public API. ```dart -returnValueOut = api.myMethod(parameterIn) +returnValueOut = await GoogleMapsNavigationPlatform.instance.newMethod(parameterIn) ``` 4. Check that the parameters and return values passed between the public API and platform match. ```dart -final VerificationResult result = verify(mockApi.myMethod(captureAny)); +final VerificationResult result = verify(mockApi.newMethod(captureAny)); final MyType parameterOut = result.captured[0] as MyType; -expect(parameterIn, parameterOut) -expect(returnValueIn, returnValueOut) +expect(parameterIn.param, parameterOut.param) +expect(returnValueIn.param, returnValueOut.param) ``` -## 5. Running an example +See examples in `test/` folders. + +## 5. Running the Navigation Example -Each plugin provides an example app which aims to showcase the main use-cases of each plugin. +The Google Maps Flutter Navigation plugin provides an example app that showcases its main use-cases. -To run an example, run the `flutter run` command from the `example` directory of each plugins main -directory. For example, for Google Maps Flutter Navigation example: +To run the Navigation example, navigate to the `example` directory of the plugin and run the app: ```bash -cd packages/google_maps_navigation/example -flutter run +cd example +flutter run --dart-define MAPS_API_KEY=YOUR_API_KEY ``` -Using Melos (installed in step 3), any changes made to the plugins locally will also be reflected within all -example applications code automatically. - ## 6. Running tests -Google Maps Flutter Navigation packages comprises of a number of tests for each plugin, either integration or unit tests. +Google Maps Flutter Navigation package has integration and unit tests. ### Unit tests -Unit tests are responsible for ensuring expected behavior whilst developing the plugins Dart code. To run unit tests for a specific plugin, run the -`flutter test` command from the plugins root directory. For example, Google Maps Flutter Navigation tests can be run -with the following commands: +To run unit tests for the Google Maps Flutter Navigation plugin, navigate to the plugin's root directory and execute the `flutter test` command. Use the following command: ```bash -cd packages/google_maps_navigation flutter test ``` To run unit tests on Android call ```bash -cd packages/google_maps_navigation/example +cd example/android ./gradlew test ``` -To run unit tests on iOS open Test Navigator in Xcode -and tap play icon button for RunnerTests. +To run unit tests on iOS, follow these steps: +1. Open Xcode. +2. Navigate to the Test Navigator. +3. Find and select the "RunnerTests" target. +4. Click on the play icon button to run the tests. ### Integration tests -Integration tests are responsible for ensuring that the individual plugins are working together as expected. Patrol is used for the integration tests to simplify interactions with native elements. To use patrol, you first need to activate the patrol_cli. +Integration tests are responsible for ensuring that the plugin works against the native Navigation SDK for both Android and iOS platforms. Patrol is used for the integration tests to simplify interactions with native elements. To use patrol, you first need to activate the patrol_cli. ```bash -flutter pub global activate patrol_cli 2.2.2 +flutter pub global activate patrol_cli 2.5.0 ``` -Ensure everything patrol needs is set up with the command: +To ensure that all necessary dependencies for patrol are properly set up, run the following command: ```bash patrol doctor @@ -163,23 +137,15 @@ patrol doctor Google Maps Flutter Navigation integration tests can be run with the following command: ```bash -cd packages/google_maps_navigation/example -dart integration_test/run_tests.dart +cd ./example +patrol test --dart-define MAPS_API_KEY=YOUR_API_KEY ``` -On ios you can pass the MAPS_API_KEY with following command: - -```bash -cd packages/google_maps_navigation/example -dart integration_test/run_tests.dart --dart-defines=MAPS_API_KEY=YOUR_API_KEY -``` - - To only run a specific test file, use patrol command with -t flag. For example to run a navigation_test.dart run it with the following command: ```bash -cd packages/google_maps_navigation/example -patrol test -t integration_test/navigation_test.dart +cd ./example +patrol test --dart-define MAPS_API_KEY=YOUR_API_KEY -t integration_test/navigation_test.dart ``` Test report should appear in the build folder: @@ -195,60 +161,81 @@ When adding new tests, add the location dialog and ToS check to the beginning of await checkLocationDialogAndTosAcceptance($); ``` -For debugging the tests, you can add debugPrint() functions in your test and use patrol develop mode with --verbose falg to see the printed messages. To run navigation_test.dart in develop mode, use the following command: +For debugging the tests, you can add `debugPrint()` functions in your test and use patrol develop mode with the `--verbose` flag to see the printed messages. To run `navigation_test.dart` in develop mode, use the following command: ```bash -cd packages/google_maps_navigation/example -patrol develop --verbose -t integration_test/navigation_test.dart +cd ./example +patrol develop --dart-define MAPS_API_KEY=YOUR_API_KEY --verbose -t integration_test/navigation_test.dart ``` -FYI, the patrol develop mode's "hot restart" doesn't work properly with all the test files. +Please note that the "hot restart" feature in patrol's develop mode may not work correctly with all test files. + +#### Android emulator setup + +If the patrol tests fail to run on the Android emulator due to insufficient RAM, increase the emulator's default RAM allocation to ensure proper test execution. ## 7. Contributing code -We gladly accept contributions via GitHub pull requests. +We welcome contributions through GitHub pull requests. -Please peruse the -[Flutter style guide](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo) and -[design principles](https://flutter.io/design-principles/) before -working on anything non-trivial. These guidelines are intended to -keep the code consistent and avoid common pitfalls. +Before working on any significant changes, please review the [Flutter style guide](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo) and [design principles](https://flutter.io/design-principles/). These guidelines help maintain code consistency and avoid common pitfalls. -To start working on a patch: +To begin working on a patch, follow these steps: + +1. Fetch the latest changes from the upstream repository: + ```bash + git fetch upstream + ``` + +2. Create a new branch based on the latest upstream master branch: + ```bash + git checkout upstream/master -b + ``` -1. `git fetch upstream` -2. `git checkout upstream/master -b ` 3. Start coding! -Once you have made your changes, ensure that it passes the internal analyzer & formatting checks. The following -commands can be run locally to highlight any issues before committing your code: +Before committing your changes, it's important to ensure that your code passes the internal analyzer and formatting checks. You can run the following commands locally to identify any issues: -```bash -# Run the analyze check -melos run flutter-analyze +- Run the analyze check: + ```bash + melos run flutter-analyze + ``` -# Format code -melos run format -``` +- Format your code: + ```bash + melos run format + ``` -If you have changed pigeon messages, make sure to run: +If you have made changes to pigeon messages, don't forget to generate the necessary code by running: ```bash melos run generate:pigeon ``` -If you have changed files that have mocked tests, make sure to run: +If you have changed files that have mocked tests, make sure to run the following command: ```bash melos run generate:mocks ``` -And run affecting tests locally. +And run affecting tests locally to make sure they still pass. + +Assuming all is successful, commit and push your code using the following commands: -Assuming all is successful, commit and push your code: +1. Stage your changes: + ```bash + git add . + ``` -1. `git commit -a -m ""` -2. `git push origin ` +2. Commit your changes with an informative commit message: + ```bash + git commit -m "" + ``` + +3. Push your changes to the remote repository: + ```bash + git push origin + ``` To send us a pull request: @@ -256,74 +243,47 @@ To send us a pull request: go to `https://github.com/googlemaps/flutter-navigation-sdk` and click the "Compare & pull request" button -Please make sure all your check-ins have detailed commit messages explaining the patch. +Please ensure that all your commits have detailed commit messages explaining the changes made. When naming the title of your pull request, please follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) -guide. For example, for a fix to the Driver plugin: +guide. For example, for a fix to the plugin: -`fix(google_maps_flutter_driver): fixed a bug!` +`fix: Fixed a bug!` -Plugins tests are run automatically on contributions using GitHub Actions. Depending on -your code contributions, various tests will be run against your updated code automatically. +Automated tests will be run on your contributions using GitHub Actions. Depending on +your code changes, various tests will be performed automatically. -Once you've gotten an LGTM from a project maintainer and once your PR has received -the green light from all our automated testing, wait for one the package maintainers -to merge the pull request. +Once you have received an LGTM (Looks Good To Me) from a project maintainer and once your pull request has passed all automated tests, please wait for one of the package maintainers to merge your changes. -You must complete the +Before contributing, please ensure that you have completed the [Contributor License Agreement](https://cla.developers.google.com/clas). -You can do this online, and it only takes a minute. -If you've never submitted code before, you must add your (or your -organization's) name and contact info to the [AUTHORS](AUTHORS) file. +This can be done online and only takes a few moments. +If you are submitting code for the first time, please add your (or your +organization's) name and contact information to the [AUTHORS](AUTHORS) file. -If you create a new file, do not forget to add the license header. You can use -[`addlicense`](https://github.com/google/addlicense) to add the license to all -necessary files. +This project uses Google's `addlicense` [here](https://github.com/google/addlicense) tool to add the license header to all necessary files. Running `addlicense` is a required step before committing any new files. To install `addlicense`, run: ```bash go install github.com/google/addlicense@latest ``` -Do not forget to add `$HOME/go/bin` to your `PATH`. If you are using Bash on -Linux or macOS, you need to add `export PATH="$HOME/go/bin:$PATH"` to your -`.bash_profile`. +Make sure to include `$HOME/go/bin` in your `PATH` environment variable. +If you are using Bash on Linux or macOS, add `export PATH="$HOME/go/bin:$PATH"` to your `.bash_profile`. -To add the license header to all files, run from the root of the repository: +To add the license header to all files, run the following command from the root of the repository: ```bash melos run add-license-header ``` This command uses `addlicense` with all necessary flags. -### The review process - -Newly opened PRs first go through initial triage which results in one of: - -- **Merging the PR** - if the PR can be quickly reviewed and looks good. -- **Closing the PR** - if the PR maintainer decides that the PR should not be merged. -- **Moving the PR to the backlog** - if the review requires non trivial effort and the issue isn't a priority; in this case the maintainer will: - - Make sure that the PR has an associated issue labeled with "plugin". - - Add the "backlog" label to the issue. - - Leave a comment on the PR explaining that the review is not trivial and that the issue will be looked at according to priority order. -- **Starting a non trivial review** - if the review requires non trivial effort and the issue is a priority; in this case the maintainer will: - - Add the "in review" label to the issue. - - Self assign the PR. - - -### Graduate packages - -Sometimes you may need to 'graduate' a package from a 'dev' or 'beta' (versions tagged like this: `0.10.0-dev.4`) to a stable version. Melos can also be used -to graduate multiple packages using the following steps: - -1. Switch to `master` branch locally. -2. Run 'git pull origin master'. -3. Run `git fetch --all` to make sure all tags and commits are fetched. -4. Run `melos version --graduate` to prompt a list of all packages to be graduated (You may also specifically select packages using the scope flag like this: `--scope="*driver*"`) -5. Run `git push --follow-tags` to push the auto commits and tags to the remote repository. -6. Run `melos publish` to dry run and confirm all packages are publishable. -7. Run `melos publish --no-dry-run` to now publish to Pub.dev. +To check the license header of all files, run from the root of the repository: +```bash +melos run check-license-header +``` ## 8. Contributing documentation -We gladly accept contributions to the plugin documentation. As our docs are also part of this repo, -see "Contributing code" above for how to prepare and submit a PR to the repo. \ No newline at end of file +We welcome contributions to the plugin documentation. The documentation for this project is generated using Dart Docs. All documentation for the app-facing API is described in Dart files. + +Please refer to the "Contributing code" section above for instructions on how to prepare and submit a pull request to the repository. diff --git a/README.md b/README.md index dbd33ab..8152166 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ class _NavigationSampleState extends State { body: _navigationSessionInitialized ? GoogleMapsNavigationView( onViewCreated: _onViewCreated, - initialNavigationUiEnabled: false, + initialNavigationUIEnabledPreference: NavigationUIEnabledPreference.disabled, // Other view initialization settings ) : const Center(child: CircularProgressIndicator()), @@ -150,7 +150,7 @@ class _NavigationSampleState extends State { void _onViewCreated(GoogleNavigationViewController controller) { _navigationViewController = controller; - controller.enableMyLocation(enabled: true); + controller.setMyLocationEnabled(true); // Additional setup can be added here. } diff --git a/analysis_options.yaml b/analysis_options.yaml index 1510eb3..8237f38 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -45,7 +45,7 @@ linter: - avoid_bool_literals_in_conditional_expressions # - 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_classes_with_only_static_members - avoid_double_and_int_checks - avoid_dynamic_calls - avoid_empty_else diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt index f561887..02df86f 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/Convert.kt @@ -79,6 +79,21 @@ object Convert { return mapOptions } + /** + * Converts pigeon [NavigationUIEnabledPreferenceDto] to [NavigationUIEnabledPreference]. + * + * @param preference pigeon [NavigationUIEnabledPreferenceDto]. + * @return [NavigationUIEnabledPreference]. + */ + private fun convertNavigationUIEnabledPreferenceFromDto( + preference: NavigationUIEnabledPreferenceDto + ): NavigationUIEnabledPreference { + return when (preference) { + NavigationUIEnabledPreferenceDto.AUTOMATIC -> NavigationUIEnabledPreference.AUTOMATIC + NavigationUIEnabledPreferenceDto.DISABLED -> NavigationUIEnabledPreference.DISABLED + } + } + /** * Converts pigeon [NavigationViewOptionsDto] to [NavigationViewOptions]. * @@ -88,7 +103,11 @@ object Convert { fun convertNavigationViewOptionsFromDto( options: NavigationViewOptionsDto ): NavigationViewOptions { - return NavigationViewOptions(navigationUiEnabled = options.navigationUIEnabled) + + return NavigationViewOptions( + navigationUiEnabledPreference = + convertNavigationUIEnabledPreferenceFromDto(options.navigationUIEnabledPreference) + ) } /** @@ -181,7 +200,7 @@ object Convert { * @param point Google Maps [LatLng]. * @return Pigeon [LatLngDto]. */ - fun convertLatLngToDto(point: LatLng): LatLngDto { + private fun convertLatLngToDto(point: LatLng): LatLngDto { return LatLngDto(point.latitude, point.longitude) } @@ -260,7 +279,7 @@ object Convert { */ fun convertWaypointFromDto(waypoint: NavigationWaypointDto): Waypoint { val builder = Waypoint.builder() - if (waypoint.target?.latitude != null && waypoint.target?.longitude != null) { + if (waypoint.target != null) { builder.setLatLng(waypoint.target.latitude, waypoint.target.longitude) } if (waypoint.preferSameSideOfRoad == true) { @@ -384,7 +403,7 @@ object Convert { * @param travelMode pigeon [TravelModeDto]. * @return Google Navigation [@RoutingOptions.TravelMode Int]. */ - private fun convertTravelModeFromDto(travelMode: TravelModeDto): Int { + fun convertTravelModeFromDto(travelMode: TravelModeDto): Int { return when (travelMode) { TravelModeDto.CYCLING -> RoutingOptions.TravelMode.CYCLING TravelModeDto.DRIVING -> RoutingOptions.TravelMode.DRIVING @@ -505,7 +524,11 @@ object Convert { * * @param markerOptions pigeon [MarkerOptionsDto]. */ - fun sinkMarkerOptions(markerOptions: MarkerOptionsDto, sink: MarkerOptionsSink) { + fun sinkMarkerOptions( + markerOptions: MarkerOptionsDto, + sink: MarkerOptionsSink, + imageRegistry: ImageRegistry + ) { sink.setAlpha(markerOptions.alpha.toFloat()) sink.setAnchor(markerOptions.anchor.u.toFloat(), markerOptions.anchor.v.toFloat()) sink.setDraggable(markerOptions.draggable) @@ -521,6 +544,10 @@ object Convert { sink.setTitle(markerOptions.infoWindow.title) sink.setVisible(markerOptions.visible) sink.setZIndex(markerOptions.zIndex.toFloat()) + markerOptions.icon.registeredImageId?.let { + val registeredImage = imageRegistry.findRegisteredImage(it) + sink.setIcon(registeredImage) + } ?: run { sink.setIcon(null) } } fun convertRouteSegmentTrafficDataToDto( @@ -530,6 +557,10 @@ object Convert { when (trafficData.status) { NavigationTrafficData.Status.OK -> RouteSegmentTrafficDataStatusDto.OK NavigationTrafficData.Status.UNAVAILABLE -> RouteSegmentTrafficDataStatusDto.UNAVAILABLE + null -> { + // Should never happen, added to suppress compiler warning. + throw FlutterError("nullTrafficDataStatus", "Traffic data status is null") + } } return RouteSegmentTrafficDataDto( @@ -543,6 +574,13 @@ object Convert { RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto.TRAFFICJAM NavigationRoadStretchRenderingData.Style.UNKNOWN -> RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto.UNKNOWN + null -> { + // Should never happen, added to suppress compiler warning. + throw FlutterError( + "nullTrafficDataRoadStretchRenderingDataListStyle", + "Traffic data road stretch rendering data list style is null" + ) + } } RouteSegmentTrafficDataRoadStretchRenderingDataDto( style, @@ -637,7 +675,8 @@ object Convert { ) ), visible = marker.isVisible, - zIndex = marker.zIndex.toDouble() + zIndex = marker.zIndex.toDouble(), + icon = registeredImageToImageDescriptorDto(markerController.registeredImage) ) } @@ -647,7 +686,7 @@ object Convert { * @param span pigeon [StyleSpanDto]. * @return google maps [StyleSpan]. */ - fun convertStyleSpan(span: StyleSpanDto): StyleSpan? { + private fun convertStyleSpan(span: StyleSpanDto): StyleSpan? { if (span.style.solidColor != null) { return StyleSpan(span.style.solidColor.toInt(), span.length) } @@ -667,7 +706,7 @@ object Convert { * @param span google maps [StyleSpan]. * @return pigeon [StyleSpanDto]. */ - fun convertStyleSpan(span: StyleSpan): StyleSpanDto? { + private fun convertStyleSpan(span: StyleSpan): StyleSpanDto { return StyleSpanDto(length = span.segments, style = StyleSpanStrokeStyleDto()) } @@ -677,7 +716,7 @@ object Convert { * @param strokeJointType pigeon class [StrokeJointTypeDto] * @return google maps [JointType] int value */ - fun convertStrokeJointType(strokeJointType: StrokeJointTypeDto): Int { + private fun convertStrokeJointType(strokeJointType: StrokeJointTypeDto): Int { return when (strokeJointType) { StrokeJointTypeDto.BEVEL -> JointType.BEVEL StrokeJointTypeDto.DEFAULTJOINT -> JointType.DEFAULT @@ -691,7 +730,7 @@ object Convert { * @param jointType google maps [JointType] int value * @return pigeon [StrokeJointTypeDto] */ - fun convertStrokeJointType(jointType: Int): StrokeJointTypeDto { + private fun convertStrokeJointType(jointType: Int): StrokeJointTypeDto { return when (jointType) { JointType.BEVEL -> StrokeJointTypeDto.BEVEL JointType.DEFAULT -> StrokeJointTypeDto.DEFAULTJOINT @@ -706,7 +745,7 @@ object Convert { * @param patternItem pigeon class [PatternItemDto] * @return google maps [PatternItem] class */ - fun convertPatternItem(patternItem: PatternItemDto): PatternItem { + private fun convertPatternItem(patternItem: PatternItemDto): PatternItem { return when (patternItem.type) { PatternTypeDto.DASH -> Dash(patternItem.length?.toFloat() ?: 0F) PatternTypeDto.DOT -> Dot() @@ -720,7 +759,7 @@ object Convert { * @param patternItem google maps [PatternItem]. * @return pigeon [PatternItemDto]. */ - fun convertPatternItem(patternItem: PatternItem): PatternItemDto { + private fun convertPatternItem(patternItem: PatternItem): PatternItemDto { return when (patternItem) { is Dash -> PatternItemDto(PatternTypeDto.DASH, patternItem.length.toDouble()) is Dot -> PatternItemDto(PatternTypeDto.DOT) @@ -773,7 +812,7 @@ object Convert { if (polylineOptions.zIndex != null) { sink.setZIndex(polylineOptions.zIndex.toFloat()) } - val spans = polylineOptions.spans.filterNotNull().map { convertStyleSpan(it) }.filterNotNull() + val spans = polylineOptions.spans.filterNotNull().mapNotNull { convertStyleSpan(it) } sink.setSpans(spans) } @@ -835,4 +874,25 @@ object Convert { clickable = circle.isClickable ) } + + /** + * Creates [ImageDescriptorDto] from [RegisteredImage] object. If registeredImage is null, returns + * id for default marker icon. + * + * @param registeredImage [RegisteredImage] object. + * @return [ImageDescriptorDto] object. + */ + fun registeredImageToImageDescriptorDto(registeredImage: RegisteredImage?): ImageDescriptorDto { + return if (registeredImage != null) { + ImageDescriptorDto( + registeredImage.imageId, + registeredImage.imagePixelRatio, + registeredImage.width, + registeredImage.height + ) + } else { + // For default marker icon + ImageDescriptorDto() + } + } } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsImageRegistryMessageHandler.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsImageRegistryMessageHandler.kt new file mode 100644 index 0000000..b80c476 --- /dev/null +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsImageRegistryMessageHandler.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Google LLC + * + * 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 + * + * https://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. + */ + +package com.google.maps.flutter.navigation + +class GoogleMapsImageRegistryMessageHandler(private val imageRegistry: ImageRegistry) : + ImageRegistryApi { + override fun registerBitmapImage( + imageId: String, + bytes: ByteArray, + imagePixelRatio: Double, + width: Double?, + height: Double? + ): ImageDescriptorDto { + return imageRegistry.registerBitmapImage(imageId, bytes, imagePixelRatio, width, height) + } + + override fun unregisterImage(imageDescriptor: ImageDescriptorDto) { + imageDescriptor.registeredImageId?.let { imageRegistry.unregisterImage(it) } + } + + override fun clearRegisteredImages() { + imageRegistry.clearRegisteredImages() + } + + override fun getRegisteredImages(): List { + return imageRegistry.registeredImages.map { Convert.registeredImageToImageDescriptorDto(it) } + } +} diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationPlugin.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationPlugin.kt index 6d25cd9..0d1aecf 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationPlugin.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationPlugin.kt @@ -26,14 +26,19 @@ import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter class GoogleMapsNavigationPlugin : FlutterPlugin, ActivityAware { private lateinit var viewRegistry: GoogleMapsNavigationViewRegistry private lateinit var viewMessageHandler: GoogleMapsNavigationViewMessageHandler + private lateinit var imageRegistryMessageHandler: GoogleMapsImageRegistryMessageHandler private lateinit var navigationViewEventApi: NavigationViewEventApi private lateinit var _binding: FlutterPlugin.FlutterPluginBinding private lateinit var lifecycle: Lifecycle + private lateinit var imageRegistry: ImageRegistry override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { viewRegistry = GoogleMapsNavigationViewRegistry() + imageRegistry = ImageRegistry() viewMessageHandler = GoogleMapsNavigationViewMessageHandler(viewRegistry) NavigationViewApi.setUp(binding.binaryMessenger, viewMessageHandler) + imageRegistryMessageHandler = GoogleMapsImageRegistryMessageHandler(imageRegistry) + ImageRegistryApi.setUp(binding.binaryMessenger, imageRegistryMessageHandler) navigationViewEventApi = NavigationViewEventApi(binding.binaryMessenger) _binding = binding binding.applicationContext.registerComponentCallbacks(viewRegistry) @@ -41,12 +46,14 @@ class GoogleMapsNavigationPlugin : FlutterPlugin, ActivityAware { override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { NavigationViewApi.setUp(binding.binaryMessenger, null) // Cleanup + ImageRegistryApi.setUp(binding.binaryMessenger, null) GoogleMapsNavigationSessionManager.destroyInstance() binding.applicationContext.unregisterComponentCallbacks(viewRegistry) } override fun onAttachedToActivity(binding: ActivityPluginBinding) { - val factory = GoogleMapsNavigationViewFactory(viewRegistry, navigationViewEventApi) + val factory = + GoogleMapsNavigationViewFactory(viewRegistry, navigationViewEventApi, imageRegistry) _binding.platformViewRegistry.registerViewFactory("google_maps_navigation", factory) GoogleMapsNavigationSessionManager.createInstance(_binding.binaryMessenger) val inspectorHandler = GoogleMapsNavigationInspectorHandler(viewRegistry) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt index 5854034..6103a63 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionManager.kt @@ -19,6 +19,7 @@ package com.google.maps.flutter.navigation import android.app.Activity import android.location.Location import com.google.android.gms.maps.model.LatLng +import com.google.android.libraries.navigation.CustomRoutesOptions import com.google.android.libraries.navigation.DisplayOptions import com.google.android.libraries.navigation.NavigationApi import com.google.android.libraries.navigation.NavigationApi.NavigatorListener @@ -34,6 +35,7 @@ import com.google.android.libraries.navigation.TermsAndConditionsCheckOption import com.google.android.libraries.navigation.TermsAndConditionsUIParams import com.google.android.libraries.navigation.TimeAndDistance import com.google.android.libraries.navigation.Waypoint +import com.google.maps.flutter.navigation.Convert.convertTravelModeFromDto import io.flutter.plugin.common.BinaryMessenger import java.lang.ref.WeakReference @@ -94,7 +96,9 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven Navigator.RemainingTimeOrDistanceChangedListener? = null private var roadSnappedLocationProvider: RoadSnappedLocationProvider? = null - private var roadSnappedLocationListener: RoadSnappedLocationProvider.LocationListener? = null + private var roadSnappedLocationListener: + RoadSnappedLocationProvider.GpsAvailabilityEnhancedLocationListener? = + null private var speedingListener: SpeedingListener? = null private var weakActivity: WeakReference? = null @@ -104,7 +108,7 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven } /** Convenience function for returning the activity. */ - fun getActivity(): Activity { + private fun getActivity(): Activity { return weakActivity?.get() ?: throw FlutterError("activityNotFound", "Activity not created.") } @@ -127,8 +131,17 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven } } + // Expose the navigator to the google_maps_driver side. + // DriverApi initialization requires navigator. + fun getNavigatorWithoutError(): Navigator? { + return navigator + } + /** Creates Navigator instance. */ - fun createNavigationSession(callback: (Result) -> Unit) { + fun createNavigationSession( + abnormalTerminationReportingEnabled: Boolean, + callback: (Result) -> Unit + ) { if (navigator != null) { // Navigator is already initialized, just re-register listeners. registerNavigationListeners() @@ -151,6 +164,9 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven return } + // Enable or disable abnormal termination reporting. + NavigationApi.setAbnormalTerminationReportingEnabled(abnormalTerminationReportingEnabled) + val listener = object : NavigatorListener { override fun onNavigatorReady(newNavigator: Navigator) { @@ -287,10 +303,8 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven Navigator.RemainingTimeOrDistanceChangedListener { val timeAndDistance = getNavigator().currentTimeAndDistance navigationSessionEventApi.onRemainingTimeOrDistanceChanged( - RemainingTimeOrDistanceChangedEventDto( - timeAndDistance.seconds.toDouble(), - timeAndDistance.meters.toDouble() - ) + timeAndDistance.seconds.toDouble(), + timeAndDistance.meters.toDouble() ) {} } } @@ -307,35 +321,26 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven val navigator = getNavigator() if (arrivalListener == null) { arrivalListener = - Navigator.ArrivalListener { // Show an onscreen message - navigationSessionEventApi.onArrival( - OnArrivalEventDto(Convert.convertWaypointToDto(it.waypoint)) - ) {} + Navigator.ArrivalListener { + navigationSessionEventApi.onArrival(Convert.convertWaypointToDto(it.waypoint)) {} } navigator.addArrivalListener(arrivalListener) } if (routeChangedListener == null) { routeChangedListener = - Navigator.RouteChangedListener { // Show an onscreen message when the route changes - navigationSessionEventApi.onRouteChanged(RouteChangedEventDto("")) {} - } + Navigator.RouteChangedListener { navigationSessionEventApi.onRouteChanged() {} } navigator.addRouteChangedListener(routeChangedListener) } if (reroutingListener == null) { - reroutingListener = - Navigator.ReroutingListener { - navigationSessionEventApi.onRerouting(ReroutingEventDto("")) {} - } + reroutingListener = Navigator.ReroutingListener { navigationSessionEventApi.onRerouting() {} } navigator.addReroutingListener(reroutingListener) } if (trafficUpdatedListener == null) { trafficUpdatedListener = - Navigator.TrafficUpdatedListener { - navigationSessionEventApi.onTrafficUpdated(TrafficUpdatedEventDto("")) {} - } + Navigator.TrafficUpdatedListener { navigationSessionEventApi.onTrafficUpdated() {} } navigator.addTrafficUpdatedListener(trafficUpdatedListener) } @@ -381,9 +386,36 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven waypoints: List, routingOptions: RoutingOptions, displayOptions: DisplayOptions, + routeTokenOptions: RouteTokenOptionsDto?, callback: (Result) -> Unit ) { try { + // If route toke options are present set token and travel mode if given. + if (routeTokenOptions != null) { + val customRoutesOptionBuilder = + CustomRoutesOptions.builder().setRouteToken(routeTokenOptions.routeToken) + if (routeTokenOptions.travelMode != null) { + customRoutesOptionBuilder.setTravelMode( + convertTravelModeFromDto(routeTokenOptions.travelMode) + ) + } + + val customRoutesOptions: CustomRoutesOptions + try { + customRoutesOptions = customRoutesOptionBuilder.build() + } catch (e: IllegalStateException) { + throw FlutterError( + "routeTokenMalformed", + "The route token passed is malformed", + e.message + ) + } + + getNavigator() + .setDestinations(waypoints, customRoutesOptions, displayOptions) + .setOnResultListener { callback(Result.success(it)) } + return + } getNavigator() .setDestinations(waypoints, routingOptions, displayOptions) .setOnResultListener { callback(Result.success(it)) } @@ -512,8 +544,8 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven } } - private fun sendNavigationSessionEvent(type: NavigationSessionEventTypeDto, message: String) { - navigationSessionEventApi.onNavigationSessionEvent(NavigationSessionEventDto(type, message)) {} + fun getNavSDKVersion(): String { + return NavigationApi.getNavSDKVersion() } /** @@ -624,20 +656,22 @@ private constructor(private val navigationSessionEventApi: NavigationSessionEven fun enableRoadSnappedLocationUpdates() { if (roadSnappedLocationListener == null) { roadSnappedLocationListener = - object : RoadSnappedLocationProvider.LocationListener { + object : RoadSnappedLocationProvider.GpsAvailabilityEnhancedLocationListener { override fun onLocationChanged(location: Location) { navigationSessionEventApi.onRoadSnappedLocationUpdated( - RoadSnappedLocationUpdatedEventDto(LatLngDto(location.latitude, location.longitude)) + LatLngDto(location.latitude, location.longitude) ) {} } override fun onRawLocationUpdate(location: Location) { navigationSessionEventApi.onRoadSnappedRawLocationUpdated( - RoadSnappedRawLocationUpdatedEventDto( - LatLngDto(location.latitude, location.longitude) - ) + LatLngDto(location.latitude, location.longitude) ) {} } + + override fun onGpsAvailabilityUpdate(isGpsAvailable: Boolean) { + navigationSessionEventApi.onGpsAvailabilityUpdate(isGpsAvailable) {} + } } getRoadSnappedLocationProvider()?.addLocationListener(roadSnappedLocationListener) } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionMessageHandler.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionMessageHandler.kt index 4c8712e..a206ccf 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionMessageHandler.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationSessionMessageHandler.kt @@ -25,8 +25,11 @@ class GoogleMapsNavigationSessionMessageHandler : NavigationSessionApi { return GoogleMapsNavigationSessionManager.getInstance() } - override fun createNavigationSession(callback: (Result) -> Unit) { - manager().createNavigationSession(callback) + override fun createNavigationSession( + abnormalTerminationReportingEnabled: Boolean, + callback: (Result) -> Unit + ) { + manager().createNavigationSession(abnormalTerminationReportingEnabled, callback) } override fun isInitialized(): Boolean { @@ -60,6 +63,10 @@ class GoogleMapsNavigationSessionMessageHandler : NavigationSessionApi { manager().resetTermsAccepted() } + override fun getNavSDKVersion(): String { + return manager().getNavSDKVersion() + } + override fun isGuidanceRunning(): Boolean { return manager().isGuidanceRunning() } @@ -72,16 +79,25 @@ class GoogleMapsNavigationSessionMessageHandler : NavigationSessionApi { manager().stopGuidance() } - override fun setDestinations(msg: DestinationsDto, callback: (Result) -> Unit) { - val waypoints = msg.waypoints.filterNotNull().map { Convert.convertWaypointFromDto(it) } - val displayOptions = Convert.convertDisplayOptionsFromDto(msg.displayOptions) + override fun setDestinations( + destinations: DestinationsDto, + callback: (Result) -> Unit + ) { + val waypoints = + destinations.waypoints.filterNotNull().map { Convert.convertWaypointFromDto(it) } + val displayOptions = Convert.convertDisplayOptionsFromDto(destinations.displayOptions) val routingOptions = - if (msg.routingOptions != null) { - Convert.convertRoutingOptionsFromDto(msg.routingOptions) + if (destinations.routingOptions != null) { + Convert.convertRoutingOptionsFromDto(destinations.routingOptions) } else { RoutingOptions() } - manager().setDestinations(waypoints, routingOptions, displayOptions) { + manager().setDestinations( + waypoints, + routingOptions, + displayOptions, + destinations.routeTokenOptions + ) { if (it.isSuccess) { callback(Result.success(Convert.convertRouteStatusToDto(it.getOrThrow()))) } else { diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt index dada6c9..1dd9512 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationView.kt @@ -26,6 +26,8 @@ import android.view.View import com.google.android.gms.maps.CameraUpdate import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.GoogleMap.OnCameraFollowLocationCallback +import com.google.android.gms.maps.GoogleMap.OnCameraMoveStartedListener import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener import com.google.android.gms.maps.GoogleMapOptions import com.google.android.gms.maps.model.CameraPosition @@ -48,6 +50,7 @@ internal constructor( private val viewId: Int, private val viewRegistry: GoogleMapsNavigationViewRegistry, private val navigationViewEventApi: NavigationViewEventApi, + private val imageRegistry: ImageRegistry ) : PlatformView { companion object { const val INVALIDATION_FRAME_SKIP_AMOUNT = 4 // Amount of skip frames before invalidation @@ -62,6 +65,11 @@ internal constructor( private val _polylines = mutableListOf() private val _circles = mutableListOf() + // Store preferred zoom values here because MapView getMinZoom and + // getMaxZoom always return min/max possible values and not the preferred ones. + private var _minZoomLevelPreference: Float? = null + private var _maxZoomLevelPreference: Float? = null + /// Default values for UI features. private var _isNavigationTripProgressBarEnabled: Boolean = false private var _isNavigationHeaderEnabled: Boolean = true @@ -69,7 +77,8 @@ internal constructor( private var _isRecenterButtonEnabled: Boolean = true private var _isSpeedLimitIconEnabled: Boolean = false private var _isSpeedometerEnabled: Boolean = false - private var _isIncidentCardsEnabled: Boolean = true + private var _isTrafficIncidentCardsEnabled: Boolean = true + private var _consumeMyLocationButtonClickEventsEnabled: Boolean = false // Nullable variable to hold the callback function private var _mapReadyCallback: ((Result) -> Unit)? = null @@ -88,13 +97,24 @@ internal constructor( _navigationView.onResume() // Initialize navigation view with given navigation view options - var navigatorInitialized = GoogleMapsNavigationSessionManager.getInstance().isInitialized() - var navigationViewEnabled = navigationOptions.navigationUiEnabled ?: navigatorInitialized + var navigationViewEnabled: Boolean = false + if ( + navigationOptions.navigationUiEnabledPreference == NavigationUIEnabledPreference.AUTOMATIC + ) { + val navigatorInitialized = GoogleMapsNavigationSessionManager.getInstance().isInitialized() + if (navigatorInitialized) { + navigationViewEnabled = true + } + } _navigationView.isNavigationUiEnabled = navigationViewEnabled + _minZoomLevelPreference = mapOptions.minZoomPreference + _maxZoomLevelPreference = mapOptions.maxZoomPreference + _navigationView.getMapAsync { map -> _map = map initListeners() + imageRegistry.mapViewInitializationComplete() // Re set navigation view enabled state as sometimes earlier value is not // respected. @@ -142,6 +162,9 @@ internal constructor( _navigationView.addOnRecenterButtonClickedListener { navigationViewEventApi.onRecenterButtonClicked(viewId.toLong()) {} } + _navigationView.addOnNavigationUiChangedListener { + navigationViewEventApi.onNavigationUIEnabledChanged(viewId.toLong(), it) {} + } getMap().setOnMapClickListener { navigationViewEventApi.onMapClickEvent( viewId.toLong(), @@ -200,20 +223,48 @@ internal constructor( getMap().setOnPolygonClickListener { polygon -> val polygonId = findPolygonId(polygon) - navigationViewEventApi.onPolygonClicked(PolygonClickedEventDto(viewId.toLong(), polygonId)) {} + navigationViewEventApi.onPolygonClicked(viewId.toLong(), polygonId) {} } getMap().setOnPolylineClickListener { polyline -> val polylineId = findPolylineId(polyline) - navigationViewEventApi.onPolylineClicked( - PolylineClickedEventDto(viewId.toLong(), polylineId) - ) {} + navigationViewEventApi.onPolylineClicked(viewId.toLong(), polylineId) {} } getMap().setOnCircleClickListener { circle -> val circleId = findCircleId(circle) - navigationViewEventApi.onCircleClicked(CircleClickedEventDto(viewId.toLong(), circleId)) {} + navigationViewEventApi.onCircleClicked(viewId.toLong(), circleId) {} + } + + getMap().setOnMyLocationClickListener { + navigationViewEventApi.onMyLocationClicked(viewId.toLong()) {} } + + getMap().setOnMyLocationButtonClickListener { + navigationViewEventApi.onMyLocationButtonClicked(viewId.toLong()) {} + _consumeMyLocationButtonClickEventsEnabled + } + + getMap() + .setOnFollowMyLocationCallback( + object : OnCameraFollowLocationCallback { + override fun onCameraStartedFollowingLocation() { + navigationViewEventApi.onCameraChanged( + viewId.toLong(), + CameraEventTypeDto.ONCAMERASTARTEDFOLLOWINGLOCATION, + Convert.convertCameraPositionToDto(getMap().cameraPosition) + ) {} + } + + override fun onCameraStoppedFollowingLocation() { + navigationViewEventApi.onCameraChanged( + viewId.toLong(), + CameraEventTypeDto.ONCAMERASTOPPEDFOLLOWINGLOCATION, + Convert.convertCameraPositionToDto(getMap().cameraPosition) + ) {} + } + } + ) } @Throws(FlutterError::class) @@ -225,14 +276,26 @@ internal constructor( } } - private fun invalidateViewAfterMapLoad() { + /** + * Workaround for map view not showing added or edited map objects immediately after add/edit. + * Schedules [NavigationView.invalidate] call after a certain amount of frames are drawn. In + * marker updates short delay is not enough, [doubleInvalidate] is set to true. + * + * @param doubleInvalidate if true, schedules another invalidate event after the first one. + */ + private fun invalidateViewAfterMapLoad(doubleInvalidate: Boolean = false) { if (_loadedCallbackPending) { return } _loadedCallbackPending = true getMap().setOnMapLoadedCallback { _loadedCallbackPending = false - _frameDelayHandler.scheduleActionWithFrameDelay { _navigationView.invalidate() } + _frameDelayHandler.scheduleActionWithFrameDelay { + _navigationView.invalidate() + if (doubleInvalidate) { + _frameDelayHandler.scheduleActionWithFrameDelay { _navigationView.invalidate() } + } + } } } @@ -307,19 +370,17 @@ internal constructor( @Throws(FlutterError::class) private fun sendMarkerEvent(marker: Marker, eventType: MarkerEventTypeDto) { val markerId = findMarkerId(marker) - navigationViewEventApi.onMarkerEvent(MarkerEventDto(viewId.toLong(), markerId, eventType)) {} + navigationViewEventApi.onMarkerEvent(viewId.toLong(), markerId, eventType) {} } @Throws(FlutterError::class) private fun sendMarkerDragEvent(marker: Marker, eventType: MarkerDragEventTypeDto) { val markerId = findMarkerId(marker) navigationViewEventApi.onMarkerDragEvent( - MarkerDragEventDto( - viewId.toLong(), - markerId, - eventType, - LatLngDto(marker.position.latitude, marker.position.longitude) - ) + viewId.toLong(), + markerId, + eventType, + LatLngDto(marker.position.latitude, marker.position.longitude) ) {} } @@ -328,52 +389,52 @@ internal constructor( } @SuppressLint("MissingPermission") - fun enableMyLocation(enabled: Boolean) { + fun setMyLocationEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() getMap().isMyLocationEnabled = enabled } - fun enableMyLocationButton(enabled: Boolean) { + fun setMyLocationButtonEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() getMap().uiSettings.isMyLocationButtonEnabled = enabled } - fun enableZoomGestures(enabled: Boolean) { + fun setZoomGesturesEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() getMap().uiSettings.isZoomGesturesEnabled = enabled } - fun enableZoomControls(enabled: Boolean) { + fun setZoomControlsEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() getMap().uiSettings.isZoomControlsEnabled = enabled } - fun enableCompass(enabled: Boolean) { + fun setCompassEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() getMap().uiSettings.isCompassEnabled = enabled } - fun enableRotateGestures(enabled: Boolean) { + fun setRotateGesturesEnabled(enabled: Boolean) { getMap().uiSettings.isRotateGesturesEnabled = enabled } - fun enableScrollGestures(enabled: Boolean) { + fun setScrollGesturesEnabled(enabled: Boolean) { getMap().uiSettings.isScrollGesturesEnabled = enabled } - fun enableScrollGesturesDuringRotateOrZoom(enabled: Boolean) { + fun setScrollGesturesDuringRotateOrZoomEnabled(enabled: Boolean) { getMap().uiSettings.isScrollGesturesEnabledDuringRotateOrZoom = enabled } - fun enableTiltGestures(enabled: Boolean) { + fun setTiltGesturesEnabled(enabled: Boolean) { getMap().uiSettings.isTiltGesturesEnabled = enabled } - fun enableMapToolbar(enabled: Boolean) { + fun setMapToolbarEnabled(enabled: Boolean) { getMap().uiSettings.isMapToolbarEnabled = enabled } - fun enableTraffic(enabled: Boolean) { + fun setTrafficEnabled(enabled: Boolean) { getMap().isTrafficEnabled = enabled } @@ -418,6 +479,7 @@ internal constructor( } fun getMyLocation(): Location? { + // Remove this functionality and either guide users to use separate flutter // library for geolocation or implement separate method under // [GoogleMapsNavigationSessionManager] to fetch the location // using the [FusedLocationProviderApi]. @@ -587,7 +649,7 @@ internal constructor( } } - fun enableNavigationTripProgressBar(enabled: Boolean) { + fun setNavigationTripProgressBarEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() _navigationView.setTripProgressBarEnabled(enabled) _isNavigationTripProgressBarEnabled = enabled @@ -597,7 +659,7 @@ internal constructor( return _isNavigationHeaderEnabled } - fun enableNavigationHeader(enabled: Boolean) { + fun setNavigationHeaderEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() _navigationView.setHeaderEnabled(enabled) _isNavigationHeaderEnabled = enabled @@ -607,7 +669,7 @@ internal constructor( return _isNavigationFooterEnabled } - fun enableNavigationFooter(enabled: Boolean) { + fun setNavigationFooterEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() _navigationView.setEtaCardEnabled(enabled) _isNavigationFooterEnabled = enabled @@ -617,7 +679,7 @@ internal constructor( return _isRecenterButtonEnabled } - fun enableRecenterButton(enabled: Boolean) { + fun setRecenterButtonEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() _navigationView.setRecenterButtonEnabled(enabled) _isRecenterButtonEnabled = enabled @@ -627,37 +689,37 @@ internal constructor( return _isSpeedLimitIconEnabled } - fun enableSpeedLimitIcon(enable: Boolean) { + fun setSpeedLimitIconEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() - _navigationView.setSpeedLimitIconEnabled(enable) - _isSpeedLimitIconEnabled = enable + _navigationView.setSpeedLimitIconEnabled(enabled) + _isSpeedLimitIconEnabled = enabled } fun isSpeedometerEnabled(): Boolean { return _isSpeedometerEnabled } - fun enableSpeedometer(enable: Boolean) { + fun setSpeedometerEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() - _navigationView.setSpeedometerEnabled(enable) - _isSpeedometerEnabled = enable + _navigationView.setSpeedometerEnabled(enabled) + _isSpeedometerEnabled = enabled } - fun isIncidentCardsEnabled(): Boolean { - return _isIncidentCardsEnabled + fun isTrafficIncidentCardsEnabled(): Boolean { + return _isTrafficIncidentCardsEnabled } - fun enableIncidentCards(enable: Boolean) { + fun setTrafficIncidentCardsEnabled(enabled: Boolean) { invalidateViewAfterMapLoad() - _navigationView.setTrafficIncidentCardsEnabled(enable) - _isIncidentCardsEnabled = enable + _navigationView.setTrafficIncidentCardsEnabled(enabled) + _isTrafficIncidentCardsEnabled = enabled } fun isNavigationUIEnabled(): Boolean { return _navigationView.isNavigationUiEnabled } - fun enableNavigationUI(enabled: Boolean) { + fun setNavigationUIEnabled(enabled: Boolean) { if (_navigationView.isNavigationUiEnabled != enabled) { invalidateViewAfterMapLoad() _navigationView.isNavigationUiEnabled = enabled @@ -669,6 +731,46 @@ internal constructor( _navigationView.showRouteOverview() } + fun getMinZoomPreference(): Float { + return _minZoomLevelPreference ?: getMap().minZoomLevel + } + + fun getMaxZoomPreference(): Float { + return _maxZoomLevelPreference ?: getMap().maxZoomLevel + } + + fun resetMinMaxZoomPreference() { + _minZoomLevelPreference = null + _maxZoomLevelPreference = null + getMap().resetMinMaxZoomPreference() + } + + @Throws(FlutterError::class) + fun setMinZoomPreference(minZoomPreference: Float) { + if (minZoomPreference > (_maxZoomLevelPreference ?: getMap().maxZoomLevel)) { + throw FlutterError( + "minZoomGreaterThanMaxZoom", + "Minimum zoom level cannot be greater than maximum zoom level" + ) + } + + _minZoomLevelPreference = minZoomPreference + getMap().setMinZoomPreference(minZoomPreference) + } + + @Throws(FlutterError::class) + fun setMaxZoomPreference(maxZoomPreference: Float) { + if (maxZoomPreference < (_minZoomLevelPreference ?: getMap().minZoomLevel)) { + throw FlutterError( + "maxZoomLessThanMinZoom", + "Maximum zoom level cannot be less than minimum zoom level" + ) + } + + _maxZoomLevelPreference = maxZoomPreference + getMap().setMaxZoomPreference(maxZoomPreference) + } + fun awaitMapReady(callback: (Result) -> Unit) { if (_map != null) { // Call the callback immediately as the map is already initialized @@ -694,14 +796,15 @@ internal constructor( } fun addMarkers(markers: List): List { - invalidateViewAfterMapLoad() val result = mutableListOf() markers.forEach { val builder = MarkerBuilder() - Convert.sinkMarkerOptions(it.options, builder) + Convert.sinkMarkerOptions(it.options, builder, imageRegistry) val options = builder.build() val marker = getMap().addMarker(options) if (marker != null) { + val registeredImage = + it.options.icon.registeredImageId?.let { id -> imageRegistry.findRegisteredImage(id) } val controller = MarkerController( marker, @@ -710,23 +813,26 @@ internal constructor( it.options.anchor.u.toFloat(), it.options.anchor.v.toFloat(), it.options.infoWindow.anchor.u.toFloat(), - it.options.infoWindow.anchor.v.toFloat() + it.options.infoWindow.anchor.v.toFloat(), + registeredImage ) _markers.add(controller) result.add(it) } } + // Double invalidate map view. Marker icon updates seem to take extra + // time and some times icon did not update properly after single invalidate. + invalidateViewAfterMapLoad(true) return result } @Throws(FlutterError::class) fun updateMarkers(markers: List): List { - invalidateViewAfterMapLoad() val result = mutableListOf() var error: Throwable? = null markers.forEach { findMarkerController(it.markerId)?.let { controller -> - Convert.sinkMarkerOptions(it.options, controller) + Convert.sinkMarkerOptions(it.options, controller, imageRegistry) result.add(it) } ?: run { @@ -734,6 +840,9 @@ internal constructor( } } error?.let { throw error as Throwable } + // Double invalidate map view. Marker icon updates seem to take extra + // time and some times icon did not update properly after single invalidate. + invalidateViewAfterMapLoad(true) return result } @@ -919,7 +1028,7 @@ internal constructor( val builder = CircleBuilder() Convert.sinkCircleOptions(it.options, builder, density) val options = builder.build() - val circle = getMap()?.addCircle(options) + val circle = getMap().addCircle(options) if (circle != null) { val controller = CircleController(circle, it.circleId) _circles.add(controller) @@ -943,6 +1052,7 @@ internal constructor( error = FlutterError("circleNotFound", "Failed to update circle with id ${it.circleId}") } } + error?.let { throw error as Throwable } return result } @@ -966,4 +1076,46 @@ internal constructor( _circles.forEach { controller -> controller.remove() } _circles.clear() } + + fun setConsumeMyLocationButtonClickEventsEnabled(enabled: Boolean) { + _consumeMyLocationButtonClickEventsEnabled = enabled + } + + fun isConsumeMyLocationButtonClickEventsEnabled(): Boolean { + return _consumeMyLocationButtonClickEventsEnabled + } + + fun registerOnCameraChangedListener() { + getMap().setOnCameraMoveStartedListener { reason -> + val event = + when (reason) { + OnCameraMoveStartedListener.REASON_API_ANIMATION, + OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION -> + CameraEventTypeDto.MOVESTARTEDBYAPI + OnCameraMoveStartedListener.REASON_GESTURE -> CameraEventTypeDto.MOVESTARTEDBYGESTURE + else -> { + // This should not happen, added that the compiler does not complain. + throw RuntimeException("Unknown camera move started reason: $reason") + } + } + val position = Convert.convertCameraPositionToDto(getMap().cameraPosition) + navigationViewEventApi.onCameraChanged(viewId.toLong(), event, position) {} + } + getMap().setOnCameraMoveListener { + val position = Convert.convertCameraPositionToDto(getMap().cameraPosition) + navigationViewEventApi.onCameraChanged( + viewId.toLong(), + CameraEventTypeDto.ONCAMERAMOVE, + position + ) {} + } + getMap().setOnCameraIdleListener { + val position = Convert.convertCameraPositionToDto(getMap().cameraPosition) + navigationViewEventApi.onCameraChanged( + viewId.toLong(), + CameraEventTypeDto.ONCAMERAIDLE, + position + ) {} + } + } } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewFactory.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewFactory.kt index afb4891..06fed76 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewFactory.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewFactory.kt @@ -24,19 +24,21 @@ import io.flutter.plugin.platform.PlatformViewFactory class GoogleMapsNavigationViewFactory( private val viewRegistry: GoogleMapsNavigationViewRegistry, private val navigationViewEventApi: NavigationViewEventApi, + private val imageRegistry: ImageRegistry ) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { override fun create(context: Context, viewId: Int, args: Any?): PlatformView { val params = NavigationViewCreationOptionsDto.fromList(args as List) val mapOptions = Convert.convertMapOptionsFromDto(params.mapOptions) - val navigationViewOptionsDto = + val navigationViewOptions = Convert.convertNavigationViewOptionsFromDto(params.navigationViewOptions) return GoogleMapsNavigationView( context, mapOptions, - navigationViewOptionsDto, + navigationViewOptions, viewId, viewRegistry, - navigationViewEventApi + navigationViewEventApi, + imageRegistry ) } } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewMessageHandler.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewMessageHandler.kt index 8fdf77f..c71c735 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewMessageHandler.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsNavigationViewMessageHandler.kt @@ -39,8 +39,8 @@ class GoogleMapsNavigationViewMessageHandler( return getView(viewId.toInt()).isMyLocationEnabled() } - override fun enableMyLocation(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableMyLocation(enabled) + override fun setMyLocationEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setMyLocationEnabled(enabled) } override fun getMapType(viewId: Long): MapTypeDto { @@ -59,50 +59,58 @@ class GoogleMapsNavigationViewMessageHandler( view.setMapStyle(styleJson) } - override fun enableMyLocationButton(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableMyLocationButton(enabled) + override fun setMyLocationButtonEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setMyLocationButtonEnabled(enabled) } - override fun enableZoomGestures(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableZoomGestures(enabled) + override fun setConsumeMyLocationButtonClickEventsEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setConsumeMyLocationButtonClickEventsEnabled(enabled) } - override fun enableZoomControls(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableZoomControls(enabled) + override fun setZoomGesturesEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setZoomGesturesEnabled(enabled) } - override fun enableCompass(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableCompass(enabled) + override fun setZoomControlsEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setZoomControlsEnabled(enabled) } - override fun enableRotateGestures(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableRotateGestures(enabled) + override fun setCompassEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setCompassEnabled(enabled) } - override fun enableScrollGestures(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableScrollGestures(enabled) + override fun setRotateGesturesEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setRotateGesturesEnabled(enabled) } - override fun enableScrollGesturesDuringRotateOrZoom(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableScrollGesturesDuringRotateOrZoom(enabled) + override fun setScrollGesturesEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setScrollGesturesEnabled(enabled) } - override fun enableTiltGestures(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableTiltGestures(enabled) + override fun setScrollGesturesDuringRotateOrZoomEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setScrollGesturesDuringRotateOrZoomEnabled(enabled) } - override fun enableMapToolbar(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableMapToolbar(enabled) + override fun setTiltGesturesEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setTiltGesturesEnabled(enabled) } - override fun enableTraffic(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableTraffic(enabled) + override fun setMapToolbarEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setMapToolbarEnabled(enabled) + } + + override fun setTrafficEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setTrafficEnabled(enabled) } override fun isMyLocationButtonEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isMyLocationButtonEnabled() } + override fun isConsumeMyLocationButtonClickEventsEnabled(viewId: Long): Boolean { + return getView(viewId.toInt()).isConsumeMyLocationButtonClickEventsEnabled() + } + override fun isZoomGesturesEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isZoomGesturesEnabled() } @@ -288,70 +296,90 @@ class GoogleMapsNavigationViewMessageHandler( view?.followMyLocation(Convert.convertCameraPerspectiveFromDto(perspective), zoomLevel) } - override fun enableNavigationTripProgressBar(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableNavigationTripProgressBar(enabled) + override fun setNavigationTripProgressBarEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setNavigationTripProgressBarEnabled(enabled) } override fun isNavigationHeaderEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isNavigationHeaderEnabled() } - override fun enableNavigationHeader(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableNavigationHeader(enabled) + override fun setNavigationHeaderEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setNavigationHeaderEnabled(enabled) } override fun isNavigationFooterEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isNavigationFooterEnabled() } - override fun enableNavigationFooter(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableNavigationFooter(enabled) + override fun setNavigationFooterEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setNavigationFooterEnabled(enabled) } override fun isRecenterButtonEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isRecenterButtonEnabled() } - override fun enableRecenterButton(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableRecenterButton(enabled) + override fun setRecenterButtonEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setRecenterButtonEnabled(enabled) } override fun isSpeedLimitIconEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isSpeedLimitIconEnabled() } - override fun enableSpeedLimitIcon(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableSpeedLimitIcon(enabled) + override fun setSpeedLimitIconEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setSpeedLimitIconEnabled(enabled) } override fun isSpeedometerEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isSpeedometerEnabled() } - override fun enableSpeedometer(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableSpeedometer(enabled) + override fun setSpeedometerEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setSpeedometerEnabled(enabled) } - override fun isIncidentCardsEnabled(viewId: Long): Boolean { - return getView(viewId.toInt()).isIncidentCardsEnabled() + override fun isTrafficIncidentCardsEnabled(viewId: Long): Boolean { + return getView(viewId.toInt()).isTrafficIncidentCardsEnabled() } - override fun enableIncidentCards(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableIncidentCards(enabled) + override fun setTrafficIncidentCardsEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setTrafficIncidentCardsEnabled(enabled) } override fun isNavigationUIEnabled(viewId: Long): Boolean { return getView(viewId.toInt()).isNavigationUIEnabled() } - override fun enableNavigationUI(viewId: Long, enabled: Boolean) { - getView(viewId.toInt()).enableNavigationUI(enabled) + override fun setNavigationUIEnabled(viewId: Long, enabled: Boolean) { + getView(viewId.toInt()).setNavigationUIEnabled(enabled) } override fun showRouteOverview(viewId: Long) { getView(viewId.toInt()).showRouteOverview() } + override fun getMinZoomPreference(viewId: Long): Double { + return getView(viewId.toInt()).getMinZoomPreference().toDouble() + } + + override fun getMaxZoomPreference(viewId: Long): Double { + return getView(viewId.toInt()).getMaxZoomPreference().toDouble() + } + + override fun resetMinMaxZoomPreference(viewId: Long) { + getView(viewId.toInt()).resetMinMaxZoomPreference() + } + + override fun setMinZoomPreference(viewId: Long, minZoomPreference: Double) { + getView(viewId.toInt()).setMinZoomPreference(minZoomPreference.toFloat()) + } + + override fun setMaxZoomPreference(viewId: Long, maxZoomPreference: Double) { + getView(viewId.toInt()).setMaxZoomPreference(maxZoomPreference.toFloat()) + } + override fun getMarkers(viewId: Long): List { return getView(viewId.toInt()).getMarkers() } @@ -401,15 +429,15 @@ class GoogleMapsNavigationViewMessageHandler( } override fun addPolylines(viewId: Long, polylines: List): List { - return getView(viewId.toInt()).addPolylines(polylines.filterNotNull()) + return getView(viewId.toInt()).addPolylines(polylines) } override fun updatePolylines(viewId: Long, polylines: List): List { - return getView(viewId.toInt()).updatePolylines(polylines.filterNotNull()) + return getView(viewId.toInt()).updatePolylines(polylines) } override fun removePolylines(viewId: Long, polylines: List) { - getView(viewId.toInt()).removePolylines(polylines.filterNotNull()) + getView(viewId.toInt()).removePolylines(polylines) } override fun clearPolylines(viewId: Long) { @@ -421,18 +449,22 @@ class GoogleMapsNavigationViewMessageHandler( } override fun addCircles(viewId: Long, circles: List): List { - return getView(viewId.toInt()).addCircles(circles.filterNotNull()) + return getView(viewId.toInt()).addCircles(circles) } override fun updateCircles(viewId: Long, circles: List): List { - return getView(viewId.toInt()).updateCircles(circles.filterNotNull()) + return getView(viewId.toInt()).updateCircles(circles) } override fun removeCircles(viewId: Long, circles: List) { - getView(viewId.toInt()).removeCircles(circles.filterNotNull()) + getView(viewId.toInt()).removeCircles(circles) } override fun clearCircles(viewId: Long) { getView(viewId.toInt()).clearCircles() } + + override fun registerOnCameraChangedListener(viewId: Long) { + getView(viewId.toInt()).registerOnCameraChangedListener() + } } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/ImageRegistry.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/ImageRegistry.kt new file mode 100644 index 0000000..09de870 --- /dev/null +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/ImageRegistry.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2023 Google LLC + * + * 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 + * + * https://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. + */ + +package com.google.maps.flutter.navigation + +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import com.google.android.gms.maps.model.BitmapDescriptorFactory + +class ImageRegistry { + val registeredImages = mutableListOf() + private val bitmapQueue = mutableListOf() + + private var isMapViewInitialized = false + + fun mapViewInitializationComplete() { + isMapViewInitialized = true + bitmapQueue.forEach { + addRegisteredImage(it.imageId, it.bitmap, it.imagePixelRatio, it.width, it.height) + } + bitmapQueue.clear() + } + + @Throws(FlutterError::class) + fun registerBitmapImage( + imageId: String, + bytes: ByteArray, + imagePixelRatio: Double, + width: Double?, + height: Double? + ): ImageDescriptorDto { + val density = Resources.getSystem().displayMetrics.density + val bitmap = createBitmap(bytes, imagePixelRatio, density, width, height) + + if (!isMapViewInitialized) { + // BitmapDescriptor cannot me created if map view is not initialized yet. + // Add to queue to make it later. + bitmapQueue.add(QueuedBitmap(imageId, bitmap, imagePixelRatio, width, height)) + } else { + addRegisteredImage(imageId, bitmap, imagePixelRatio, width, height) + } + return ImageDescriptorDto(imageId, imagePixelRatio, width, height) + } + + private fun addRegisteredImage( + imageId: String, + bitmap: Bitmap, + imagePixelRatio: Double, + width: Double?, + height: Double? + ) { + val bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap) + val registeredImage = RegisteredImage(imageId, bitmapDescriptor, imagePixelRatio, width, height) + registeredImages.add(registeredImage) + } + + @Throws(FlutterError::class) + private fun createBitmap( + bytes: ByteArray, + imagePixelRatio: Double, + density: Float, + width: Double?, + height: Double? + ): Bitmap { + val bitmap = + BitmapFactory.decodeByteArray(bytes, 0, bytes.size) + ?: throw FlutterError( + "imageDecodingFailed", + "Failed to decode bitmap, is the byte array valid image?" + ) + val scaledWidth: Double + val scaledHeight: Double + + if (width != null && height != null) { + scaledWidth = width * density + scaledHeight = height * density + } else if (width != null) { + // Calculate new height to match image aspect ratio. + val newHeight = (width / bitmap.width) * bitmap.height + scaledWidth = width * density + scaledHeight = newHeight * density + } else if (height != null) { + // Calculate new width to match image aspect ratio. + val newWidth = (height / bitmap.height) * bitmap.width + scaledWidth = newWidth * density + scaledHeight = height * density + } else { + // Default to imagePixelRatio if no width or height specified. + scaledWidth = (bitmap.width / imagePixelRatio) * density + scaledHeight = (bitmap.height / imagePixelRatio) * density + } + + return Bitmap.createScaledBitmap(bitmap, scaledWidth.toInt(), scaledHeight.toInt(), true) + } + + fun findRegisteredImage(imageId: String): RegisteredImage? { + return registeredImages.firstOrNull { it.imageId == imageId } + } + + fun unregisterImage(imageId: String) { + registeredImages.removeAll { it.imageId == imageId } + } + + fun clearRegisteredImages() { + registeredImages.clear() + } +} diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerBuilder.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerBuilder.kt index e94870b..579d360 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerBuilder.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerBuilder.kt @@ -76,4 +76,12 @@ class MarkerBuilder : MarkerOptionsSink { override fun setZIndex(zIndex: Float) { _markerOptions.zIndex(zIndex) } + + override fun setIcon(registeredImage: RegisteredImage?) { + // registeredImage will be stored in the MarkerController object + // after the marker has been created. + if (registeredImage != null) { + _markerOptions.icon(registeredImage.bitmapDescriptor) + } + } } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerController.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerController.kt index e487144..52632af 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerController.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerController.kt @@ -27,7 +27,8 @@ class MarkerController( anchorU: Float, anchorV: Float, infoWindowAnchorU: Float, - infoWindowAnchorV: Float + infoWindowAnchorV: Float, + registeredImage: RegisteredImage? ) : MarkerOptionsSink { var consumeTapEvents: Boolean = consumeTapEvents private set @@ -45,6 +46,9 @@ class MarkerController( var infoWindowAnchorV: Float = infoWindowAnchorV private set + var registeredImage: RegisteredImage? = registeredImage + private set + override fun setAlpha(alpha: Float) { marker.alpha = alpha } @@ -100,4 +104,13 @@ class MarkerController( fun remove() { marker.remove() } + + override fun setIcon(registeredImage: RegisteredImage?) { + this.registeredImage = registeredImage + if (registeredImage != null) { + marker.setIcon(registeredImage.bitmapDescriptor) + } else { + marker.setIcon(null) + } + } } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerOptionsSink.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerOptionsSink.kt index 7bae76d..86644e5 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerOptionsSink.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/MarkerOptionsSink.kt @@ -43,4 +43,6 @@ interface MarkerOptionsSink { fun setVisible(visible: Boolean) fun setZIndex(zIndex: Float) + + fun setIcon(registeredImage: RegisteredImage?) } diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/NavigationViewConfiguration.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/NavigationViewConfiguration.kt index 38034a8..bed74da 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/NavigationViewConfiguration.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/NavigationViewConfiguration.kt @@ -16,7 +16,16 @@ package com.google.maps.flutter.navigation +/** Determines the initial visibility of the navigation UI on map initialization. */ +enum class NavigationUIEnabledPreference { + /** Navigation UI gets enabled if the navigation session has been started. */ + AUTOMATIC, + + /** Navigation UI is disabled. */ + DISABLED, +} + /** Class for navigation view configuration options. */ data class NavigationViewOptions( - val navigationUiEnabled: Boolean?, + val navigationUiEnabledPreference: NavigationUIEnabledPreference?, ) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/QueuedBitmap.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/QueuedBitmap.kt new file mode 100644 index 0000000..c72b3e2 --- /dev/null +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/QueuedBitmap.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Google LLC + * + * 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 + * + * https://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. + */ + +package com.google.maps.flutter.navigation + +import android.graphics.Bitmap + +data class QueuedBitmap( + val imageId: String, + val bitmap: Bitmap, + val imagePixelRatio: Double, + val width: Double?, + val height: Double? +) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/RegisteredImage.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/RegisteredImage.kt new file mode 100644 index 0000000..0985cfe --- /dev/null +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/RegisteredImage.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Google LLC + * + * 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 + * + * https://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. + */ + +package com.google.maps.flutter.navigation + +import com.google.android.gms.maps.model.BitmapDescriptor + +data class RegisteredImage( + val imageId: String, + val bitmapDescriptor: BitmapDescriptor, + val imagePixelRatio: Double, + val width: Double?, + val height: Double? +) diff --git a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt index 79a00cb..08c0650 100644 --- a/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt +++ b/android/src/main/kotlin/com/google/maps/flutter/navigation/messages.g.kt @@ -62,6 +62,20 @@ class FlutterError( val details: Any? = null ) : Throwable() +/** Determines the initial visibility of the navigation UI on map initialization. */ +enum class NavigationUIEnabledPreferenceDto(val raw: Int) { + /** Navigation UI gets enabled if the navigation session has already been successfully started. */ + AUTOMATIC(0), + /** Navigation UI is disabled. */ + DISABLED(1); + + companion object { + fun ofRaw(raw: Int): NavigationUIEnabledPreferenceDto? { + return values().firstOrNull { it.raw == raw } + } + } +} + enum class MapTypeDto(val raw: Int) { NONE(0), NORMAL(1), @@ -137,13 +151,16 @@ enum class PatternTypeDto(val raw: Int) { } } -enum class NavigationSessionEventTypeDto(val raw: Int) { - ARRIVALEVENT(0), - ROUTECHANGED(1), - ERRORRECEIVED(2); +enum class CameraEventTypeDto(val raw: Int) { + MOVESTARTEDBYAPI(0), + MOVESTARTEDBYGESTURE(1), + ONCAMERAMOVE(2), + ONCAMERAIDLE(3), + ONCAMERASTARTEDFOLLOWINGLOCATION(4), + ONCAMERASTOPPEDFOLLOWINGLOCATION(5); companion object { - fun ofRaw(raw: Int): NavigationSessionEventTypeDto? { + fun ofRaw(raw: Int): CameraEventTypeDto? { return values().firstOrNull { it.raw == raw } } } @@ -356,19 +373,19 @@ data class MapOptionsDto( */ data class NavigationViewOptionsDto( /** Determines the initial visibility of the navigation UI on map initialization. */ - val navigationUIEnabled: Boolean + val navigationUIEnabledPreference: NavigationUIEnabledPreferenceDto ) { companion object { @Suppress("UNCHECKED_CAST") fun fromList(list: List): NavigationViewOptionsDto { - val navigationUIEnabled = list[0] as Boolean - return NavigationViewOptionsDto(navigationUIEnabled) + val navigationUIEnabledPreference = NavigationUIEnabledPreferenceDto.ofRaw(list[0] as Int)!! + return NavigationViewOptionsDto(navigationUIEnabledPreference) } } fun toList(): List { return listOf( - navigationUIEnabled, + navigationUIEnabledPreference.raw, ) } } @@ -464,7 +481,8 @@ data class MarkerOptionsDto( val rotation: Double, val infoWindow: InfoWindowDto, val visible: Boolean, - val zIndex: Double + val zIndex: Double, + val icon: ImageDescriptorDto ) { companion object { @Suppress("UNCHECKED_CAST") @@ -479,6 +497,7 @@ data class MarkerOptionsDto( val infoWindow = InfoWindowDto.fromList(list[7] as List) val visible = list[8] as Boolean val zIndex = list[9] as Double + val icon = ImageDescriptorDto.fromList(list[10] as List) return MarkerOptionsDto( alpha, anchor, @@ -489,7 +508,8 @@ data class MarkerOptionsDto( rotation, infoWindow, visible, - zIndex + zIndex, + icon ) } } @@ -506,6 +526,35 @@ data class MarkerOptionsDto( infoWindow.toList(), visible, zIndex, + icon.toList(), + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class ImageDescriptorDto( + val registeredImageId: String? = null, + val imagePixelRatio: Double? = null, + val width: Double? = null, + val height: Double? = null +) { + companion object { + @Suppress("UNCHECKED_CAST") + fun fromList(list: List): ImageDescriptorDto { + val registeredImageId = list[0] as String? + val imagePixelRatio = list[1] as Double? + val width = list[2] as Double? + val height = list[3] as Double? + return ImageDescriptorDto(registeredImageId, imagePixelRatio, width, height) + } + } + + fun toList(): List { + return listOf( + registeredImageId, + imagePixelRatio, + width, + height, ) } } @@ -555,59 +604,6 @@ data class MarkerAnchorDto(val u: Double, val v: Double) { } } -/** Generated class from Pigeon that represents data sent in messages. */ -data class MarkerEventDto( - val viewId: Long, - val markerId: String, - val eventType: MarkerEventTypeDto -) { - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MarkerEventDto { - val viewId = list[0].let { if (it is Int) it.toLong() else it as Long } - val markerId = list[1] as String - val eventType = MarkerEventTypeDto.ofRaw(list[2] as Int)!! - return MarkerEventDto(viewId, markerId, eventType) - } - } - - fun toList(): List { - return listOf( - viewId, - markerId, - eventType.raw, - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class MarkerDragEventDto( - val viewId: Long, - val markerId: String, - val eventType: MarkerDragEventTypeDto, - val position: LatLngDto -) { - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MarkerDragEventDto { - val viewId = list[0].let { if (it is Int) it.toLong() else it as Long } - val markerId = list[1] as String - val eventType = MarkerDragEventTypeDto.ofRaw(list[2] as Int)!! - val position = LatLngDto.fromList(list[3] as List) - return MarkerDragEventDto(viewId, markerId, eventType, position) - } - } - - fun toList(): List { - return listOf( - viewId, - markerId, - eventType.raw, - position.toList(), - ) - } -} - /** Generated class from Pigeon that represents data sent in messages. */ data class PolygonDto(val polygonId: String, val options: PolygonOptionsDto) { @@ -699,26 +695,6 @@ data class PolygonHoleDto(val points: List) { } } -/** Generated class from Pigeon that represents data sent in messages. */ -data class PolygonClickedEventDto(val viewId: Long, val polygonId: String) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PolygonClickedEventDto { - val viewId = list[0].let { if (it is Int) it.toLong() else it as Long } - val polygonId = list[1] as String - return PolygonClickedEventDto(viewId, polygonId) - } - } - - fun toList(): List { - return listOf( - viewId, - polygonId, - ) - } -} - /** Generated class from Pigeon that represents data sent in messages. */ data class StyleSpanStrokeStyleDto( val solidColor: Long? = null, @@ -862,26 +838,6 @@ data class PolylineOptionsDto( } } -/** Generated class from Pigeon that represents data sent in messages. */ -data class PolylineClickedEventDto(val viewId: Long, val polylineId: String) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PolylineClickedEventDto { - val viewId = list[0].let { if (it is Int) it.toLong() else it as Long } - val polylineId = list[1] as String - return PolylineClickedEventDto(viewId, polylineId) - } - } - - fun toList(): List { - return listOf( - viewId, - polylineId, - ) - } -} - /** Generated class from Pigeon that represents data sent in messages. */ data class CircleDto( /** Identifies circle. */ @@ -960,41 +916,21 @@ data class CircleOptionsDto( } /** Generated class from Pigeon that represents data sent in messages. */ -data class CircleClickedEventDto(val viewId: Long, val circleId: String) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): CircleClickedEventDto { - val viewId = list[0].let { if (it is Int) it.toLong() else it as Long } - val circleId = list[1] as String - return CircleClickedEventDto(viewId, circleId) - } - } - - fun toList(): List { - return listOf( - viewId, - circleId, - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class NavigationSessionEventDto(val type: NavigationSessionEventTypeDto, val message: String) { +data class RouteTokenOptionsDto(val routeToken: String, val travelMode: TravelModeDto? = null) { companion object { @Suppress("UNCHECKED_CAST") - fun fromList(list: List): NavigationSessionEventDto { - val type = NavigationSessionEventTypeDto.ofRaw(list[0] as Int)!! - val message = list[1] as String - return NavigationSessionEventDto(type, message) + fun fromList(list: List): RouteTokenOptionsDto { + val routeToken = list[0] as String + val travelMode: TravelModeDto? = (list[1] as Int?)?.let { TravelModeDto.ofRaw(it) } + return RouteTokenOptionsDto(routeToken, travelMode) } } fun toList(): List { return listOf( - type.raw, - message, + routeToken, + travelMode?.raw, ) } } @@ -1003,7 +939,8 @@ data class NavigationSessionEventDto(val type: NavigationSessionEventTypeDto, va data class DestinationsDto( val waypoints: List, val displayOptions: NavigationDisplayOptionsDto, - val routingOptions: RoutingOptionsDto? = null + val routingOptions: RoutingOptionsDto? = null, + val routeTokenOptions: RouteTokenOptionsDto? = null ) { companion object { @Suppress("UNCHECKED_CAST") @@ -1012,7 +949,9 @@ data class DestinationsDto( val displayOptions = NavigationDisplayOptionsDto.fromList(list[1] as List) val routingOptions: RoutingOptionsDto? = (list[2] as List?)?.let { RoutingOptionsDto.fromList(it) } - return DestinationsDto(waypoints, displayOptions, routingOptions) + val routeTokenOptions: RouteTokenOptionsDto? = + (list[3] as List?)?.let { RouteTokenOptionsDto.fromList(it) } + return DestinationsDto(waypoints, displayOptions, routingOptions, routeTokenOptions) } } @@ -1021,6 +960,7 @@ data class DestinationsDto( waypoints, displayOptions.toList(), routingOptions?.toList(), + routeTokenOptions?.toList(), ) } } @@ -1268,136 +1208,6 @@ data class SpeedingUpdatedEventDto( } } -/** Generated class from Pigeon that represents data sent in messages. */ -data class RoadSnappedLocationUpdatedEventDto(val location: LatLngDto) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): RoadSnappedLocationUpdatedEventDto { - val location = LatLngDto.fromList(list[0] as List) - return RoadSnappedLocationUpdatedEventDto(location) - } - } - - fun toList(): List { - return listOf( - location.toList(), - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class RoadSnappedRawLocationUpdatedEventDto(val location: LatLngDto? = null) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): RoadSnappedRawLocationUpdatedEventDto { - val location: LatLngDto? = (list[0] as List?)?.let { LatLngDto.fromList(it) } - return RoadSnappedRawLocationUpdatedEventDto(location) - } - } - - fun toList(): List { - return listOf( - location?.toList(), - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class OnArrivalEventDto(val waypoint: NavigationWaypointDto) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): OnArrivalEventDto { - val waypoint = NavigationWaypointDto.fromList(list[0] as List) - return OnArrivalEventDto(waypoint) - } - } - - fun toList(): List { - return listOf( - waypoint.toList(), - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class RemainingTimeOrDistanceChangedEventDto( - val remainingTime: Double, - val remainingDistance: Double -) { - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): RemainingTimeOrDistanceChangedEventDto { - val remainingTime = list[0] as Double - val remainingDistance = list[1] as Double - return RemainingTimeOrDistanceChangedEventDto(remainingTime, remainingDistance) - } - } - - fun toList(): List { - return listOf( - remainingTime, - remainingDistance, - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class RouteChangedEventDto(val message: String) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): RouteChangedEventDto { - val message = list[0] as String - return RouteChangedEventDto(message) - } - } - - fun toList(): List { - return listOf( - message, - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class ReroutingEventDto(val message: String) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): ReroutingEventDto { - val message = list[0] as String - return ReroutingEventDto(message) - } - } - - fun toList(): List { - return listOf( - message, - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class TrafficUpdatedEventDto(val message: String) { - - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): TrafficUpdatedEventDto { - val message = list[0] as String - return TrafficUpdatedEventDto(message) - } - } - - fun toList(): List { - return listOf( - message, - ) - } -} - /** Generated class from Pigeon that represents data sent in messages. */ data class SpeedAlertOptionsDto( val severityUpgradeDurationSeconds: Double, @@ -1625,48 +1435,51 @@ private object NavigationViewApiCodec : StandardMessageCodec() { return (readValue(buffer) as? List)?.let { CircleOptionsDto.fromList(it) } } 131.toByte() -> { - return (readValue(buffer) as? List)?.let { InfoWindowDto.fromList(it) } + return (readValue(buffer) as? List)?.let { ImageDescriptorDto.fromList(it) } } 132.toByte() -> { - return (readValue(buffer) as? List)?.let { LatLngBoundsDto.fromList(it) } + return (readValue(buffer) as? List)?.let { InfoWindowDto.fromList(it) } } 133.toByte() -> { - return (readValue(buffer) as? List)?.let { LatLngDto.fromList(it) } + return (readValue(buffer) as? List)?.let { LatLngBoundsDto.fromList(it) } } 134.toByte() -> { return (readValue(buffer) as? List)?.let { LatLngDto.fromList(it) } } 135.toByte() -> { - return (readValue(buffer) as? List)?.let { MarkerAnchorDto.fromList(it) } + return (readValue(buffer) as? List)?.let { LatLngDto.fromList(it) } } 136.toByte() -> { - return (readValue(buffer) as? List)?.let { MarkerDto.fromList(it) } + return (readValue(buffer) as? List)?.let { MarkerAnchorDto.fromList(it) } } 137.toByte() -> { - return (readValue(buffer) as? List)?.let { MarkerOptionsDto.fromList(it) } + return (readValue(buffer) as? List)?.let { MarkerDto.fromList(it) } } 138.toByte() -> { - return (readValue(buffer) as? List)?.let { PatternItemDto.fromList(it) } + return (readValue(buffer) as? List)?.let { MarkerOptionsDto.fromList(it) } } 139.toByte() -> { - return (readValue(buffer) as? List)?.let { PolygonDto.fromList(it) } + return (readValue(buffer) as? List)?.let { PatternItemDto.fromList(it) } } 140.toByte() -> { - return (readValue(buffer) as? List)?.let { PolygonHoleDto.fromList(it) } + return (readValue(buffer) as? List)?.let { PolygonDto.fromList(it) } } 141.toByte() -> { - return (readValue(buffer) as? List)?.let { PolygonOptionsDto.fromList(it) } + return (readValue(buffer) as? List)?.let { PolygonHoleDto.fromList(it) } } 142.toByte() -> { - return (readValue(buffer) as? List)?.let { PolylineDto.fromList(it) } + return (readValue(buffer) as? List)?.let { PolygonOptionsDto.fromList(it) } } 143.toByte() -> { - return (readValue(buffer) as? List)?.let { PolylineOptionsDto.fromList(it) } + return (readValue(buffer) as? List)?.let { PolylineDto.fromList(it) } } 144.toByte() -> { - return (readValue(buffer) as? List)?.let { StyleSpanDto.fromList(it) } + return (readValue(buffer) as? List)?.let { PolylineOptionsDto.fromList(it) } } 145.toByte() -> { + return (readValue(buffer) as? List)?.let { StyleSpanDto.fromList(it) } + } + 146.toByte() -> { return (readValue(buffer) as? List)?.let { StyleSpanStrokeStyleDto.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -1687,15 +1500,15 @@ private object NavigationViewApiCodec : StandardMessageCodec() { stream.write(130) writeValue(stream, value.toList()) } - is InfoWindowDto -> { + is ImageDescriptorDto -> { stream.write(131) writeValue(stream, value.toList()) } - is LatLngBoundsDto -> { + is InfoWindowDto -> { stream.write(132) writeValue(stream, value.toList()) } - is LatLngDto -> { + is LatLngBoundsDto -> { stream.write(133) writeValue(stream, value.toList()) } @@ -1703,50 +1516,54 @@ private object NavigationViewApiCodec : StandardMessageCodec() { stream.write(134) writeValue(stream, value.toList()) } - is MarkerAnchorDto -> { + is LatLngDto -> { stream.write(135) writeValue(stream, value.toList()) } - is MarkerDto -> { + is MarkerAnchorDto -> { stream.write(136) writeValue(stream, value.toList()) } - is MarkerOptionsDto -> { + is MarkerDto -> { stream.write(137) writeValue(stream, value.toList()) } - is PatternItemDto -> { + is MarkerOptionsDto -> { stream.write(138) writeValue(stream, value.toList()) } - is PolygonDto -> { + is PatternItemDto -> { stream.write(139) writeValue(stream, value.toList()) } - is PolygonHoleDto -> { + is PolygonDto -> { stream.write(140) writeValue(stream, value.toList()) } - is PolygonOptionsDto -> { + is PolygonHoleDto -> { stream.write(141) writeValue(stream, value.toList()) } - is PolylineDto -> { + is PolygonOptionsDto -> { stream.write(142) writeValue(stream, value.toList()) } - is PolylineOptionsDto -> { + is PolylineDto -> { stream.write(143) writeValue(stream, value.toList()) } - is StyleSpanDto -> { + is PolylineOptionsDto -> { stream.write(144) writeValue(stream, value.toList()) } - is StyleSpanStrokeStyleDto -> { + is StyleSpanDto -> { stream.write(145) writeValue(stream, value.toList()) } + is StyleSpanStrokeStyleDto -> { + stream.write(146) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -1758,7 +1575,7 @@ interface NavigationViewApi { fun isMyLocationEnabled(viewId: Long): Boolean - fun enableMyLocation(viewId: Long, enabled: Boolean) + fun setMyLocationEnabled(viewId: Long, enabled: Boolean) fun getMyLocation(viewId: Long): LatLngDto? @@ -1770,35 +1587,35 @@ interface NavigationViewApi { fun isNavigationTripProgressBarEnabled(viewId: Long): Boolean - fun enableNavigationTripProgressBar(viewId: Long, enabled: Boolean) + fun setNavigationTripProgressBarEnabled(viewId: Long, enabled: Boolean) fun isNavigationHeaderEnabled(viewId: Long): Boolean - fun enableNavigationHeader(viewId: Long, enabled: Boolean) + fun setNavigationHeaderEnabled(viewId: Long, enabled: Boolean) fun isNavigationFooterEnabled(viewId: Long): Boolean - fun enableNavigationFooter(viewId: Long, enabled: Boolean) + fun setNavigationFooterEnabled(viewId: Long, enabled: Boolean) fun isRecenterButtonEnabled(viewId: Long): Boolean - fun enableRecenterButton(viewId: Long, enabled: Boolean) + fun setRecenterButtonEnabled(viewId: Long, enabled: Boolean) fun isSpeedLimitIconEnabled(viewId: Long): Boolean - fun enableSpeedLimitIcon(viewId: Long, enabled: Boolean) + fun setSpeedLimitIconEnabled(viewId: Long, enabled: Boolean) fun isSpeedometerEnabled(viewId: Long): Boolean - fun enableSpeedometer(viewId: Long, enabled: Boolean) + fun setSpeedometerEnabled(viewId: Long, enabled: Boolean) - fun isIncidentCardsEnabled(viewId: Long): Boolean + fun isTrafficIncidentCardsEnabled(viewId: Long): Boolean - fun enableIncidentCards(viewId: Long, enabled: Boolean) + fun setTrafficIncidentCardsEnabled(viewId: Long, enabled: Boolean) fun isNavigationUIEnabled(viewId: Long): Boolean - fun enableNavigationUI(viewId: Long, enabled: Boolean) + fun setNavigationUIEnabled(viewId: Long, enabled: Boolean) fun getCameraPosition(viewId: Long): CameraPositionDto @@ -1876,28 +1693,42 @@ interface NavigationViewApi { fun showRouteOverview(viewId: Long) - fun enableMyLocationButton(viewId: Long, enabled: Boolean) + fun getMinZoomPreference(viewId: Long): Double + + fun getMaxZoomPreference(viewId: Long): Double - fun enableZoomGestures(viewId: Long, enabled: Boolean) + fun resetMinMaxZoomPreference(viewId: Long) - fun enableZoomControls(viewId: Long, enabled: Boolean) + fun setMinZoomPreference(viewId: Long, minZoomPreference: Double) - fun enableCompass(viewId: Long, enabled: Boolean) + fun setMaxZoomPreference(viewId: Long, maxZoomPreference: Double) - fun enableRotateGestures(viewId: Long, enabled: Boolean) + fun setMyLocationButtonEnabled(viewId: Long, enabled: Boolean) - fun enableScrollGestures(viewId: Long, enabled: Boolean) + fun setConsumeMyLocationButtonClickEventsEnabled(viewId: Long, enabled: Boolean) - fun enableScrollGesturesDuringRotateOrZoom(viewId: Long, enabled: Boolean) + fun setZoomGesturesEnabled(viewId: Long, enabled: Boolean) - fun enableTiltGestures(viewId: Long, enabled: Boolean) + fun setZoomControlsEnabled(viewId: Long, enabled: Boolean) - fun enableMapToolbar(viewId: Long, enabled: Boolean) + fun setCompassEnabled(viewId: Long, enabled: Boolean) - fun enableTraffic(viewId: Long, enabled: Boolean) + fun setRotateGesturesEnabled(viewId: Long, enabled: Boolean) + + fun setScrollGesturesEnabled(viewId: Long, enabled: Boolean) + + fun setScrollGesturesDuringRotateOrZoomEnabled(viewId: Long, enabled: Boolean) + + fun setTiltGesturesEnabled(viewId: Long, enabled: Boolean) + + fun setMapToolbarEnabled(viewId: Long, enabled: Boolean) + + fun setTrafficEnabled(viewId: Long, enabled: Boolean) fun isMyLocationButtonEnabled(viewId: Long): Boolean + fun isConsumeMyLocationButtonClickEventsEnabled(viewId: Long): Boolean + fun isZoomGesturesEnabled(viewId: Long): Boolean fun isZoomControlsEnabled(viewId: Long): Boolean @@ -1958,6 +1789,8 @@ interface NavigationViewApi { fun clearCircles(viewId: Long) + fun registerOnCameraChangedListener(viewId: Long) + companion object { /** The codec used by NavigationViewApi. */ val codec: MessageCodec by lazy { NavigationViewApiCodec } @@ -2017,7 +1850,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocation", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationEnabled", codec ) if (api != null) { @@ -2027,7 +1860,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableMyLocation(viewIdArg, enabledArg) + api.setMyLocationEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2161,7 +1994,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationTripProgressBar", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationTripProgressBarEnabled", codec ) if (api != null) { @@ -2171,7 +2004,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableNavigationTripProgressBar(viewIdArg, enabledArg) + api.setNavigationTripProgressBarEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2209,7 +2042,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationHeader", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationHeaderEnabled", codec ) if (api != null) { @@ -2219,7 +2052,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableNavigationHeader(viewIdArg, enabledArg) + api.setNavigationHeaderEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2257,7 +2090,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationFooter", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationFooterEnabled", codec ) if (api != null) { @@ -2267,7 +2100,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableNavigationFooter(viewIdArg, enabledArg) + api.setNavigationFooterEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2305,7 +2138,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRecenterButton", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRecenterButtonEnabled", codec ) if (api != null) { @@ -2315,7 +2148,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableRecenterButton(viewIdArg, enabledArg) + api.setRecenterButtonEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2353,7 +2186,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedLimitIcon", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedLimitIconEnabled", codec ) if (api != null) { @@ -2363,7 +2196,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableSpeedLimitIcon(viewIdArg, enabledArg) + api.setSpeedLimitIconEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2401,7 +2234,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedometer", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedometerEnabled", codec ) if (api != null) { @@ -2411,7 +2244,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableSpeedometer(viewIdArg, enabledArg) + api.setSpeedometerEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2426,7 +2259,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isIncidentCardsEnabled", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isTrafficIncidentCardsEnabled", codec ) if (api != null) { @@ -2435,7 +2268,7 @@ interface NavigationViewApi { val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } var wrapped: List try { - wrapped = listOf(api.isIncidentCardsEnabled(viewIdArg)) + wrapped = listOf(api.isTrafficIncidentCardsEnabled(viewIdArg)) } catch (exception: Throwable) { wrapped = wrapError(exception) } @@ -2449,7 +2282,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableIncidentCards", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficIncidentCardsEnabled", codec ) if (api != null) { @@ -2459,7 +2292,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableIncidentCards(viewIdArg, enabledArg) + api.setTrafficIncidentCardsEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2497,7 +2330,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationUI", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationUIEnabled", codec ) if (api != null) { @@ -2507,7 +2340,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableNavigationUI(viewIdArg, enabledArg) + api.setNavigationUIEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -2997,17 +2830,112 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocationButton", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMinZoomPreference", codec ) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableMyLocationButton(viewIdArg, enabledArg) + wrapped = listOf(api.getMinZoomPreference(viewIdArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMaxZoomPreference", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + var wrapped: List + try { + wrapped = listOf(api.getMaxZoomPreference(viewIdArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.resetMinMaxZoomPreference", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + var wrapped: List + try { + api.resetMinMaxZoomPreference(viewIdArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMinZoomPreference", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val minZoomPreferenceArg = args[1] as Double + var wrapped: List + try { + api.setMinZoomPreference(viewIdArg, minZoomPreferenceArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMaxZoomPreference", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val maxZoomPreferenceArg = args[1] as Double + var wrapped: List + try { + api.setMaxZoomPreference(viewIdArg, maxZoomPreferenceArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3022,7 +2950,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomGestures", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationButtonEnabled", codec ) if (api != null) { @@ -3032,7 +2960,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableZoomGestures(viewIdArg, enabledArg) + api.setMyLocationButtonEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3047,7 +2975,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomControls", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setConsumeMyLocationButtonClickEventsEnabled", codec ) if (api != null) { @@ -3057,7 +2985,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableZoomControls(viewIdArg, enabledArg) + api.setConsumeMyLocationButtonClickEventsEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3072,7 +3000,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableCompass", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomGesturesEnabled", codec ) if (api != null) { @@ -3082,7 +3010,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableCompass(viewIdArg, enabledArg) + api.setZoomGesturesEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3097,7 +3025,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRotateGestures", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomControlsEnabled", codec ) if (api != null) { @@ -3107,7 +3035,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableRotateGestures(viewIdArg, enabledArg) + api.setZoomControlsEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3122,7 +3050,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGestures", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setCompassEnabled", codec ) if (api != null) { @@ -3132,7 +3060,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableScrollGestures(viewIdArg, enabledArg) + api.setCompassEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3147,7 +3075,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGesturesDuringRotateOrZoom", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRotateGesturesEnabled", codec ) if (api != null) { @@ -3157,7 +3085,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableScrollGesturesDuringRotateOrZoom(viewIdArg, enabledArg) + api.setRotateGesturesEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3172,7 +3100,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTiltGestures", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesEnabled", codec ) if (api != null) { @@ -3182,7 +3110,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableTiltGestures(viewIdArg, enabledArg) + api.setScrollGesturesEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3197,7 +3125,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMapToolbar", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesDuringRotateOrZoomEnabled", codec ) if (api != null) { @@ -3207,7 +3135,7 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableMapToolbar(viewIdArg, enabledArg) + api.setScrollGesturesDuringRotateOrZoomEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3222,7 +3150,7 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTraffic", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTiltGesturesEnabled", codec ) if (api != null) { @@ -3232,7 +3160,57 @@ interface NavigationViewApi { val enabledArg = args[1] as Boolean var wrapped: List try { - api.enableTraffic(viewIdArg, enabledArg) + api.setTiltGesturesEnabled(viewIdArg, enabledArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMapToolbarEnabled", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val enabledArg = args[1] as Boolean + var wrapped: List + try { + api.setMapToolbarEnabled(viewIdArg, enabledArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficEnabled", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val enabledArg = args[1] as Boolean + var wrapped: List + try { + api.setTrafficEnabled(viewIdArg, enabledArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3266,6 +3244,29 @@ interface NavigationViewApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isConsumeMyLocationButtonClickEventsEnabled", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + var wrapped: List + try { + wrapped = listOf(api.isConsumeMyLocationButtonClickEventsEnabled(viewIdArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( @@ -3884,17 +3885,207 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.addCircles", + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.addCircles", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val circlesArg = args[1] as List + var wrapped: List + try { + wrapped = listOf(api.addCircles(viewIdArg, circlesArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.updateCircles", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val circlesArg = args[1] as List + var wrapped: List + try { + wrapped = listOf(api.updateCircles(viewIdArg, circlesArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.removeCircles", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val circlesArg = args[1] as List + var wrapped: List + try { + api.removeCircles(viewIdArg, circlesArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.clearCircles", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + var wrapped: List + try { + api.clearCircles(viewIdArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.registerOnCameraChangedListener", + codec + ) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + var wrapped: List + try { + api.registerOnCameraChangedListener(viewIdArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} + +@Suppress("UNCHECKED_CAST") +private object ImageRegistryApiCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 128.toByte() -> { + return (readValue(buffer) as? List)?.let { ImageDescriptorDto.fromList(it) } + } + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { ImageDescriptorDto.fromList(it) } + } + else -> super.readValueOfType(type, buffer) + } + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is ImageDescriptorDto -> { + stream.write(128) + writeValue(stream, value.toList()) + } + is ImageDescriptorDto -> { + stream.write(129) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface ImageRegistryApi { + fun registerBitmapImage( + imageId: String, + bytes: ByteArray, + imagePixelRatio: Double, + width: Double?, + height: Double? + ): ImageDescriptorDto + + fun unregisterImage(imageDescriptor: ImageDescriptorDto) + + fun getRegisteredImages(): List + + fun clearRegisteredImages() + + companion object { + /** The codec used by ImageRegistryApi. */ + val codec: MessageCodec by lazy { ImageRegistryApiCodec } + /** + * Sets up an instance of `ImageRegistryApi` to handle messages through the `binaryMessenger`. + */ + @Suppress("UNCHECKED_CAST") + fun setUp(binaryMessenger: BinaryMessenger, api: ImageRegistryApi?) { + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage", codec ) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val circlesArg = args[1] as List + val imageIdArg = args[0] as String + val bytesArg = args[1] as ByteArray + val imagePixelRatioArg = args[2] as Double + val widthArg = args[3] as Double? + val heightArg = args[4] as Double? var wrapped: List try { - wrapped = listOf(api.addCircles(viewIdArg, circlesArg)) + wrapped = + listOf( + api.registerBitmapImage( + imageIdArg, + bytesArg, + imagePixelRatioArg, + widthArg, + heightArg + ) + ) } catch (exception: Throwable) { wrapped = wrapError(exception) } @@ -3908,17 +4099,17 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.updateCircles", + "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.unregisterImage", codec ) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val circlesArg = args[1] as List + val imageDescriptorArg = args[0] as ImageDescriptorDto var wrapped: List try { - wrapped = listOf(api.updateCircles(viewIdArg, circlesArg)) + api.unregisterImage(imageDescriptorArg) + wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) } @@ -3932,18 +4123,14 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.removeCircles", + "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.getRegisteredImages", codec ) if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val circlesArg = args[1] as List + channel.setMessageHandler { _, reply -> var wrapped: List try { - api.removeCircles(viewIdArg, circlesArg) - wrapped = listOf(null) + wrapped = listOf(api.getRegisteredImages()) } catch (exception: Throwable) { wrapped = wrapError(exception) } @@ -3957,16 +4144,14 @@ interface NavigationViewApi { val channel = BasicMessageChannel( binaryMessenger, - "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.clearCircles", + "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.clearRegisteredImages", codec ) if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val viewIdArg = args[0].let { if (it is Int) it.toLong() else it as Long } + channel.setMessageHandler { _, reply -> var wrapped: List try { - api.clearCircles(viewIdArg) + api.clearRegisteredImages() wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -3986,30 +4171,18 @@ private object NavigationViewEventApiCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 128.toByte() -> { - return (readValue(buffer) as? List)?.let { CircleClickedEventDto.fromList(it) } + return (readValue(buffer) as? List)?.let { CameraPositionDto.fromList(it) } } 129.toByte() -> { return (readValue(buffer) as? List)?.let { LatLngDto.fromList(it) } } - 130.toByte() -> { - return (readValue(buffer) as? List)?.let { MarkerDragEventDto.fromList(it) } - } - 131.toByte() -> { - return (readValue(buffer) as? List)?.let { MarkerEventDto.fromList(it) } - } - 132.toByte() -> { - return (readValue(buffer) as? List)?.let { PolygonClickedEventDto.fromList(it) } - } - 133.toByte() -> { - return (readValue(buffer) as? List)?.let { PolylineClickedEventDto.fromList(it) } - } else -> super.readValueOfType(type, buffer) } } override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { - is CircleClickedEventDto -> { + is CameraPositionDto -> { stream.write(128) writeValue(stream, value.toList()) } @@ -4017,22 +4190,6 @@ private object NavigationViewEventApiCodec : StandardMessageCodec() { stream.write(129) writeValue(stream, value.toList()) } - is MarkerDragEventDto -> { - stream.write(130) - writeValue(stream, value.toList()) - } - is MarkerEventDto -> { - stream.write(131) - writeValue(stream, value.toList()) - } - is PolygonClickedEventDto -> { - stream.write(132) - writeValue(stream, value.toList()) - } - is PolylineClickedEventDto -> { - stream.write(133) - writeValue(stream, value.toList()) - } else -> super.writeValue(stream, value) } } @@ -4097,11 +4254,16 @@ class NavigationViewEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onMarkerEvent(msgArg: MarkerEventDto, callback: (Result) -> Unit) { + fun onMarkerEvent( + viewIdArg: Long, + markerIdArg: String, + eventTypeArg: MarkerEventTypeDto, + callback: (Result) -> Unit + ) { val channelName = "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerEvent" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(viewIdArg, markerIdArg, eventTypeArg.raw)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -4114,11 +4276,17 @@ class NavigationViewEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onMarkerDragEvent(msgArg: MarkerDragEventDto, callback: (Result) -> Unit) { + fun onMarkerDragEvent( + viewIdArg: Long, + markerIdArg: String, + eventTypeArg: MarkerDragEventTypeDto, + positionArg: LatLngDto, + callback: (Result) -> Unit + ) { val channelName = "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(viewIdArg, markerIdArg, eventTypeArg.raw, positionArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -4131,11 +4299,11 @@ class NavigationViewEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onPolygonClicked(msgArg: PolygonClickedEventDto, callback: (Result) -> Unit) { + fun onPolygonClicked(viewIdArg: Long, polygonIdArg: String, callback: (Result) -> Unit) { val channelName = "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolygonClicked" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(viewIdArg, polygonIdArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -4148,11 +4316,11 @@ class NavigationViewEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onPolylineClicked(msgArg: PolylineClickedEventDto, callback: (Result) -> Unit) { + fun onPolylineClicked(viewIdArg: Long, polylineIdArg: String, callback: (Result) -> Unit) { val channelName = "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolylineClicked" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(viewIdArg, polylineIdArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -4165,11 +4333,88 @@ class NavigationViewEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onCircleClicked(msgArg: CircleClickedEventDto, callback: (Result) -> Unit) { + fun onCircleClicked(viewIdArg: Long, circleIdArg: String, callback: (Result) -> Unit) { val channelName = "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCircleClicked" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(viewIdArg, circleIdArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + + fun onNavigationUIEnabledChanged( + viewIdArg: Long, + navigationUIEnabledArg: Boolean, + callback: (Result) -> Unit + ) { + val channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onNavigationUIEnabledChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(viewIdArg, navigationUIEnabledArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + + fun onMyLocationClicked(viewIdArg: Long, callback: (Result) -> Unit) { + val channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationClicked" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(viewIdArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + + fun onMyLocationButtonClicked(viewIdArg: Long, callback: (Result) -> Unit) { + val channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationButtonClicked" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(viewIdArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + + fun onCameraChanged( + viewIdArg: Long, + eventTypeArg: CameraEventTypeDto, + positionArg: CameraPositionDto, + callback: (Result) -> Unit + ) { + val channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCameraChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(viewIdArg, eventTypeArg.raw, positionArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -4228,12 +4473,15 @@ private object NavigationSessionApiCodec : StandardMessageCodec() { } } 140.toByte() -> { - return (readValue(buffer) as? List)?.let { RoutingOptionsDto.fromList(it) } + return (readValue(buffer) as? List)?.let { RouteTokenOptionsDto.fromList(it) } } 141.toByte() -> { - return (readValue(buffer) as? List)?.let { SimulationOptionsDto.fromList(it) } + return (readValue(buffer) as? List)?.let { RoutingOptionsDto.fromList(it) } } 142.toByte() -> { + return (readValue(buffer) as? List)?.let { SimulationOptionsDto.fromList(it) } + } + 143.toByte() -> { return (readValue(buffer) as? List)?.let { SpeedAlertOptionsDto.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -4290,18 +4538,22 @@ private object NavigationSessionApiCodec : StandardMessageCodec() { stream.write(139) writeValue(stream, value.toList()) } - is RoutingOptionsDto -> { + is RouteTokenOptionsDto -> { stream.write(140) writeValue(stream, value.toList()) } - is SimulationOptionsDto -> { + is RoutingOptionsDto -> { stream.write(141) writeValue(stream, value.toList()) } - is SpeedAlertOptionsDto -> { + is SimulationOptionsDto -> { stream.write(142) writeValue(stream, value.toList()) } + is SpeedAlertOptionsDto -> { + stream.write(143) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -4310,7 +4562,10 @@ private object NavigationSessionApiCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface NavigationSessionApi { /** General. */ - fun createNavigationSession(callback: (Result) -> Unit) + fun createNavigationSession( + abnormalTerminationReportingEnabled: Boolean, + callback: (Result) -> Unit + ) fun isInitialized(): Boolean @@ -4326,6 +4581,8 @@ interface NavigationSessionApi { fun areTermsAccepted(): Boolean fun resetTermsAccepted() + + fun getNavSDKVersion(): String /** Navigation. */ fun isGuidanceRunning(): Boolean @@ -4333,7 +4590,7 @@ interface NavigationSessionApi { fun stopGuidance() - fun setDestinations(msg: DestinationsDto, callback: (Result) -> Unit) + fun setDestinations(destinations: DestinationsDto, callback: (Result) -> Unit) fun clearDestinations() @@ -4409,8 +4666,11 @@ interface NavigationSessionApi { codec ) if (api != null) { - channel.setMessageHandler { _, reply -> - api.createNavigationSession() { result: Result -> + channel.setMessageHandler { message, reply -> + val args = message as List + val abnormalTerminationReportingEnabledArg = args[0] as Boolean + api.createNavigationSession(abnormalTerminationReportingEnabledArg) { + result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -4540,6 +4800,27 @@ interface NavigationSessionApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.getNavSDKVersion", + codec + ) + if (api != null) { + channel.setMessageHandler { _, reply -> + var wrapped: List + try { + wrapped = listOf(api.getNavSDKVersion()) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( @@ -4615,8 +4896,8 @@ interface NavigationSessionApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val msgArg = args[0] as DestinationsDto - api.setDestinations(msgArg) { result: Result -> + val destinationsArg = args[0] as DestinationsDto + api.setDestinations(destinationsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -5134,44 +5415,11 @@ private object NavigationSessionEventApiCodec : StandardMessageCodec() { return (readValue(buffer) as? List)?.let { LatLngDto.fromList(it) } } 129.toByte() -> { - return (readValue(buffer) as? List)?.let { LatLngDto.fromList(it) } - } - 130.toByte() -> { - return (readValue(buffer) as? List)?.let { NavigationSessionEventDto.fromList(it) } - } - 131.toByte() -> { return (readValue(buffer) as? List)?.let { NavigationWaypointDto.fromList(it) } } - 132.toByte() -> { - return (readValue(buffer) as? List)?.let { OnArrivalEventDto.fromList(it) } - } - 133.toByte() -> { - return (readValue(buffer) as? List)?.let { - RemainingTimeOrDistanceChangedEventDto.fromList(it) - } - } - 134.toByte() -> { - return (readValue(buffer) as? List)?.let { ReroutingEventDto.fromList(it) } - } - 135.toByte() -> { - return (readValue(buffer) as? List)?.let { - RoadSnappedLocationUpdatedEventDto.fromList(it) - } - } - 136.toByte() -> { - return (readValue(buffer) as? List)?.let { - RoadSnappedRawLocationUpdatedEventDto.fromList(it) - } - } - 137.toByte() -> { - return (readValue(buffer) as? List)?.let { RouteChangedEventDto.fromList(it) } - } - 138.toByte() -> { + 130.toByte() -> { return (readValue(buffer) as? List)?.let { SpeedingUpdatedEventDto.fromList(it) } } - 139.toByte() -> { - return (readValue(buffer) as? List)?.let { TrafficUpdatedEventDto.fromList(it) } - } else -> super.readValueOfType(type, buffer) } } @@ -5182,48 +5430,12 @@ private object NavigationSessionEventApiCodec : StandardMessageCodec() { stream.write(128) writeValue(stream, value.toList()) } - is LatLngDto -> { - stream.write(129) - writeValue(stream, value.toList()) - } - is NavigationSessionEventDto -> { - stream.write(130) - writeValue(stream, value.toList()) - } is NavigationWaypointDto -> { - stream.write(131) - writeValue(stream, value.toList()) - } - is OnArrivalEventDto -> { - stream.write(132) - writeValue(stream, value.toList()) - } - is RemainingTimeOrDistanceChangedEventDto -> { - stream.write(133) - writeValue(stream, value.toList()) - } - is ReroutingEventDto -> { - stream.write(134) - writeValue(stream, value.toList()) - } - is RoadSnappedLocationUpdatedEventDto -> { - stream.write(135) - writeValue(stream, value.toList()) - } - is RoadSnappedRawLocationUpdatedEventDto -> { - stream.write(136) - writeValue(stream, value.toList()) - } - is RouteChangedEventDto -> { - stream.write(137) + stream.write(129) writeValue(stream, value.toList()) } is SpeedingUpdatedEventDto -> { - stream.write(138) - writeValue(stream, value.toList()) - } - is TrafficUpdatedEventDto -> { - stream.write(139) + stream.write(130) writeValue(stream, value.toList()) } else -> super.writeValue(stream, value) @@ -5239,12 +5451,9 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { val codec: MessageCodec by lazy { NavigationSessionEventApiCodec } } - fun onNavigationSessionEvent( - msgArg: NavigationSessionEventDto, - callback: (Result) -> Unit - ) { + fun onSpeedingUpdated(msgArg: SpeedingUpdatedEventDto, callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onNavigationSessionEvent" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(listOf(msgArg)) { if (it is List<*>) { @@ -5259,11 +5468,11 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onSpeedingUpdated(msgArg: SpeedingUpdatedEventDto, callback: (Result) -> Unit) { + fun onRoadSnappedLocationUpdated(locationArg: LatLngDto, callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(locationArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -5276,14 +5485,11 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onRoadSnappedLocationUpdated( - msgArg: RoadSnappedLocationUpdatedEventDto, - callback: (Result) -> Unit - ) { + fun onRoadSnappedRawLocationUpdated(locationArg: LatLngDto, callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(locationArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -5296,14 +5502,11 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onRoadSnappedRawLocationUpdated( - msgArg: RoadSnappedRawLocationUpdatedEventDto, - callback: (Result) -> Unit - ) { + fun onArrival(waypointArg: NavigationWaypointDto, callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(waypointArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -5316,11 +5519,11 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onArrival(msgArg: OnArrivalEventDto, callback: (Result) -> Unit) { + fun onRouteChanged(callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(null) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -5333,11 +5536,15 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } - fun onRouteChanged(msgArg: RouteChangedEventDto, callback: (Result) -> Unit) { + fun onRemainingTimeOrDistanceChanged( + remainingTimeArg: Double, + remainingDistanceArg: Double, + callback: (Result) -> Unit + ) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(remainingTimeArg, remainingDistanceArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -5349,15 +5556,12 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } } - - fun onRemainingTimeOrDistanceChanged( - msgArg: RemainingTimeOrDistanceChangedEventDto, - callback: (Result) -> Unit - ) { + /** Android-only event. */ + fun onTrafficUpdated(callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(null) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -5369,12 +5573,12 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } } - /** Android only event. */ - fun onTrafficUpdated(msgArg: TrafficUpdatedEventDto, callback: (Result) -> Unit) { + /** Android-only event. */ + fun onRerouting(callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(null) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -5386,12 +5590,12 @@ class NavigationSessionEventApi(private val binaryMessenger: BinaryMessenger) { } } } - /** Android only event. */ - fun onRerouting(msgArg: ReroutingEventDto, callback: (Result) -> Unit) { + /** Android-only event. */ + fun onGpsAvailabilityUpdate(availableArg: Boolean, callback: (Result) -> Unit) { val channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onGpsAvailabilityUpdate" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(msgArg)) { + channel.send(listOf(availableArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) diff --git a/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt b/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt index 2f431e4..a5e8d04 100644 --- a/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt +++ b/android/src/test/kotlin/com/google/maps/flutter/navigation/ConvertTest.kt @@ -18,6 +18,7 @@ package com.google.maps.flutter.navigation import android.graphics.Color import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.Circle import com.google.android.gms.maps.model.JointType import com.google.android.gms.maps.model.LatLng @@ -52,6 +53,8 @@ internal class ConvertTest { @MockK(relaxUnitFun = true) lateinit var polyline: Polyline + @MockK(relaxUnitFun = true) lateinit var bitmapDescriptor: BitmapDescriptor + @Test fun convertMapType_returnsExpectedValue() { assertEquals(GoogleMap.MAP_TYPE_NONE, Convert.convertMapTypeFromDto(MapTypeDto.NONE)) @@ -492,7 +495,7 @@ internal class ConvertTest { @Test fun markerControllerToMarkerOptions_returnsExpectedValue() { - val controller = MarkerController(marker, "Marker_01", true, 0.1F, 0.2F, 0.3F, 0.4F) + val controller = MarkerController(marker, "Marker_01", true, 0.1F, 0.2F, 0.3F, 0.4F, null) every { marker.alpha } returns 0.5F every { marker.isDraggable } returns true every { marker.isFlat } returns true @@ -576,4 +579,15 @@ internal class ConvertTest { assertEquals(4.0, options.zIndex) assertEquals(emptyList(), options.spans) } + + @Test + fun convertRegisteredImageToImageIdDto_returnsExpectedValue() { + val registeredImage = RegisteredImage("Image_0", bitmapDescriptor, 1.0, 10.0, 20.0) + val imageDescriptor = Convert.registeredImageToImageDescriptorDto(registeredImage) + + assertEquals("Image_0", imageDescriptor.registeredImageId) + assertEquals(1.0, imageDescriptor.imagePixelRatio) + assertEquals(10.0, imageDescriptor.width) + assertEquals(20.0, imageDescriptor.height) + } } diff --git a/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerBuilderTest.kt b/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerBuilderTest.kt index 12d1582..79132ea 100644 --- a/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerBuilderTest.kt +++ b/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerBuilderTest.kt @@ -16,12 +16,26 @@ package com.google.maps.flutter.navigation +import com.google.android.gms.maps.model.BitmapDescriptor +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.junit4.MockKRule import kotlin.test.assertEquals +import org.junit.Rule import org.junit.Test internal class MarkerBuilderTest { + @get:Rule val mockkRule = MockKRule(this) + + @MockK(relaxUnitFun = true) lateinit var imageRegistry: ImageRegistry + @MockK(relaxUnitFun = true) lateinit var bitmapDescriptor: BitmapDescriptor + @Test fun markerBuilder_returnsExpectedValue() { + every { imageRegistry.findRegisteredImage("default") } returns null + every { imageRegistry.findRegisteredImage("Image_0") } returns + RegisteredImage("Image_0", bitmapDescriptor, 1.0, null, null) + val optionsIn = MarkerOptionsDto( alpha = 0.5, @@ -33,10 +47,11 @@ internal class MarkerBuilderTest { position = LatLngDto(10.0, 20.0), rotation = 40.0, visible = true, - zIndex = 2.0 + zIndex = 2.0, + icon = ImageDescriptorDto("default", 1.0) ) val builder = MarkerBuilder() - Convert.sinkMarkerOptions(optionsIn, builder) + Convert.sinkMarkerOptions(optionsIn, builder, imageRegistry) val optionsOut = builder.build() assertEquals(0.5F, optionsOut.alpha) @@ -54,5 +69,11 @@ internal class MarkerBuilderTest { assertEquals("Title", optionsOut.title) assertEquals(true, optionsOut.isVisible) assertEquals(2.0F, optionsOut.zIndex) + assertEquals(null, optionsOut.icon) + + val optionsWithIcon = optionsIn.copy(icon = ImageDescriptorDto("Image_0", 1.0)) + Convert.sinkMarkerOptions(optionsWithIcon, builder, imageRegistry) + val optionsOutWithIcon = builder.build() + assertEquals(bitmapDescriptor, optionsOutWithIcon.icon) } } diff --git a/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerControllerTest.kt b/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerControllerTest.kt index a6f3944..cc9075a 100644 --- a/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerControllerTest.kt +++ b/android/src/test/kotlin/com/google/maps/flutter/navigation/MarkerControllerTest.kt @@ -16,8 +16,10 @@ package com.google.maps.flutter.navigation +import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.Marker +import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.junit4.MockKRule import io.mockk.verify @@ -29,9 +31,15 @@ internal class MarkerControllerTest { @get:Rule val mockkRule = MockKRule(this) @MockK(relaxUnitFun = true) lateinit var marker: Marker + @MockK(relaxUnitFun = true) lateinit var imageRegistry: ImageRegistry + @MockK(relaxUnitFun = true) lateinit var bitmapDescriptor: BitmapDescriptor @Test fun markerController_callsExpectedFunctions() { + every { imageRegistry.findRegisteredImage("default") } returns null + every { imageRegistry.findRegisteredImage("Image_0") } returns + RegisteredImage("Image_0", bitmapDescriptor, 1.0, null, null) + val optionsIn = MarkerOptionsDto( alpha = 0.5, @@ -43,10 +51,11 @@ internal class MarkerControllerTest { position = LatLngDto(10.0, 20.0), rotation = 40.0, visible = true, - zIndex = 2.0 + zIndex = 2.0, + icon = ImageDescriptorDto("default", 1.0) ) - val controller = MarkerController(marker, "Marker_0", true, 0.1F, 0.2F, 0.3F, 0.4F) - Convert.sinkMarkerOptions(optionsIn, controller) + val controller = MarkerController(marker, "Marker_0", true, 0.1F, 0.2F, 0.3F, 0.4F, null) + Convert.sinkMarkerOptions(optionsIn, controller, imageRegistry) verify { marker.alpha = 0.5F } verify { marker.setAnchor(0.1F, 0.2F) } @@ -64,5 +73,10 @@ internal class MarkerControllerTest { assertEquals(0.2F, controller.anchorV) assertEquals(0.3F, controller.infoWindowAnchorU) assertEquals(0.4F, controller.infoWindowAnchorV) + verify { marker.setIcon(null) } + + val optionsWithIcon = optionsIn.copy(icon = ImageDescriptorDto("Image_0", 1.0)) + Convert.sinkMarkerOptions(optionsWithIcon, controller, imageRegistry) + verify { marker.setIcon(bitmapDescriptor) } } } diff --git a/dartdoc_options.yaml b/dartdoc_options.yaml index d7d627b..a028247 100644 --- a/dartdoc_options.yaml +++ b/dartdoc_options.yaml @@ -20,7 +20,10 @@ dartdoc: "Navigation View": markdown: doc/navigation-view.md name: Navigation View - categoryOrder: ["Navigation", "Navigation View"] + "Image Registry": + markdown: doc/image-registry.md + name: Image Registry + categoryOrder: ["Navigation", "Navigation View", "Image Registry"] showUndocumentedCategories: true nodoc: ['**/*.g.dart'] diff --git a/doc/image-registry.md b/doc/image-registry.md new file mode 100644 index 0000000..e69de29 diff --git a/example/README.md b/example/README.md index 7cac3a2..a2a6597 100644 --- a/example/README.md +++ b/example/README.md @@ -5,61 +5,88 @@ Demonstrates how to use the google_maps_navigation plugin. ## Setting up API Keys To run the example project, you need to provide your Google Maps API key for both Android and iOS platforms. +Both Android and iOS builds are able to use the `MAPS_API_KEY` variable provided through Dart defines. +However, this can be overridden to use separate, platform-specific API keys if needed. -### Android +### Using Dart defines -1. The project uses the [Google Maps Secrets Gradle Plugin](https://developers.google.com/maps/documentation/android-sdk/secrets-gradle-plugin) for Android to securely manage the API key. +Using Dart defines to provide the Google Maps API key is the preferred method for this example app. It allows the key to be utilized in Dart code for accessing Google Maps services, such as the Routes API. Additionally, Patrol integration tests should also be run with the API key provided via Dart defines. -2. Place your Google Maps API key in the `example/android/local.properties` file in the following format: +Run the app with the API key as a Dart define. +```bash +flutter run --dart-define MAPS_API_KEY=YOUR_API_KEY +``` - ``` - MAPS_API_KEY=YOUR_API_KEY_HERE - ``` +The example app demonstrates multiple ways to provide the Maps API key for platforms. - This key will be automatically used by the plugin during the build process. - - **Note**: To create the local.properties file, open example/android on Android Studio and it will generate that file for you. +### Android specific API key -3. Check if flutter.sdk path is configured on your local.properties file: +For Android, the example app determines the `MAPS_API_KEY` using the following order (first match applies): +1. `MAPS_API_KEY` variable in `local.properties` file +2. `MAPS_API_KEY` variable in environment variables +3. `MAPS_API_KEY` variable in Dart defines - ``` - flutter.sdk=PATH_TO_FLUTTER_SDK - ``` +#### Setting the API Key in local.properties +The project uses the [Google Maps Secrets Gradle Plugin](https://developers.google.com/maps/documentation/android-sdk/secrets-gradle-plugin) for secure API key management. Place your Google Maps API key in `example/android/local.properties` file in the following format: -### iOS +```text +MAPS_API_KEY=YOUR_API_KEY_HERE +``` -On iOS, example application implementation initially attempts to read the `MAPS_API_KEY` from Dart defines. If it is not provided in Dart defines, the implementation will then try to fetch the API key from Xcode environment variables. If it’s still not set, it will default to a placeholder. Here are the options to set the API key for example application: +This key will be specifically used for the Android build, overriding the Dart define value. -**Option 1:** Running from the command line: +> [!NOTE] +> `local.properties` file should always be listed in your .gitignore file to ensure it is not committed to your repository. -```bash -flutter run --dart-define=MAPS_API_KEY=YOUR_API_KEY -``` +### iOS specific API key + +For iOS, the app attempts to read the `MAPS_API_KEY` in this order (first match applies): + +1. `MAPS_API_KEY` variable in Xcode environment variables +2. `MAPS_API_KEY` variable in Dart defines +3. Default API key from `AppDelegate.swift`. + +#### Set the API key -**Option 2:** Add the API key to the runner environment parameters in Xcode by editing the scheme and adding an environment variable named `MAPS_API_KEY`. +**1. Option: Xcode Environment Variables** + +Add an environment variable named `MAPS_API_KEY` in the Xcode scheme settings. + +**2. Option: Directly in AppDelegate.swift** + +Set the API key directly in `example/ios/Runner/AppDelegate.swift`: -**Option 3:** Add the API key directly to the `example/ios/Runner/AppDelegate.swift` file by replacing "YOUR_API_KEY" string with your actual Google Maps API key: ```swift ... override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? -) -> Bool { - var mapsApiKey = findMapApiKeyFromDartDefines("MAPS_API_KEY") ?? ProcessInfo.processInfo.environment["MAPS_API_KEY"] ?? "" - if (mapsApiKey.isEmpty) { - mapsApiKey = "YOUR_API_KEY" // REPLACE THIS TEXT WITH YOUR API KEY - } - GMSServices.provideAPIKey(mapsApiKey) +) -> Bool { + GMSServices.provideAPIKey("YOUR_API_KEY") // REPLACE THIS TEXT WITH YOUR API KEY + GMSServices.setMetalRendererEnabled(true) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } ... ``` -**NOTE**: Please be aware that adding the API key directly to your project files or environment variables can risk exposing the key if you accidentally commit it to a public repository. Always ensure that sensitive information like API keys are not included in your commits. +> [!NOTE] +> Be cautious with API keys. Avoid exposing them in public repositories, especially when hardcoded in the project files or the environment variables. -If you have pod related issues to run the example code you can run the following commands from example/ios folder: - - pod repo update - - pod install +## Running the example app -This should install all required pod files to run the example in iOS. \ No newline at end of file +To run the example app, follow these steps: +1. Start the emulator or connect your device. +2. Run the following command from the root of the example project: + ``` bash + cd example + flutter run + ``` +If you want to run the example app with a specific API key, see the [Setting up API Keys](#setting-up-api-keys) section. + +> [!TIP] +> If you encounter pod-related issues when running the example code on iOS, you can try running the following commands from the `example/ios` folder: +> - pod repo update +> - pod install +> +> These commands will update and install the required pod files specifically for iOS. diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index cee5546..214ab9e 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -70,13 +70,13 @@ android { multiDexEnabled true testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner" - } - - - defaultConfig { resourceConfigurations += ['en'] - manifestPlaceholders = [MAPS_API_KEY: "$System.env.MAPS_API_KEY"] + + // Extract MAPS_API_KEY from Dart defines or environment variables + // and use it as manifest placeholder. + def mapsApiKey = System.env.MAPS_API_KEY ?: findDartDefineValue("MAPS_API_KEY") ?: "" + manifestPlaceholders = [MAPS_API_KEY: mapsApiKey] } buildTypes { @@ -127,3 +127,13 @@ secrets { // Ignore all keys matching the regexp "flutter.*" ignoreList.add("flutter.*") } + +// Helper function to extract specific Dart define value +def findDartDefineValue(key) { + def encodedDartDefines = project.hasProperty('dart-defines') ? project.property('dart-defines') : "" + def defines = encodedDartDefines.split(",").collectEntries { String it -> + def define = new String(it.decodeBase64(), 'UTF-8').split('=') + return [(define.first()): define.last()] + } + return defines.containsKey(key) ? defines[key] : null +} \ No newline at end of file diff --git a/example/assets/2.0x/marker1.png b/example/assets/2.0x/marker1.png new file mode 100644 index 0000000..dfa36e1 Binary files /dev/null and b/example/assets/2.0x/marker1.png differ diff --git a/example/assets/marker1.png b/example/assets/marker1.png new file mode 100644 index 0000000..430f7a8 Binary files /dev/null and b/example/assets/marker1.png differ diff --git a/example/integration_test/camera_test.dart b/example/integration_test/camera_test.dart deleted file mode 100644 index 5d12c9d..0000000 --- a/example/integration_test/camera_test.dart +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2023 Google LLC -// -// 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 -// -// https://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. - -// This is a basic Flutter integration test. -// -// Since integration tests run in a full Flutter application, they can interact -// with the host side of a plugin implementation, unlike Dart unit tests. -// -// For more information about Flutter integration tests, please see -// https://docs.flutter.dev/cookbook/testing/integration/introduction - -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'shared.dart'; - -void main() { - Future checkCameraFollowsLocation( - CameraPosition camera, GoogleNavigationViewController controller) async { - // Check the camera location is close to my location (locationThreshold = ~200m) - final LatLng? myLocation = await controller.getMyLocation(); - expect(myLocation, isNotNull); - expect(camera.target.latitude, closeTo(myLocation!.latitude, 0.02)); - expect(camera.target.longitude, closeTo(myLocation.longitude, 0.02)); - } - - patrolTest('Test camera modes', (PatrolIntegrationTester $) async { - /// Initialize navigation. - final GoogleNavigationViewController controller = await startNavigation($); - - await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRoute(); - - await controller.followMyLocation(CameraPerspective.tilted); - - double distanceToNorth(double angle) { - final double diff = (angle + 180) % 360 - 180; - return (diff < -180 ? diff + 360 : diff).abs(); - } - - // 1. Test default. - // - // The navigation follows the user's location in tilted mode. - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - CameraPosition camera = await controller.getCameraPosition(); - - debugPrint('Default tilt: ${camera.tilt}'); - debugPrint('Default zoom: ${camera.zoom}'); - debugPrint('Default bearing: ${camera.bearing}'); - - // Verify that the follow my locate camera mode is active. - await checkCameraFollowsLocation(camera, controller); - - // Strong tilting (Android 45, iOS 55 degrees) - expect(camera.tilt, greaterThanOrEqualTo(40)); - - // Zoomed map (Android 18, iOS 17.25) - expect(camera.zoom, greaterThanOrEqualTo(16)); - - LatLng oldTarget = camera.target; - double oldZoom = camera.zoom; - - // 2. Test CameraPerspective.topDownHeadingUp. - await controller.followMyLocation(CameraPerspective.topDownHeadingUp); - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - - camera = await controller.getCameraPosition(); - - debugPrint('topDownHeadingUp tilt: ${camera.tilt}'); - debugPrint('topDownHeadingUp zoom: ${camera.zoom}'); - debugPrint('topDownHeadingUp bearing: ${camera.bearing}'); - - // Verify that the follow my locate camera mode is active. - await checkCameraFollowsLocation(camera, controller); - - // No tilt when top-down. - expect(camera.tilt, lessThanOrEqualTo(0.1)); - - // The camera target should have moved. - expect(camera.target != oldTarget, true); - - oldTarget = camera.target; - - // 3. Test CameraPerspective.topDownNorthUp - await controller.followMyLocation(CameraPerspective.topDownNorthUp); - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - - camera = await controller.getCameraPosition(); - - debugPrint('topDownNorthUp tilt: ${camera.tilt}'); - debugPrint('topDownNorthUp zoom: ${camera.zoom}'); - debugPrint('topDownNorthUp bearing: ${camera.bearing}'); - - // Verify that the follow my locate camera mode is active. - await checkCameraFollowsLocation(camera, controller); - - // No tilt when top-down. - expect(camera.tilt, lessThanOrEqualTo(0.1)); - - // Expect zoom to be farther. - expect(oldZoom, greaterThanOrEqualTo(camera.zoom)); - - // North-up means zero bearing. - // iOS reports 0 degrees, Android is more fuzzy e.g. 359.7 degrees. - expect(distanceToNorth(camera.bearing), lessThanOrEqualTo(2.0)); - - // The camera target should have moved again. - expect(camera.target != oldTarget, true); - - oldTarget = camera.target; - - // 4. Test CameraPerspective.tilted - await controller.followMyLocation(CameraPerspective.tilted); - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - - camera = await controller.getCameraPosition(); - - debugPrint('tilted tilt: ${camera.tilt}'); - debugPrint('tilted zoom: ${camera.zoom}'); - debugPrint('tilted bearing: ${camera.bearing}'); - - // Verify that the follow my locate camera mode is active. - await checkCameraFollowsLocation(camera, controller); - - // Repeat tests done with the default state above - expect(camera.tilt, greaterThanOrEqualTo(40)); - expect(camera.zoom, greaterThanOrEqualTo(16)); - expect(distanceToNorth(camera.bearing), greaterThanOrEqualTo(0.01)); - - // The camera target should have moved again. - expect(camera.target != oldTarget, true); - - oldTarget = camera.target; - oldZoom = camera.zoom; - - // 5. Test showRouteOverview(). - await controller.showRouteOverview(); - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - - camera = await controller.getCameraPosition(); - - debugPrint('showRouteOverview tilt: ${camera.tilt}'); - debugPrint('showRouteOverview zoom: ${camera.zoom}'); - debugPrint('showRouteOverview bearing: ${camera.bearing}'); - - // No tilt when in route overview. - expect(camera.tilt, lessThanOrEqualTo(0.1)); - - // Expect zoom to be farthest away. - expect(oldZoom, greaterThanOrEqualTo(camera.zoom)); - - // Route is shown north up. - expect(distanceToNorth(camera.bearing), lessThanOrEqualTo(1.0)); - - // The camera target should have moved again. - expect(camera.target != oldTarget, true); - - // 6. Test the optional follow my location parameter zoom level - const double zoomLevel = 8.5; - await controller.followMyLocation(CameraPerspective.tilted, - zoomLevel: zoomLevel); - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - camera = await controller.getCameraPosition(); - expect(camera.zoom, closeTo(zoomLevel, 0.01)); - }); - - patrolTest( - 'Test using the recenter button to move the camera during navigation', - (PatrolIntegrationTester $) async { - /// Initialize navigation. - final GoogleNavigationViewController controller = await startNavigation($); - await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRoute(); - - // Reset the camera to my location. - await controller.followMyLocation(CameraPerspective.tilted); - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - - // Check the camera location is close to my location - CameraPosition camera = await controller.getCameraPosition(); - await checkCameraFollowsLocation(camera, controller); - - // Check the re-center button is enabled - final bool isEnabled = await controller.isRecenterButtonEnabled(); - expect(isEnabled, true); - - /// Move camera away to reveal the re-center button. - final CameraUpdate updateCameraPosition = - CameraUpdate.newCameraPosition(CameraPosition( - target: LatLng( - latitude: camera.target.latitude + 0.2, - longitude: camera.target.longitude + 0.2, - ))); - await controller.moveCamera(updateCameraPosition); - await $.pumpAndSettle(duration: const Duration(milliseconds: 500)); - - // Verify the distance is considerable 0.05 ~= 5.6km - camera = await controller.getCameraPosition(); - final LatLng? myLocation = await controller.getMyLocation(); - expect(myLocation, isNotNull); - expect(camera.target.latitude - myLocation!.latitude, greaterThan(0.05)); - expect(camera.target.longitude - myLocation.longitude, greaterThan(0.05)); - - // Tap the button to re-center the camera to my location. - if (Platform.isAndroid) { - await $.native.tap(Selector(text: 'Re-center')); - } else if (Platform.isIOS) { - await $.native.tap(Selector(text: 'RE-CENTER')); - } else { - fail('Unsupported platform: ${Platform.operatingSystem}'); - } - await $.pumpAndSettle(duration: const Duration(milliseconds: 3000)); - - /// Verify the camera matches my location again. - camera = await controller.getCameraPosition(); - await checkCameraFollowsLocation(camera, controller); - }); -} diff --git a/example/integration_test/navigation_test.dart b/example/integration_test/navigation_test.dart deleted file mode 100644 index e5d227f..0000000 --- a/example/integration_test/navigation_test.dart +++ /dev/null @@ -1,732 +0,0 @@ -// Copyright 2023 Google LLC -// -// 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 -// -// https://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. - -// This is a basic Flutter integration test. -// -// Since integration tests run in a full Flutter application, they can interact -// with the host side of a plugin implementation, unlike Dart unit tests. -// -// For more information about Flutter integration tests, please see -// https://docs.flutter.dev/cookbook/testing/integration/introduction - -import 'dart:async'; -import 'dart:io'; -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/material.dart'; -import 'package:google_maps_navigation/src/google_maps_navigation_platform_interface.dart'; -import 'package:google_maps_navigation/src/inspector/inspector_platform.dart'; -import 'shared.dart'; - -void main() { - GoogleMapsNavigationPlatform.instance.enableDebugInspection(); - - final GoogleNavigationInspectorPlatform inspector = - GoogleNavigationInspectorPlatform.instance!; - - bool isPhysicalDevice = false; - final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - if (Platform.isIOS) { - deviceInfo.iosInfo.then((IosDeviceInfo info) { - isPhysicalDevice = info.isPhysicalDevice; - debugPrint('isPhysicalDevice: $isPhysicalDevice'); - }); - } - - patrolTest( - 'Test navigation guidance for a single destination with location updates', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - final Completer hasArrived = Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - /// Specify tolerance and navigation start and end coordinates. - /// start = '1298 California St', end = '1630 California St' - const double tolerance = 0.0005; - const double startX = 37.79136614772824, - startY = -122.41565900473043, - endX = 37.79074126174107, - endY = -122.4213915308914; - - /// Finish executing the tests once onArrival event comes in - /// and test that the guidance stops. - Future onArrivalEvent(OnArrivalEvent msg) async { - hasArrived.complete(); - await GoogleMapsNavigator.stopGuidance(); - } - - GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); - - /// Simulate location. - await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: startX, - longitude: startY, - )); - - /// Set Destination. - final Destinations destinations = Destinations( - waypoints: [ - NavigationWaypoint.withLatLngTarget( - title: '1630 California St', - target: const LatLng( - latitude: endX, - longitude: endY, - ), - ), - ], - displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), - ); - final NavigationRouteStatus status = - await GoogleMapsNavigator.setDestinations(destinations); - expect(status, NavigationRouteStatus.statusOk); - await $.pumpAndSettle(); - - expect(await GoogleMapsNavigator.isGuidanceRunning(), false); - - /// Start guidance. - await GoogleMapsNavigator.startGuidance(); - await $.pumpAndSettle(); - - /// Test that the received coordinates fit between start and end location coordinates within tolerance. - void onLocationEvent(RoadSnappedLocationUpdatedEvent msg) { - expectSync(msg.location.latitude, lessThanOrEqualTo(startX + tolerance)); - expectSync(msg.location.latitude, greaterThanOrEqualTo(endX - tolerance)); - expectSync(msg.location.longitude, lessThanOrEqualTo(startY + tolerance)); - expectSync( - msg.location.longitude, greaterThanOrEqualTo(endY - tolerance)); - } - - await GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener( - onLocationEvent); - - /// Start simulation. - await GoogleMapsNavigator.simulator - .simulateLocationsAlongExistingRouteWithOptions(SimulationOptions( - speedMultiplier: 100, - )); - - expect(await GoogleMapsNavigator.isGuidanceRunning(), true); - await hasArrived.future; - expect(await GoogleMapsNavigator.isGuidanceRunning(), false); - }); - - patrolTest( - 'Test navigation guidance for multiple destinations with location updates', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - final Completer navigationFinished = Completer(); - int arrivalEventCount = 0; - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - /// Specify tolerance and navigation start and end coordinates. - /// start = '1298 California St', mid = 1440 California St', end = 1630 California St'. - const double tolerance = 0.0005; - const double startX = 37.79136614772824, - startY = -122.41565900473043, - midX = 37.79107688014248, - midY = -122.41816675204078, - endX = 37.79074126174107, - endY = -122.4213915308914; - - Future onArrivalEvent(OnArrivalEvent msg) async { - arrivalEventCount += 1; - debugPrint('arrivalCount: $arrivalEventCount'); - await GoogleMapsNavigator.continueToNextDestination(); - - /// Finish executing the tests once 2 onArrival events come in. - /// Test the guidance stops on last Arrival. - if (arrivalEventCount == 2) { - navigationFinished.complete(); - } - } - - GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); - - /// Simulate location (1298 California St) - await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: startX, - longitude: startY, - )); - - /// Set Destination. - final Destinations destinations = Destinations( - waypoints: [ - NavigationWaypoint.withLatLngTarget( - title: '1440 California St', - target: const LatLng( - latitude: midX, - longitude: midY, - ), - ), - NavigationWaypoint.withLatLngTarget( - title: '1630 California St', - target: const LatLng( - latitude: endX, - longitude: endY, - ), - ), - ], - displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), - ); - final NavigationRouteStatus status = - await GoogleMapsNavigator.setDestinations(destinations); - expect(status, NavigationRouteStatus.statusOk); - await $.pumpAndSettle(); - - expect(await GoogleMapsNavigator.isGuidanceRunning(), false); - - /// Start guidance. - await GoogleMapsNavigator.startGuidance(); - await $.pumpAndSettle(); - - /// Test that the received coordinates fit between start and end location coordinates within tolerance. - void onLocationEvent(RoadSnappedLocationUpdatedEvent msg) { - /// Sometimes on Android, the simulator "overshoots" and passes the destination - /// with high speedMultiplier. - if (arrivalEventCount < 2) { - expectSync( - msg.location.latitude, lessThanOrEqualTo(startX + tolerance)); - expectSync( - msg.location.latitude, greaterThanOrEqualTo(endX - tolerance)); - expectSync( - msg.location.longitude, lessThanOrEqualTo(startY + tolerance)); - expectSync( - msg.location.longitude, greaterThanOrEqualTo(endY - tolerance)); - } - } - - await GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener( - onLocationEvent); - - /// Start simulation. - await GoogleMapsNavigator.simulator - .simulateLocationsAlongExistingRouteWithOptions(SimulationOptions( - speedMultiplier: 10, - )); - - expect(await GoogleMapsNavigator.isGuidanceRunning(), true); - await navigationFinished.future; - expect(await GoogleMapsNavigator.isGuidanceRunning(), false); - }); - - patrolTest( - 'Test location updates when not actively navigating (simulateAlongNewRoute)', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - final Completer finishTest = Completer(); - bool hasArrived = false; - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - /// Specify tolerance and navigation start and end coordinates. - /// start = '1298 California St', end = '1630 California St' - const double tolerance = 0.001; - const double startX = 37.79136614772824, - startY = -122.41565900473043, - endX = 37.79074126174107, - endY = -122.4213915308914; - - /// Simulate location. - await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: startX, - longitude: startY, - )); - - /// Create a waypoint. - final List waypoint = [ - NavigationWaypoint.withLatLngTarget( - title: '1630 California St', - target: const LatLng( - latitude: endX, - longitude: endY, - ), - ), - ]; - - /// Test that the received coordinates fit between start and end location coordinates within tolerance. - /// End the test when user arrives to the end location coordinates within tolerance. - void onLocationEvent(RoadSnappedLocationUpdatedEvent msg) { - if ((!hasArrived) && - (msg.location.latitude - endX <= tolerance) && - (endY - msg.location.longitude <= tolerance)) { - hasArrived = true; - finishTest.complete(); - } else { - expectSync( - msg.location.latitude, lessThanOrEqualTo(startX + tolerance)); - expectSync( - msg.location.latitude, greaterThanOrEqualTo(endX - tolerance)); - expectSync( - msg.location.longitude, lessThanOrEqualTo(startY + tolerance)); - expectSync( - msg.location.longitude, greaterThanOrEqualTo(endY - tolerance)); - } - } - - await GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener( - onLocationEvent); - - /// Start simulation. - await GoogleMapsNavigator.simulator - .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( - waypoint, - RoutingOptions(routingStrategy: NavigationRoutingStrategy.shorter), - SimulationOptions(speedMultiplier: 100)); - - await finishTest.future; - }); - - patrolTest('Test that the navigation and updates stop onArrival', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - /// Simulate location (1298 California St) - await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: 37.79136614772824, - longitude: -122.41565900473043, - )); - - /// Set Destination. - final Destinations destinations = Destinations( - waypoints: [ - NavigationWaypoint.withLatLngTarget( - title: 'Grace Cathedral', - target: const LatLng( - latitude: 37.791957, - longitude: -122.412529, - ), - ), - ], - displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), - ); - final NavigationRouteStatus status = - await GoogleMapsNavigator.setDestinations(destinations); - expect(status, NavigationRouteStatus.statusOk); - await $.pumpAndSettle(); - - /// Start guidance. - await GoogleMapsNavigator.startGuidance(); - await $.pumpAndSettle(); - - /// Start simulation. - await GoogleMapsNavigator.simulator - .simulateLocationsAlongExistingRouteWithOptions(SimulationOptions( - speedMultiplier: 100, - )); - await $.pumpAndSettle(); - - /// Test that guidance ends after onArrival. - void onArrivalEvent(OnArrivalEvent msg) { - expect(GoogleMapsNavigator.isGuidanceRunning(), false); - } - - GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); - }); - - // Skip test on iOS simulator. - if (Platform.isIOS && !isPhysicalDevice) { - } else { - patrolTest( - 'Test that onNetworkError is received when no network connection.', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - /// Simulate location (1298 California St) - await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: 37.79136614772824, - longitude: -122.41565900473043, - )); - - /// Create a waypoint. - final List waypoint = [ - NavigationWaypoint.withLatLngTarget( - title: 'Grace Cathedral', - target: const LatLng( - latitude: 37.791957, - longitude: -122.412529, - ), - ), - ]; - - /// Set Destination. - final Destinations destinations = Destinations( - waypoints: waypoint, - displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), - ); - - /// Cut network connection. - await $.native.disableCellular(); - await $.native.disableWifi(); - - /// Test that the error is received. - final NavigationRouteStatus routeStatus = - await GoogleMapsNavigator.setDestinations(destinations); - expect(routeStatus, equals(NavigationRouteStatus.networkError)); - - final NavigationRouteStatus routeStatusSim = await GoogleMapsNavigator - .simulator - .simulateLocationsAlongNewRoute(waypoint); - expect(routeStatusSim, equals(NavigationRouteStatus.networkError)); - - /// Re-enable network connection. - await $.native.enableCellular(); - await $.native.enableWifi(); - }); - } - - patrolTest( - 'Test that routeNotFound error is received when no route is found.', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - /// Simulate location (1298 California St) - await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: 37.79136614772824, - longitude: -122.41565900473043, - )); - - /// Create a waypoint. - final List waypoint = [ - NavigationWaypoint.withLatLngTarget( - title: 'Kiviniemi', - target: const LatLng( - latitude: 70.06006451782844, - longitude: 27.390062785112185, - ), - ), - ]; - - /// Set Destination to another continent. - final Destinations destinations = Destinations( - waypoints: waypoint, - displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), - ); - - /// Test that the error is received. - final NavigationRouteStatus routeStatus = - await GoogleMapsNavigator.setDestinations(destinations); - expect(routeStatus, equals(NavigationRouteStatus.routeNotFound)); - - final NavigationRouteStatus routeStatusSim = await GoogleMapsNavigator - .simulator - .simulateLocationsAlongNewRoute(waypoint); - expect(routeStatusSim, equals(NavigationRouteStatus.routeNotFound)); - }); - - patrolTest('Test route structures during navigation', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - final Completer hasArrived = Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - /// Finish executing the tests once onArrival event comes in. - void onArrivalEvent(OnArrivalEvent msg) { - hasArrived.complete(); - } - - GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); - - /// Simulate location (1298 California St) - await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: 37.79136614772824, - longitude: -122.41565900473043, - )); - - /// Set Destination. - final Destinations destinations = Destinations( - waypoints: [ - NavigationWaypoint.withLatLngTarget( - title: 'Grace Cathedral', - target: const LatLng( - latitude: 37.791957, - longitude: -122.412529, - ), - ), - ], - displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), - ); - final NavigationRouteStatus status = - await GoogleMapsNavigator.setDestinations(destinations); - expect(status, NavigationRouteStatus.statusOk); - await $.pumpAndSettle(); - - /// Start guidance. - await GoogleMapsNavigator.startGuidance(); - await $.pumpAndSettle(); - - final List beginRouteSegments = - await GoogleMapsNavigator.getRouteSegments(); - final RouteSegment? beginCurrentSegment = - await GoogleMapsNavigator.getCurrentRouteSegment(); - final List beginTraveledRoute = - await GoogleMapsNavigator.getTraveledRoute(); - - /// The route segments list is not empty. - expect(beginRouteSegments.length, greaterThan(0)); - - /// The current route segment. - expect(beginCurrentSegment, isNotNull); - - /// Start simulation. - await GoogleMapsNavigator.simulator - .simulateLocationsAlongExistingRouteWithOptions(SimulationOptions( - speedMultiplier: 30, - )); - await $.pumpAndSettle(); - - await hasArrived.future; - - // Values after arrival - final List endTraveledRoute = - await GoogleMapsNavigator.getTraveledRoute(); - final RouteSegment? endSegment = - await GoogleMapsNavigator.getCurrentRouteSegment(); - - /// Traveled route is different than in the beginning - expect(endTraveledRoute.length, greaterThan(beginTraveledRoute.length)); - - /// Check that the last segment is near target destination. - expect(endSegment!.destinationLatLng.longitude, greaterThan(-122.413)); - expect(endSegment.destinationLatLng.longitude, lessThan(-122.411)); - }); - - patrolTest('Test that the navigation session is attached to existing map', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - bool isSessionAttached; - final GoogleNavigationViewController viewController = - await viewControllerCompleter.future; - final int viewId = viewController.getViewId(); - if (Platform.isIOS) { - isSessionAttached = await inspector.isViewAttachedToSession(viewId); - expect(isSessionAttached, false); - } - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - isSessionAttached = await inspector.isViewAttachedToSession(viewId); - expect(isSessionAttached, true); - }); - - patrolTest('Test that the map attaches existing navigation session to itself', - (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - // Accept ToS and grant location permission if not accepted/granted. - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); - - final GoogleNavigationViewController viewController = - await viewControllerCompleter.future; - final int viewId = viewController.getViewId(); - final bool isSessionAttached = await GoogleNavigationInspectorPlatform - .instance! - .isViewAttachedToSession(viewId); - expect(isSessionAttached, true); - }); -} diff --git a/example/integration_test/run_tests.dart b/example/integration_test/run_tests.dart deleted file mode 100644 index f624303..0000000 --- a/example/integration_test/run_tests.dart +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2023 Google LLC -// -// 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 -// -// https://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. - -import 'dart:convert'; -import 'dart:io'; -import 'package:pub_semver/pub_semver.dart'; - -// List of files to test -final List files = [ - 'integration_test/initialization_test.dart', - 'integration_test/session_test.dart', - 'integration_test/navigation_test.dart', - 'integration_test/navigation_ui_test.dart', - 'integration_test/camera_test.dart', - 'integration_test/map_test.dart', - 'integration_test/event_listener_test.dart', - 'integration_test/marker_polygon_polyline_circle_test.dart' -]; - -/// Runs patrol integration tests in specified order. -/// -/// Usage: -/// dart run_tests.dart -void main(List arguments) async { - final VersionConstraint patrolVersionConstraint = - VersionConstraint.parse('>=2.4.0 <2.5.0'); - - if (!await checkPatrolVersion(patrolVersionConstraint)) { - stderr.writeln( - 'patrol_cli matching version constraints $patrolVersionConstraint is required.'); - exit(1); - } - - final List patrolArguments = ['test', '-t', files.join(',')]; - - // Pass command line arguments to patrol. - patrolArguments.addAll(arguments); - - final Process process = await Process.start( - 'patrol', - patrolArguments, - runInShell: true, - ); - - // Stream stdout to console. - process.stdout.transform(utf8.decoder).listen((String data) { - stdout.write(data); - }); - - // Stream stderr to console. - process.stderr.transform(utf8.decoder).listen((String data) { - stderr.write(data); - }); - - final int exitCode = await process.exitCode; - if (exitCode != 0) { - exit(exitCode); // Exit with the same exit code as the patrol command. - } -} - -Future checkPatrolVersion( - VersionConstraint patrolVersionConstraint) async { - final ProcessResult result = - await Process.run('patrol', ['--version'], runInShell: true); - final String output = result.stdout as String; - final RegExpMatch? match = - RegExp(r'patrol_cli v(\d+\.\d+\.\d+)').firstMatch(output); - - if (match != null) { - final Version version = Version.parse(match.group(1)!); - return version.allowsAny(patrolVersionConstraint); - } - - return false; -} diff --git a/example/integration_test/shared.dart b/example/integration_test/shared.dart index bee3aab..57cc053 100644 --- a/example/integration_test/shared.dart +++ b/example/integration_test/shared.dart @@ -33,6 +33,31 @@ export 'package:flutter_test/flutter_test.dart'; export 'package:google_maps_navigation/google_maps_navigation.dart'; export 'package:patrol/patrol.dart'; +/// Location coordinates for starting position simulation in Finland - Näkkäläntie. +const double startLocationLat = 68.5938196099399; +const double startLocationLon = 23.510696979963722; + +const NativeAutomatorConfig _nativeAutomatorConfig = NativeAutomatorConfig( + findTimeout: Duration(seconds: 20), +); + +/// Create a wrapper [patrol] for [patrolTest] with custom options. +void patrol( + String description, + Future Function(PatrolIntegrationTester) callback, { + bool? skip, + NativeAutomatorConfig? nativeAutomatorConfig, +}) { + patrolTest( + description, + timeout: const Timeout( + Duration(seconds: 240)), // Add a 4 minute timeout to tests. + nativeAutomatorConfig: nativeAutomatorConfig ?? _nativeAutomatorConfig, + skip: skip, + callback, + ); +} + /// Pumps a [navigationView] widget in tester [$] and then waits until it settles. Future pumpNavigationView( PatrolIntegrationTester $, GoogleMapsNavigationView navigationView) async { @@ -61,6 +86,7 @@ Future checkTermsAndConditionsAcceptance( 'test_company_name', ); + await $.pumpAndSettle(); // Tap accept or cancel. if (Platform.isAndroid) { await $.native.tap(Selector(text: 'Yes, I am in')); @@ -101,7 +127,12 @@ Future checkLocationDialogAndTosAcceptance( } Future startNavigation( - PatrolIntegrationTester $) async { + PatrolIntegrationTester $, + {void Function(CameraPosition, bool)? onCameraMoveStarted, + void Function(CameraPosition)? onCameraMove, + void Function(CameraPosition)? onCameraIdle, + void Function(CameraPosition)? onCameraStartedFollowingLocation, + void Function(CameraPosition)? onCameraStoppedFollowingLocation}) async { final Completer controllerCompleter = Completer(); @@ -115,6 +146,11 @@ Future startNavigation( onViewCreated: (GoogleNavigationViewController viewController) { controllerCompleter.complete(viewController); }, + onCameraMoveStarted: onCameraMoveStarted, + onCameraMove: onCameraMove, + onCameraIdle: onCameraIdle, + onCameraStartedFollowingLocation: onCameraStartedFollowingLocation, + onCameraStoppedFollowingLocation: onCameraStoppedFollowingLocation, ), ); @@ -125,18 +161,18 @@ Future startNavigation( await $.pumpAndSettle(); await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( - latitude: 38.012087, - longitude: -120.270701, + latitude: startLocationLat, + longitude: startLocationLon, )); /// Set Destination. final Destinations destinations = Destinations( waypoints: [ NavigationWaypoint.withLatLngTarget( - title: 'Grace Cathedral', + title: 'Finland - Leppäjärvi', target: const LatLng( - latitude: 37.791957, - longitude: -122.412529, + latitude: 68.50680417455591, + longitude: 23.310968509112517, ), ), ], @@ -155,3 +191,66 @@ Future startNavigation( return controller; } + +/// Start navigation without setting the destination and optionally +/// simulating starting location with [simulateLocation] and skipping +/// initialization with [initializeNavigation]. +Future startNavigationWithoutDestination( + PatrolIntegrationTester $, { + bool initializeNavigation = true, + bool simulateLocation = false, + void Function(CameraPosition)? onCameraIdle, +}) async { + final Completer controllerCompleter = + Completer(); + + await checkLocationDialogAndTosAcceptance($); + + final Key key = GlobalKey(); + await pumpNavigationView( + $, + GoogleMapsNavigationView( + key: key, + onViewCreated: (GoogleNavigationViewController viewController) { + controllerCompleter.complete(viewController); + }, + onCameraIdle: onCameraIdle, + ), + ); + + final GoogleNavigationViewController controller = + await controllerCompleter.future; + + if (initializeNavigation) { + await GoogleMapsNavigator.initializeNavigationSession(); + await $.pumpAndSettle(); + } + + if (simulateLocation) { + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: startLocationLat, + longitude: startLocationLon, + )); + } + + return controller; +} + +/// A function that waits until a certain condition is met, e.g. until the camera moves where intended. +/// +/// The function constantly sends the Value objects from [getValue] function +/// to the provided [predicate] function until the [predicate] function returns true. +/// Then the function returns that Value. If [maxTries] are reached without +/// predicate returning true, null is returned. +Future waitForValueMatchingPredicate(PatrolIntegrationTester $, + Future Function() getValue, bool Function(Value) predicate, + {int maxTries = 200, int delayMs = 100}) async { + for (int i = 0; i < maxTries; i++) { + final Value currentValue = await getValue(); + if (predicate(currentValue)) { + return currentValue; + } + await $.pump(Duration(milliseconds: delayMs)); + } + return null; +} diff --git a/example/integration_test/initialization_test.dart b/example/integration_test/t01_initialization_test.dart similarity index 53% rename from example/integration_test/initialization_test.dart rename to example/integration_test/t01_initialization_test.dart index 495a6ef..b743c62 100644 --- a/example/integration_test/initialization_test.dart +++ b/example/integration_test/t01_initialization_test.dart @@ -20,10 +20,15 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; + import 'shared.dart'; void main() { - patrolTest('Test session initialization errors', + patrol('C01 - Test session initialization errors', (PatrolIntegrationTester $) async { await GoogleMapsNavigator.resetTermsAccepted(); expect(await GoogleMapsNavigator.areTermsAccepted(), false); @@ -61,6 +66,13 @@ void main() { expect(e, const TypeMatcher()); } + try { + await GoogleMapsNavigator.cleanup(); + fail('Expected SessionInitializationException'); + } on Exception catch (e) { + expect(e, const TypeMatcher()); + } + try { await GoogleMapsNavigator.startGuidance(); fail('Expected SessionNotInitializedException.'); @@ -148,5 +160,109 @@ void main() { fail( 'Expected isGuidanceRunning() to succeed after the successful navigation initialization.'); } + + // Test that SDK version call returns non-empty version string. + final String version = await GoogleMapsNavigator.getNavSDKVersion(); + expect(version.length, greaterThan(0)); + + // Test initializing with abnormal termination reporting enabled. + try { + await GoogleMapsNavigator.initializeNavigationSession( + abnormalTerminationReportingEnabled: false); + } on Exception { + fail('Expected the initialization to go through'); + } + }); + + patrol('C02 - Test Maps initialization', (PatrolIntegrationTester $) async { + final Completer viewControllerCompleter = + Completer(); + + await checkTermsAndConditionsAcceptance($); + await checkLocationDialogAcceptance($); + + // Now the initialization should finally succeed. + try { + await GoogleMapsNavigator.initializeNavigationSession(); + } on SessionInitializationException { + fail('Expected the initialization to go through'); + } + expect(await GoogleMapsNavigator.isInitialized(), true); + + const CameraPosition cameraPosition = + CameraPosition(target: LatLng(latitude: 65, longitude: 25.5), zoom: 12); + const MapType mapType = MapType.satellite; + const bool compassEnabled = false; + const bool rotateGesturesEnabled = false; + const bool scrollGesturesEnabled = false; + const bool tiltGesturesEnabled = false; + const bool zoomGesturesEnabled = false; + const bool scrollGesturesEnabledDuringRotateOrZoom = false; + const bool mapToolbarEnabled = false; + const bool zoomControlsEnabled = false; + const double minZoomPreference = 5.0; + const double maxZoomPreference = 18.0; + const NavigationUIEnabledPreference navigationUiEnabledPreference = + NavigationUIEnabledPreference.automatic; + + /// Display navigation view. + final Key key = GlobalKey(); + await pumpNavigationView( + $, + GoogleMapsNavigationView( + key: key, + onViewCreated: (GoogleNavigationViewController controller) { + controller.setMyLocationEnabled(true); + viewControllerCompleter.complete(controller); + }, + initialCameraPosition: cameraPosition, + initialMapType: mapType, + initialCompassEnabled: compassEnabled, + initialRotateGesturesEnabled: rotateGesturesEnabled, + initialScrollGesturesEnabled: scrollGesturesEnabled, + initialTiltGesturesEnabled: tiltGesturesEnabled, + initialZoomGesturesEnabled: zoomGesturesEnabled, + initialScrollGesturesEnabledDuringRotateOrZoom: + scrollGesturesEnabledDuringRotateOrZoom, + initialMapToolbarEnabled: mapToolbarEnabled, + initialZoomControlsEnabled: zoomControlsEnabled, + initialMinZoomPreference: minZoomPreference, + initialMaxZoomPreference: maxZoomPreference, + // ignore: avoid_redundant_argument_values + initialNavigationUIEnabledPreference: navigationUiEnabledPreference, + ), + ); + + final GoogleNavigationViewController controller = + await viewControllerCompleter.future; + final CameraPosition cameraOut = await controller.getCameraPosition(); + + expect(cameraOut.target.latitude, + closeTo(cameraPosition.target.latitude, 0.1)); + expect(cameraOut.target.longitude, + closeTo(cameraPosition.target.longitude, 0.1)); + expect(cameraOut.zoom, closeTo(cameraPosition.zoom, 0.1)); + expect(await controller.getMapType(), mapType); + expect(await controller.settings.isCompassEnabled(), compassEnabled); + expect(await controller.settings.isRotateGesturesEnabled(), + rotateGesturesEnabled); + expect(await controller.settings.isScrollGesturesEnabled(), + scrollGesturesEnabled); + expect( + await controller.settings.isTiltGesturesEnabled(), tiltGesturesEnabled); + expect( + await controller.settings.isZoomGesturesEnabled(), zoomGesturesEnabled); + expect( + await controller.settings.isScrollGesturesEnabledDuringRotateOrZoom(), + scrollGesturesEnabledDuringRotateOrZoom); + if (Platform.isAndroid) { + expect( + await controller.settings.isMapToolbarEnabled(), mapToolbarEnabled); + expect(await controller.settings.isZoomControlsEnabled(), + zoomControlsEnabled); + } + expect(await controller.getMinZoomPreference(), minZoomPreference); + expect(await controller.getMaxZoomPreference(), maxZoomPreference); + expect(await controller.isNavigationUIEnabled(), true); }); } diff --git a/example/integration_test/session_test.dart b/example/integration_test/t02_session_test.dart similarity index 97% rename from example/integration_test/session_test.dart rename to example/integration_test/t02_session_test.dart index 2e810b1..470b48f 100644 --- a/example/integration_test/session_test.dart +++ b/example/integration_test/t02_session_test.dart @@ -25,7 +25,7 @@ import 'package:flutter/material.dart'; import 'shared.dart'; void main() { - patrolTest('Test terms and conditions (TOS) dialog acceptance', + patrol('Test terms and conditions (TOS) dialog acceptance', (PatrolIntegrationTester $) async { // Grant the location permission. await checkLocationDialogAcceptance($); @@ -79,7 +79,7 @@ void main() { expect(redundantAccept, true); }); - patrolTest('Test driver awareness disclaimer (noTOS) acknowledgement', + patrol('Test driver awareness disclaimer (noTOS) acknowledgement', (PatrolIntegrationTester $) async { // Grant location permissions if not granted. await checkLocationDialogAcceptance($); diff --git a/example/integration_test/t03_navigation_test.dart b/example/integration_test/t03_navigation_test.dart new file mode 100644 index 0000000..f33f1b3 --- /dev/null +++ b/example/integration_test/t03_navigation_test.dart @@ -0,0 +1,849 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +// This is a basic Flutter integration test. +// +// Since integration tests run in a full Flutter application, they can interact +// with the host side of a plugin implementation, unlike Dart unit tests. +// +// For more information about Flutter integration tests, please see +// https://docs.flutter.dev/cookbook/testing/integration/introduction + +import 'dart:async'; +import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_navigation/src/google_maps_navigation_platform_interface.dart'; +import 'package:google_maps_navigation/src/inspector/inspector_platform.dart'; +import 'shared.dart'; + +void main() { + GoogleMapsNavigationPlatform.instance.enableDebugInspection(); + + final GoogleNavigationInspectorPlatform inspector = + GoogleNavigationInspectorPlatform.instance!; + + /// Start location coordinates in Finland (Näkkäläntie). + const double startLat = startLocationLat; + const double startLon = startLocationLon; + + bool isPhysicalDevice = false; + final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + if (Platform.isIOS) { + deviceInfo.iosInfo.then((IosDeviceInfo info) { + isPhysicalDevice = info.isPhysicalDevice; + debugPrint('isPhysicalDevice: $isPhysicalDevice'); + }); + } + + patrol('Test navigating to a single destination', + (PatrolIntegrationTester $) async { + final Completer hasArrived = Completer(); + + /// Set up navigation. + final GoogleNavigationViewController viewController = + await startNavigationWithoutDestination($); + + /// Specify tolerance and navigation end coordinates. + const double tolerance = 0.0005; + const double endLat = 68.59451829688189, endLon = 23.512277951523007; + + /// Finish executing the tests once onArrival event comes in + /// and test that the guidance stops. + Future onArrivalEvent(OnArrivalEvent msg) async { + hasArrived.complete(); + await GoogleMapsNavigator.stopGuidance(); + } + + GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); + + /// Simulate location and test it. + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: startLat, + longitude: startLon, + )); + await $.pumpAndSettle(); + final LatLng? currentLocation = await viewController.getMyLocation(); + expect(currentLocation?.latitude, closeTo(startLat, tolerance)); + expect(currentLocation?.longitude, closeTo(startLon, tolerance)); + + /// Set Destination. + final Destinations destinations = Destinations( + waypoints: [ + NavigationWaypoint.withLatLngTarget( + title: 'Näkkäläntie', + target: const LatLng( + latitude: endLat, + longitude: endLon, + ), + ), + ], + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + ); + final NavigationRouteStatus status = + await GoogleMapsNavigator.setDestinations(destinations); + expect(status, NavigationRouteStatus.statusOk); + await $.pumpAndSettle(); + + expect(await GoogleMapsNavigator.isGuidanceRunning(), false); + + /// Start guidance. + await GoogleMapsNavigator.startGuidance(); + await $.pumpAndSettle(); + + /// Test that the received coordinates fit between start and end location coordinates within tolerance. + void onLocationEvent(RoadSnappedLocationUpdatedEvent msg) { + debugPrint( + 'LatLngSingle: ${msg.location.latitude}, ${msg.location.longitude}'); + expectSync( + msg.location.latitude, + greaterThanOrEqualTo(startLat - tolerance), + ); + expectSync( + msg.location.latitude, + lessThanOrEqualTo(endLat + tolerance), + ); + expectSync( + msg.location.longitude, + greaterThanOrEqualTo(startLon - tolerance), + ); + expectSync( + msg.location.longitude, + lessThanOrEqualTo(endLon + tolerance), + ); + } + + await GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener( + onLocationEvent); + + /// Start simulation. + await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRoute(); + + expect(await GoogleMapsNavigator.isGuidanceRunning(), true); + await hasArrived.future; + expect(await GoogleMapsNavigator.isGuidanceRunning(), false); + + await GoogleMapsNavigator.cleanup(); + }); + + patrol('Test navigating to multiple destinations', + (PatrolIntegrationTester $) async { + final Completer navigationFinished = Completer(); + int arrivalEventCount = 0; + + /// Set up navigation. + final GoogleNavigationViewController viewController = + await startNavigationWithoutDestination($); + + /// Specify tolerance and navigation destination coordinates. + const double tolerance = 0.0005; + const double midLat = 68.59781164189049, + midLon = 23.520303427087182, + endLat = 68.60079240808535, + endLon = 23.527946512754752; + + Future onArrivalEvent(OnArrivalEvent msg) async { + arrivalEventCount += 1; + await GoogleMapsNavigator.continueToNextDestination(); + + /// Finish executing the tests once 2 onArrival events come in. + /// Test the guidance stops on last Arrival. + if (arrivalEventCount == 2) { + navigationFinished.complete(); + } + } + + GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); + + /// Simulate location and test it. + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: startLat, + longitude: startLon, + )); + await $.pumpAndSettle(timeout: const Duration(seconds: 1)); + final LatLng? currentLocation = await viewController.getMyLocation(); + expect(currentLocation!.latitude, closeTo(startLat, tolerance)); + expect(currentLocation.longitude, closeTo(startLon, tolerance)); + + /// Set Destination. + final Destinations destinations = Destinations( + waypoints: [ + NavigationWaypoint.withLatLngTarget( + title: 'Näkkäläntie 1st stop', + target: const LatLng( + latitude: midLat, + longitude: midLon, + ), + ), + NavigationWaypoint.withLatLngTarget( + title: 'Näkkäläntie 2nd stop', + target: const LatLng( + latitude: endLat, + longitude: endLon, + ), + ), + ], + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + ); + final NavigationRouteStatus status = + await GoogleMapsNavigator.setDestinations(destinations); + expect(status, NavigationRouteStatus.statusOk); + await $.pumpAndSettle(); + + expect(await GoogleMapsNavigator.isGuidanceRunning(), false); + + /// Start guidance. + await GoogleMapsNavigator.startGuidance(); + await $.pumpAndSettle(); + + /// Test that the received coordinates fit between start and end location coordinates within tolerance. + void onLocationEvent(RoadSnappedLocationUpdatedEvent msg) { + /// Sometimes on Android, the simulator "overshoots" and passes the destination + /// with high speedMultiplier. + if (arrivalEventCount < 2) { + expectSync( + msg.location.latitude, + greaterThanOrEqualTo(startLat - tolerance), + ); + expectSync( + msg.location.latitude, + lessThanOrEqualTo(endLat + tolerance), + ); + expectSync( + msg.location.longitude, + greaterThanOrEqualTo(startLon - tolerance), + ); + expectSync( + msg.location.longitude, + lessThanOrEqualTo(endLon + tolerance), + ); + } + } + + await GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener( + onLocationEvent); + + /// Start simulation. + await GoogleMapsNavigator.simulator + .simulateLocationsAlongExistingRouteWithOptions(SimulationOptions( + speedMultiplier: 10, + )); + + expect(await GoogleMapsNavigator.isGuidanceRunning(), true); + await navigationFinished.future; + expect(await GoogleMapsNavigator.isGuidanceRunning(), false); + + await GoogleMapsNavigator.cleanup(); + }); + + patrol('Test simulation along new route', (PatrolIntegrationTester $) async { + int loopIteration = 1; + + /// Set up navigation. + await startNavigationWithoutDestination($); + + /// Specify tolerance and navigation end coordinates. + const double tolerance = 0.0005; + const double endLat = 68.59451829688189, endLon = 23.512277951523007; + + /// Create a waypoint. + final List waypoint = [ + NavigationWaypoint.withLatLngTarget( + title: 'Näkkäläntie', + target: const LatLng( + latitude: endLat, + longitude: endLon, + ), + ), + ]; + + /// Create a simulator1 wrapper function for simulating locations along new route + /// with routing options. + Future simulator1() { + return GoogleMapsNavigator.simulator + .simulateLocationsAlongNewRoute(waypoint); + } + + /// Create a simulator2 wrapper function for simulating locations along new route + /// with routing options. + Future simulator2() { + return GoogleMapsNavigator.simulator + .simulateLocationsAlongNewRouteWithRoutingOptions( + waypoint, + RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.one, + routingStrategy: NavigationRoutingStrategy.shorter, + targetDistanceMeters: [100], + travelMode: NavigationTravelMode.driving, + avoidTolls: true, + avoidFerries: true, + avoidHighways: true, + locationTimeoutMs: 5000, + ), + ); + } + + /// Create a simulator3 wrapper function for simulating locations along new route + /// with routing options. + Future simulator3() { + return GoogleMapsNavigator.simulator + .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( + waypoint, + RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.none, + routingStrategy: NavigationRoutingStrategy.shorter, + targetDistanceMeters: [100], + travelMode: NavigationTravelMode.walking, + avoidTolls: false, + avoidFerries: false, + avoidHighways: false, + locationTimeoutMs: 5000, + ), + SimulationOptions(speedMultiplier: 20), + ); + } + + /// Create a simulator4 wrapper function for simulating locations along new route + /// with updated routing and simulation options. + Future simulator4() { + return GoogleMapsNavigator.simulator + .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( + waypoint, + RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.all, + routingStrategy: NavigationRoutingStrategy.defaultBest, + targetDistanceMeters: [100], + travelMode: NavigationTravelMode.driving, + avoidTolls: true, + avoidFerries: true, + avoidHighways: true, + locationTimeoutMs: 2500, + ), + SimulationOptions(speedMultiplier: 10), + ); + } + + final List Function()> simulatorTypes = + Function()>[ + simulator1, + simulator2, + simulator3, + simulator4 + ]; + + /// Test that the different simulator types work. + for (final Future Function() simulatorType + in simulatorTypes) { + bool hasArrived = false; + final Completer finishTest = Completer(); + debugPrint('Starting loop with simulator$loopIteration.'); + loopIteration += 1; + + /// Initialize navigation if iOS. + /// On iOS .cleanup() destroys the initialization. + if (Platform.isIOS) { + await GoogleMapsNavigator.initializeNavigationSession(); + await $.pumpAndSettle(); + } + + /// Simulate location. + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: startLat, + longitude: startLon, + )); + debugPrint('Starting location set.'); + await $.pumpAndSettle(); + + /// Test that the received coordinates fit between start and end location coordinates within tolerance. + /// End the test when user arrives to the end location coordinates within tolerance. + void onLocationEvent(RoadSnappedLocationUpdatedEvent msg) { + debugPrint( + 'LatLngSimulator: ${msg.location.latitude}, ${msg.location.longitude}.'); + if ((!hasArrived) && + (endLat - msg.location.latitude <= tolerance) && + (endLon - msg.location.longitude <= tolerance)) { + hasArrived = true; + finishTest.complete(); + } else { + expectSync( + msg.location.latitude, + greaterThanOrEqualTo(startLat - tolerance), + ); + expectSync( + msg.location.latitude, + lessThanOrEqualTo(endLat + tolerance), + ); + expectSync( + msg.location.longitude, + greaterThanOrEqualTo(startLon - tolerance), + ); + expectSync( + msg.location.longitude, + lessThanOrEqualTo(endLon + tolerance), + ); + } + } + + final StreamSubscription subscription = + await GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener( + onLocationEvent); + debugPrint('Listener initialized.'); + + /// Start simulation and wait for the arrival. + final NavigationRouteStatus status = await simulatorType(); + expect(status, NavigationRouteStatus.statusOk); + debugPrint('Simulation along the route started.'); + await finishTest.future; + debugPrint('Loop with simulator$loopIteration finished.'); + + await GoogleMapsNavigator.cleanup(); + await subscription.cancel(); + await $.pumpAndSettle(); + } + }); + + patrol('Test simulating the location', (PatrolIntegrationTester $) async { + /// Set up navigation. + final GoogleNavigationViewController viewController = + await startNavigationWithoutDestination($); + + /// Specify tolerance and navigation end coordinates. + const double tolerance = 0.0005; + const double endLat = 68.60338455021943, endLon = 23.548804200724454; + + /// Simulate location. + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: startLat, + longitude: startLon, + )); + await $.pumpAndSettle(); + + /// Test the location simulation. + LatLng? currentLocation = await viewController.getMyLocation(); + expect(currentLocation!.latitude, closeTo(startLat, tolerance)); + expect(currentLocation.longitude, closeTo(startLon, tolerance)); + + /// Test the simulated location was removed when using Android. + /// iOS Xcode simulators location is flaky making the test fail sometimes + /// so the test is skipped on iOS. + if (Platform.isAndroid) { + await GoogleMapsNavigator.simulator.removeUserLocation(); + await $.pumpAndSettle(duration: const Duration(seconds: 5)); + + currentLocation = await viewController.getMyLocation(); + expect(currentLocation!.latitude, isNot(closeTo(startLat, tolerance))); + expect(currentLocation.longitude, isNot(closeTo(startLon, tolerance))); + } + + /// Simulate location. + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: startLat, + longitude: startLon, + )); + await $.pumpAndSettle(duration: const Duration(seconds: 1)); + + /// Set Destination. + final Destinations destinations = Destinations( + waypoints: [ + NavigationWaypoint.withLatLngTarget( + title: 'Näkkäläntie', + target: const LatLng( + latitude: endLat, + longitude: endLon, + ), + ), + ], + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + ); + final NavigationRouteStatus status = + await GoogleMapsNavigator.setDestinations(destinations); + expect(status, NavigationRouteStatus.statusOk); + await $.pumpAndSettle(); + + /// Start guidance and simulation along the route. + await GoogleMapsNavigator.startGuidance(); + await GoogleMapsNavigator.simulator + .simulateLocationsAlongExistingRouteWithOptions( + SimulationOptions(speedMultiplier: 5)); + + /// Test pausing simulation. + await GoogleMapsNavigator.simulator.pauseSimulation(); + final LatLng? location1 = await viewController.getMyLocation(); + await $.pumpAndSettle(duration: const Duration(seconds: 1)); + final LatLng? location2 = await viewController.getMyLocation(); + expect(location1!.latitude, closeTo(location2!.latitude, tolerance)); + expect(location1.longitude, closeTo(location2.longitude, tolerance)); + + /// Test resuming the simulation. + await GoogleMapsNavigator.simulator.resumeSimulation(); + await $.pumpAndSettle(duration: const Duration(seconds: 2)); + final LatLng? location3 = await viewController.getMyLocation(); + expect(location1.latitude, isNot(closeTo(location3!.latitude, tolerance))); + expect(location1.longitude, isNot(closeTo(location3.longitude, tolerance))); + }); + + patrol('Test that the navigation and updates stop onArrival', + (PatrolIntegrationTester $) async { + /// Set up navigation. + await startNavigationWithoutDestination($); + + /// Simulate location (1298 California St) + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: 37.79136614772824, + longitude: -122.41565900473043, + )); + + /// Set Destination. + final Destinations destinations = Destinations( + waypoints: [ + NavigationWaypoint.withLatLngTarget( + title: 'Grace Cathedral', + target: const LatLng( + latitude: 37.791957, + longitude: -122.412529, + ), + ), + ], + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + ); + final NavigationRouteStatus status = + await GoogleMapsNavigator.setDestinations(destinations); + expect(status, NavigationRouteStatus.statusOk); + await $.pumpAndSettle(); + + /// Start guidance. + await GoogleMapsNavigator.startGuidance(); + await $.pumpAndSettle(); + + /// Start simulation. + await GoogleMapsNavigator.simulator + .simulateLocationsAlongExistingRouteWithOptions(SimulationOptions( + speedMultiplier: 100, + )); + await $.pumpAndSettle(); + + /// Test that guidance ends after onArrival. + void onArrivalEvent(OnArrivalEvent msg) { + expect(GoogleMapsNavigator.isGuidanceRunning(), false); + } + + GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); + }); + + // Skip test on iOS simulator. + if (Platform.isIOS && !isPhysicalDevice) { + } else { + patrol('Test network error during navigation', + (PatrolIntegrationTester $) async { + /// Set up navigation. + await startNavigationWithoutDestination($); + + /// Simulate location (1298 California St) + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: 37.79136614772824, + longitude: -122.41565900473043, + )); + + /// Create a waypoint. + final List waypoint = [ + NavigationWaypoint.withLatLngTarget( + title: 'Grace Cathedral', + target: const LatLng( + latitude: 37.791957, + longitude: -122.412529, + ), + ), + ]; + + /// Set Destination. + final Destinations destinations = Destinations( + waypoints: waypoint, + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + ); + + /// Cut network connection. + await $.native.disableCellular(); + await $.native.disableWifi(); + + /// Test that the error is received. + final NavigationRouteStatus routeStatus = + await GoogleMapsNavigator.setDestinations(destinations); + expect(routeStatus, equals(NavigationRouteStatus.networkError)); + + final NavigationRouteStatus routeStatusSim = await GoogleMapsNavigator + .simulator + .simulateLocationsAlongNewRoute(waypoint); + expect(routeStatusSim, equals(NavigationRouteStatus.networkError)); + + /// Re-enable network connection. + await $.native.enableCellular(); + await $.native.enableWifi(); + }); + } + + patrol('Test route not found errors', (PatrolIntegrationTester $) async { + /// Set up navigation. + await startNavigationWithoutDestination($); + + /// Simulate location (1298 California St) + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: 37.79136614772824, + longitude: -122.41565900473043, + )); + + /// Create a waypoint. + final List waypoint = [ + NavigationWaypoint.withLatLngTarget( + title: 'Kiviniemi', + target: const LatLng( + latitude: 70.06006451782844, + longitude: 27.390062785112185, + ), + ), + ]; + + /// Set Destination to another continent. + final Destinations destinations = Destinations( + waypoints: waypoint, + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + ); + + /// Test that the error is received. + final NavigationRouteStatus routeStatus = + await GoogleMapsNavigator.setDestinations(destinations); + expect(routeStatus, equals(NavigationRouteStatus.routeNotFound)); + + final NavigationRouteStatus routeStatusSim = await GoogleMapsNavigator + .simulator + .simulateLocationsAlongNewRoute(waypoint); + expect(routeStatusSim, equals(NavigationRouteStatus.routeNotFound)); + }); + + patrol('Test route structures', (PatrolIntegrationTester $) async { + final Completer hasArrived = Completer(); + + /// Set up navigation. + await startNavigationWithoutDestination($); + + /// Finish executing the tests once onArrival event comes in. + void onArrivalEvent(OnArrivalEvent msg) { + hasArrived.complete(); + } + + GoogleMapsNavigator.setOnArrivalListener(onArrivalEvent); + + /// Simulate location (1298 California St) + await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( + latitude: 37.79136614772824, + longitude: -122.41565900473043, + )); + + /// Set Destination. + final Destinations destinations = Destinations( + waypoints: [ + NavigationWaypoint.withLatLngTarget( + title: 'Grace Cathedral', + target: const LatLng( + latitude: 37.791957, + longitude: -122.412529, + ), + ), + ], + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + ); + final NavigationRouteStatus status = + await GoogleMapsNavigator.setDestinations(destinations); + expect(status, NavigationRouteStatus.statusOk); + await $.pumpAndSettle(); + + /// Start guidance. + await GoogleMapsNavigator.startGuidance(); + await $.pumpAndSettle(); + + final List beginRouteSegments = + await GoogleMapsNavigator.getRouteSegments(); + final RouteSegment? beginCurrentSegment = + await GoogleMapsNavigator.getCurrentRouteSegment(); + final List beginTraveledRoute = + await GoogleMapsNavigator.getTraveledRoute(); + + /// The route segments list is not empty. + expect(beginRouteSegments.length, greaterThan(0)); + + /// The current route segment. + expect(beginCurrentSegment, isNotNull); + + /// Start simulation. + await GoogleMapsNavigator.simulator + .simulateLocationsAlongExistingRouteWithOptions(SimulationOptions( + speedMultiplier: 30, + )); + await $.pumpAndSettle(); + + await hasArrived.future; + + // Values after arrival + final List endTraveledRoute = + await GoogleMapsNavigator.getTraveledRoute(); + final RouteSegment? endSegment = + await GoogleMapsNavigator.getCurrentRouteSegment(); + + /// Traveled route is different than in the beginning + expect(endTraveledRoute.length, greaterThan(beginTraveledRoute.length)); + + /// Check that the last segment is near target destination. + expect(endSegment!.destinationLatLng.longitude, closeTo(-122.412, 0.002)); + }); + + patrol('Test that the navigation session is attached to existing map', + (PatrolIntegrationTester $) async { + bool isSessionAttached; + + /// Set up navigation without initialization. + final GoogleNavigationViewController viewController = + await startNavigationWithoutDestination($, initializeNavigation: false); + + final int viewId = viewController.getViewId(); + if (Platform.isIOS) { + isSessionAttached = await inspector.isViewAttachedToSession(viewId); + expect(isSessionAttached, false); + } + + /// Initialize navigation. + await GoogleMapsNavigator.initializeNavigationSession(); + await $.pumpAndSettle(); + isSessionAttached = await inspector.isViewAttachedToSession(viewId); + expect(isSessionAttached, true); + }); + + patrol('Test that the map attaches existing navigation session to itself', + (PatrolIntegrationTester $) async { + /// Set up navigation. + final GoogleNavigationViewController viewController = + await startNavigationWithoutDestination($); + + final int viewId = viewController.getViewId(); + final bool isSessionAttached = await GoogleNavigationInspectorPlatform + .instance! + .isViewAttachedToSession(viewId); + expect(isSessionAttached, true); + }); + + patrol('Test routing options and display options', + (PatrolIntegrationTester $) async { + /// Set up navigation. + await startNavigationWithoutDestination($, simulateLocation: true); + + /// Specify navigation end coordinates. + const double endX = 68.60079240808535, endY = 23.527946512754752; + + /// Set Destination, routing options and display options. + final Destinations destinations = Destinations( + waypoints: [ + NavigationWaypoint.withLatLngTarget( + title: 'Näkkäläntie', + target: const LatLng( + latitude: endX, + longitude: endY, + ), + ), + ], + routingOptions: RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.none, + routingStrategy: NavigationRoutingStrategy.shorter, + targetDistanceMeters: [1000], + travelMode: NavigationTravelMode.walking, + avoidTolls: true, + avoidFerries: true, + avoidHighways: true, + locationTimeoutMs: 2500, + ), + displayOptions: NavigationDisplayOptions( + showDestinationMarkers: false, + showStopSigns: false, + showTrafficLights: false, + ), + ); + NavigationRouteStatus status = + await GoogleMapsNavigator.setDestinations(destinations); + expect(status, NavigationRouteStatus.statusOk); + + /// Start guidance. + await GoogleMapsNavigator.startGuidance(); + final bool guidanceRunning = await GoogleMapsNavigator.isGuidanceRunning(); + expect(guidanceRunning, true); + await $.pumpAndSettle(); + + /// Test the time and Distace for walking travelmode. + NavigationTimeAndDistance timeAndDistance = + await GoogleMapsNavigator.getCurrentTimeAndDistance(); + expect(timeAndDistance.time, closeTo(1000, 600)); + expect(timeAndDistance.distance, closeTo(1048, 200)); + + /// Test clearing the destination. + await GoogleMapsNavigator.clearDestinations(); + List getRoute = await GoogleMapsNavigator.getRouteSegments(); + expect(getRoute, isEmpty); + + /// Create destinations2 with updated routing and display options. + final Destinations destinations2 = Destinations( + waypoints: destinations.waypoints, + routingOptions: RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.one, + routingStrategy: NavigationRoutingStrategy.deltaToTargetDistance, + targetDistanceMeters: [1050], + travelMode: NavigationTravelMode.driving, + avoidTolls: false, + avoidFerries: false, + avoidHighways: false, + locationTimeoutMs: 5000, + ), + displayOptions: NavigationDisplayOptions( + showDestinationMarkers: true, + showStopSigns: true, + showTrafficLights: true, + ), + ); + status = await GoogleMapsNavigator.setDestinations(destinations2); + expect(status, NavigationRouteStatus.statusOk); + + /// Test the time and Distace for driving travelmode. + timeAndDistance = await GoogleMapsNavigator.getCurrentTimeAndDistance(); + expect(timeAndDistance.time, closeTo(80, 150)); + expect(timeAndDistance.distance, closeTo(1048, 200)); + + /// Test clearing the destination. + await GoogleMapsNavigator.clearDestinations(); + getRoute = await GoogleMapsNavigator.getRouteSegments(); + expect(getRoute, isEmpty); + + /// Create destinations3 with updated routing options. + final Destinations destinations3 = Destinations( + waypoints: destinations.waypoints, + routingOptions: RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.all, + routingStrategy: NavigationRoutingStrategy.defaultBest, + travelMode: NavigationTravelMode.cycling, + ), + displayOptions: destinations.displayOptions, + ); + status = await GoogleMapsNavigator.setDestinations(destinations3); + expect(status, NavigationRouteStatus.statusOk); + + /// Test the time and distace for cycling travelmode. + timeAndDistance = await GoogleMapsNavigator.getCurrentTimeAndDistance(); + expect(timeAndDistance.time, closeTo(180, 100)); + expect(timeAndDistance.distance, closeTo(1048, 200)); + }); +} diff --git a/example/integration_test/navigation_ui_test.dart b/example/integration_test/t04_navigation_ui_test.dart similarity index 76% rename from example/integration_test/navigation_ui_test.dart rename to example/integration_test/t04_navigation_ui_test.dart index 9d838bd..83d4f0b 100644 --- a/example/integration_test/navigation_ui_test.dart +++ b/example/integration_test/t04_navigation_ui_test.dart @@ -21,14 +21,19 @@ // https://docs.flutter.dev/cookbook/testing/integration/introduction import 'dart:async'; + import 'package:flutter/material.dart'; + import 'shared.dart'; void main() { - patrolTest('Test enabling navigation UI', (PatrolIntegrationTester $) async { + patrol('Test enabling navigation UI', (PatrolIntegrationTester $) async { final Completer viewControllerCompleter = Completer(); + /// For testing NavigationUIEnabledChanged + bool navigationUIisEnabled = false; + await checkLocationDialogAndTosAcceptance($); /// Display navigation view. @@ -38,17 +43,27 @@ void main() { GoogleMapsNavigationView( key: key, onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); + controller.setMyLocationEnabled(true); viewControllerCompleter.complete(controller); }, + onNavigationUIEnabledChanged: (bool isEnabled) { + navigationUIisEnabled = isEnabled; + }, ), ); final GoogleNavigationViewController viewController = await viewControllerCompleter.future; + expect(await viewController.isNavigationUIEnabled(), false); + expect(navigationUIisEnabled, false); + /// Initialize navigation. await GoogleMapsNavigator.initializeNavigationSession(); + + expect(await viewController.isNavigationUIEnabled(), true); + expect(navigationUIisEnabled, true); + await $.pumpAndSettle(); /// Simulate location (1298 California St) @@ -80,20 +95,23 @@ void main() { /// Start simulation. await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRoute(); - final List results = [true, false, true]; + final List results = [false, true, false]; /// Test enabling and disabling the navigation UI. for (final bool result in results) { - await viewController.enableNavigationUI(enabled: result); + await viewController.setNavigationUIEnabled(result); await $.pumpAndSettle(); expect(await viewController.isNavigationUIEnabled(), result); final bool isEnabled = await viewController.isNavigationUIEnabled(); expect(isEnabled, result); + + /// Test that NavigationUIEnabledChanged event works. + expect(navigationUIisEnabled, result); } /// Test enabling and disabling the header. for (final bool result in results) { - await viewController.enableNavigationHeader(enabled: result); + await viewController.setNavigationHeaderEnabled(result); await $.pumpAndSettle(); final bool isEnabled = await viewController.isNavigationHeaderEnabled(); expect(isEnabled, result); @@ -101,7 +119,7 @@ void main() { /// Test enabling and disabling the footer. for (final bool result in results) { - await viewController.enableNavigationFooter(enabled: result); + await viewController.setNavigationFooterEnabled(result); await $.pumpAndSettle(); final bool isEnabled = await viewController.isNavigationFooterEnabled(); expect(isEnabled, result); @@ -109,7 +127,7 @@ void main() { /// Test enabling and disabling the trip progress bar. for (final bool result in results) { - await viewController.enableNavigationTripProgressBar(enabled: result); + await viewController.setNavigationTripProgressBarEnabled(result); await $.pumpAndSettle(); final bool isEnabled = await viewController.isNavigationTripProgressBarEnabled(); @@ -118,7 +136,7 @@ void main() { /// Test enabling and disabling the speedometer. for (final bool result in results) { - await viewController.enableSpeedometer(enable: result); + await viewController.setSpeedometerEnabled(result); await $.pumpAndSettle(); final bool isEnabled = await viewController.isSpeedometerEnabled(); expect(isEnabled, result); @@ -126,23 +144,24 @@ void main() { /// Test enabling and disabling the speed limit. for (final bool result in results) { - await viewController.enableSpeedLimitIcon(enable: result); + await viewController.setSpeedLimitIconEnabled(result); await $.pumpAndSettle(); final bool isEnabled = await viewController.isSpeedLimitIconEnabled(); expect(isEnabled, result); } - /// Test enabling and disabling the incident cards. + /// Test enabling and disabling the traffic incident cards. for (final bool result in results) { - await viewController.enableIncidentCards(enable: result); + await viewController.setTrafficIncidentCardsEnabled(result); await $.pumpAndSettle(); - final bool isEnabled = await viewController.isIncidentCardsEnabled(); + final bool isEnabled = + await viewController.isTrafficIncidentCardsEnabled(); expect(isEnabled, result); } /// Test enabling and disabling the recenter button. for (final bool result in results) { - await viewController.enableRecenterButton(enabled: result); + await viewController.setRecenterButtonEnabled(result); final bool isEnabled = await viewController.isRecenterButtonEnabled(); expect(isEnabled, result); } diff --git a/example/integration_test/t05_camera_test.dart b/example/integration_test/t05_camera_test.dart new file mode 100644 index 0000000..e35f85e --- /dev/null +++ b/example/integration_test/t05_camera_test.dart @@ -0,0 +1,750 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +// This is a basic Flutter integration test. +// +// Since integration tests run in a full Flutter application, they can interact +// with the host side of a plugin implementation, unlike Dart unit tests. +// +// For more information about Flutter integration tests, please see +// https://docs.flutter.dev/cookbook/testing/integration/introduction + +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'shared.dart'; + +void main() { + CameraPosition expectedPosition = const CameraPosition(); + Completer cameraMoveStartedCompleter = Completer(); + Completer cameraMoveCompleter = Completer(); + Completer cameraIdleCompleter = Completer(); + late CameraPosition cameraMoveStartedPosition; + late CameraPosition cameraMovePosition; + late CameraPosition cameraIdlePosition; + bool? followingMyLocationActive; + CameraPosition? startedFollowingMyLocationPosition; + CameraPosition? stoppedFollowingMyLocationPosition; + + /// Define the camera event callback functions. + void onCameraMoveStarted(CameraPosition position, bool gesture) { + if (!cameraMoveStartedCompleter.isCompleted) { + debugPrint('cameraMoveStarted event'); + cameraMoveStartedPosition = position; + cameraMoveStartedCompleter.complete(); + } + } + + void onCameraMove(CameraPosition position) { + if (!cameraMoveCompleter.isCompleted) { + debugPrint('cameraMoving event'); + cameraMovePosition = position; + cameraMoveCompleter.complete(); + } + } + + void onCameraIdle(CameraPosition position) { + debugPrint('cameraIdle event'); + if (!cameraIdleCompleter.isCompleted) { + cameraIdlePosition = position; + cameraIdleCompleter.complete(); + } + } + + void onCameraStartedFollowingLocation(CameraPosition position) { + debugPrint('startedFollowingMyLocation event'); + startedFollowingMyLocationPosition = position; + followingMyLocationActive = true; + } + + void onCameraStoppedFollowingLocation(CameraPosition position) { + debugPrint('stoppedFollowingMyLocation event'); + stoppedFollowingMyLocationPosition = position; + followingMyLocationActive = false; + } + + /// Reset the camera event completers. + void resetCameraEventCompleters() { + cameraMoveStartedCompleter = Completer(); + cameraMoveCompleter = Completer(); + cameraIdleCompleter = Completer(); + } + + double distanceToNorth(double angle) { + final double diff = (angle + 180) % 360 - 180; + return (diff < -180 ? diff + 360 : diff).abs(); + } + + /// Define the predicate functions for waitForCameraPositionMatchingPredicate(). + /// + /// Check the camera zoom values match each other within tolerance. + bool checkZoomMatch(CameraPosition receivedPosition) { + return (receivedPosition.zoom - expectedPosition.zoom).abs() <= 0.01 && + (receivedPosition.zoom - expectedPosition.zoom).abs() <= 0.01; + } + + /// Check the received camera tilt value is less than or equal to the expected tilt value. + bool checkTiltLessThanOrEqualTo(CameraPosition receivedPosition) { + return receivedPosition.tilt <= expectedPosition.tilt; + } + + /// Check the received camera tilt value is greater than or equal to the expected tilt value. + bool checkTiltGreaterThanOrEqualTo(CameraPosition receivedPosition) { + return receivedPosition.tilt >= expectedPosition.tilt; + } + + /// Check the received camera bearing value is less than or equal to the expected bearing value. + bool checkBearingLessThanOrEqualTo(CameraPosition receivedPosition) { + return distanceToNorth(receivedPosition.bearing) <= + expectedPosition.bearing; + } + + /// Check that the received camera target value doesn't match the expected camera target value. + bool checkCoordinatesDiffer(CameraPosition receivedPosition) { + return receivedPosition.target != expectedPosition.target; + } + + // Check that the received camera target value matches the expected camera target value. + bool checkCoordinatesMatch(CameraPosition receivedPosition) { + return (receivedPosition.target.latitude - expectedPosition.target.latitude) + .abs() <= + 0.02 && + (receivedPosition.target.longitude - expectedPosition.target.longitude) + .abs() <= + 0.02; + } + + /// Wait for cameraMoveStarted, cameraMove and cameraIdle events. + Future waitForCameraEvents(PatrolIntegrationTester $) async { + await cameraMoveStartedCompleter.future; + await cameraMoveCompleter.future; + await cameraIdleCompleter.future; + // Check the event positions are not empty. + expect(cameraMoveStartedPosition, isNotNull); + expect(cameraMovePosition, isNotNull); + expect(cameraIdlePosition, isNotNull); + } + + /// Check the camera coordinates match each other within tolerance. + void checkCameraCoordinatesMatch( + CameraPosition received, CameraPosition expected) { + expect(received.target.latitude, closeTo(expected.target.latitude, 0.02)); + expect(received.target.longitude, closeTo(expected.target.longitude, 0.02)); + } + + // onTimeout fail function for futures. + void onTimeout() { + fail('Future timed out'); + } + + patrol('Test camera modes', (PatrolIntegrationTester $) async { + // Test that followMyLocation is not active and no stop or start events have come in. + if (Platform.isAndroid) { + expect(followingMyLocationActive, isNull); + expect(startedFollowingMyLocationPosition, isNull); + expect(stoppedFollowingMyLocationPosition, isNull); + } + + /// Initialize navigation with the event listener functions. + final GoogleNavigationViewController controller = await startNavigation( + $, + onCameraMoveStarted: onCameraMoveStarted, + onCameraMove: onCameraMove, + onCameraIdle: onCameraIdle, + onCameraStartedFollowingLocation: onCameraStartedFollowingLocation, + onCameraStoppedFollowingLocation: onCameraStoppedFollowingLocation, + ); + + // Test that the followMyLocation is active and onCameraStartedFollowingLocation event + // has been received. + if (Platform.isAndroid) { + expect(followingMyLocationActive, true); + expect(startedFollowingMyLocationPosition, isNotNull); + } + + /// Define the getPosition function for waitForCameraPositionMatchingPredicate(). + Future getPosition() async { + final CameraPosition position = await controller.getCameraPosition(); + return position; + } + + // Verify that the follow my location camera mode is active. + Future checkCameraFollowsLocation() async { + final LatLng? currentLocation = await controller.getMyLocation(); + expectedPosition = CameraPosition(target: currentLocation!); + // Wait until camera target matches my location target, fail if + // it doesn't happen within the max retries (≈20s). + final CameraPosition? followMyLocationCheck = + await waitForValueMatchingPredicate( + $, getPosition, checkCoordinatesMatch); + expect(followMyLocationCheck, isNotNull); + + // Check that Android camera started following location events have come in. + if (Platform.isAndroid) { + expect(followingMyLocationActive, true); + expect(startedFollowingMyLocationPosition, isNotNull); + } + } + + await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRoute(); + + // 1. Test default. + // + // The navigation follows the user's location in tilted mode. + + // Check the camera tilt value is over 40. + expectedPosition = const CameraPosition(tilt: 40); + + // Wait until the camera tilt is greater than the expected tilt. + CameraPosition? camera = await waitForValueMatchingPredicate( + $, + getPosition, + checkTiltGreaterThanOrEqualTo, + ); + expect(camera, isNotNull); + + debugPrint('Default tilt: ${camera!.tilt}'); + debugPrint('Default zoom: ${camera.zoom}'); + debugPrint('Default bearing: ${camera.bearing}'); + + // Verify that the follow my location camera mode is active. + await checkCameraFollowsLocation(); + + // Strong tilting (Android 45, iOS 55 degrees) + expect(camera.tilt, greaterThanOrEqualTo(40)); + + LatLng oldTarget = camera.target; + double oldZoom = camera.zoom; + + // 2. Test CameraPerspective.topDownHeadingUp. + expectedPosition = const CameraPosition(tilt: 0.1); + await controller.followMyLocation(CameraPerspective.topDownHeadingUp); + + // Wait until the camera tilt is less than or equal the expected tilt. + camera = await waitForValueMatchingPredicate( + $, getPosition, checkTiltLessThanOrEqualTo); + expect(camera, isNotNull); + + debugPrint('topDownHeadingUp tilt: ${camera!.tilt}'); + debugPrint('topDownHeadingUp zoom: ${camera.zoom}'); + debugPrint('topDownHeadingUp bearing: ${camera.bearing}'); + + // Verify that the follow my location camera mode is active. + await checkCameraFollowsLocation(); + + // No tilt when top-down. + expect(camera.tilt, lessThanOrEqualTo(0.1)); + + // Wait until camera target has moved (follows users location). + CameraPosition? cameraHasMoved = await waitForValueMatchingPredicate( + $, + getPosition, + checkCoordinatesDiffer, + ); + expect(cameraHasMoved, isNotNull); + + oldTarget = camera.target; + + // 3. Test CameraPerspective.topDownNorthUp + expectedPosition = const CameraPosition(bearing: 1.0, tilt: 0.1); + await controller.followMyLocation(CameraPerspective.topDownNorthUp); + + // Wait until the bearing & tilt are less than or equal to the expected bearing & tilt. + // On iOS the random small tilt change caused occasional test failures. + await waitForValueMatchingPredicate( + $, getPosition, checkTiltLessThanOrEqualTo); + camera = await waitForValueMatchingPredicate( + $, getPosition, checkBearingLessThanOrEqualTo); + expect(camera, isNotNull); + + debugPrint('topDownNorthUp tilt: ${camera!.tilt}'); + debugPrint('topDownNorthUp zoom: ${camera.zoom}'); + debugPrint('topDownNorthUp bearing: ${camera.bearing}'); + + // Verify that the follow my location camera mode is active. + await checkCameraFollowsLocation(); + + // No tilt when top-down. + expect(camera.tilt, lessThanOrEqualTo(0.1)); + + // North-up means zero bearing. + // iOS reports 0 degrees, Android is more fuzzy e.g. 359.7 degrees. + expect(distanceToNorth(camera.bearing), lessThanOrEqualTo(2.0)); + + // Wait until camera target has moved (follows users location). + cameraHasMoved = await waitForValueMatchingPredicate( + $, + getPosition, + checkCoordinatesDiffer, + ); + expect(cameraHasMoved, isNotNull); + + oldTarget = camera.target; + + // 4. Test CameraPerspective.tilted + expectedPosition = const CameraPosition(tilt: 40); + await controller.followMyLocation(CameraPerspective.tilted); + + // Wait until the tilt is greater than or equal to the expected tilt. + camera = await waitForValueMatchingPredicate( + $, getPosition, checkTiltGreaterThanOrEqualTo); + expect(camera, isNotNull); + + debugPrint('tilted tilt: ${camera!.tilt}'); + debugPrint('tilted zoom: ${camera.zoom}'); + debugPrint('tilted bearing: ${camera.bearing}'); + + // Verify that the follow my location camera mode is active. + await checkCameraFollowsLocation(); + + // Repeat tests done with the default state above + expect(camera.tilt, greaterThanOrEqualTo(40)); + expect(distanceToNorth(camera.bearing), greaterThanOrEqualTo(0.01)); + + // Wait until camera target has moved (follows users location). + cameraHasMoved = await waitForValueMatchingPredicate( + $, getPosition, checkCoordinatesDiffer); + expect(cameraHasMoved, isNotNull); + + oldTarget = camera.target; + oldZoom = camera.zoom; + + // 5. Test showRouteOverview(). + expectedPosition = const CameraPosition(tilt: 0.1); + await controller.showRouteOverview(); + + // Wait until the tilt is less than or equal to the expected tilt. + camera = await waitForValueMatchingPredicate( + $, getPosition, checkTiltLessThanOrEqualTo); + + // Test that stoppedFollowingMyLocation event has been received and + // followMyLocation is not active. + if (Platform.isAndroid) { + expect(followingMyLocationActive, false); + expect(stoppedFollowingMyLocationPosition, isNotNull); + } + + debugPrint('showRouteOverview tilt: ${camera!.tilt}'); + debugPrint('showRouteOverview zoom: ${camera.zoom}'); + debugPrint('showRouteOverview bearing: ${camera.bearing}'); + + // No tilt when in route overview. + expect(camera.tilt, lessThanOrEqualTo(0.1)); + + // Expect zoom to be farthest away. + expect(oldZoom, greaterThanOrEqualTo(camera.zoom)); + + // Route is shown north up. + expect(distanceToNorth(camera.bearing), lessThanOrEqualTo(1.0)); + + // Wait until camera target has moved (follows users location). + cameraHasMoved = await waitForValueMatchingPredicate( + $, getPosition, checkCoordinatesDiffer); + expect(cameraHasMoved, isNotNull); + + // 6. Test the optional follow my location parameter zoom level + // and the startedFollowingMyLocation event. + const double zoomLevel = 8.5; + expectedPosition = const CameraPosition(zoom: zoomLevel); + await controller.followMyLocation(CameraPerspective.tilted, + zoomLevel: zoomLevel); + + final CameraPosition oldCamera = await controller.getCameraPosition(); + + // Wait until the zoom roughly matches the provided zoom. + camera = + await waitForValueMatchingPredicate($, getPosition, checkZoomMatch); + expect(camera, isNotNull); + expect(camera!.zoom, closeTo(zoomLevel, 0.01)); + + // Test that startedFollowingMyLocation event has been received + // and the received position is close to the start position. + if (Platform.isAndroid) { + expect(followingMyLocationActive, true); + checkCameraCoordinatesMatch( + startedFollowingMyLocationPosition!, oldCamera); + } + + oldTarget = camera.target; + oldZoom = camera.zoom; + + // 7. Test stoppedFollowingMyLocation event. + resetCameraEventCompleters(); + + // Stop followMyLocation. + final CameraUpdate positionUpdate = CameraUpdate.newLatLng(LatLng( + latitude: oldTarget.latitude + 0.5, + longitude: oldTarget.longitude + 0.5)); + await controller.moveCamera(positionUpdate); + + // Wait for cameraIdleEvent before doing doing the tests. + await cameraIdleCompleter.future.timeout( + const Duration(seconds: 10), + onTimeout: onTimeout, + ); + camera = await controller.getCameraPosition(); + + // Test stoppedFollowingMyLocation event is received on Android. + if (Platform.isAndroid) { + expect(followingMyLocationActive, false); + expect(stoppedFollowingMyLocationPosition!.target.latitude, + closeTo(oldTarget.latitude, 0.003)); + expect(stoppedFollowingMyLocationPosition!.target.longitude, + closeTo(oldTarget.longitude, 0.003)); + } + + // 8. Test cameraMoveStarted, cameraMove and cameraIdle events. + await GoogleMapsNavigator.simulator.pauseSimulation(); + camera = await controller.getCameraPosition(); + resetCameraEventCompleters(); + + final LatLng newTarget = LatLng( + latitude: camera.target.latitude + 0.5, + longitude: camera.target.longitude + 0.5); + + // Define the target position. + expectedPosition = CameraPosition(target: newTarget); + final CameraUpdate cameraUpdate = CameraUpdate.newLatLng(newTarget); + + // Move camera and wait for cameraMoveStarted, cameraMove + // and cameraIdle events to come in. + await controller.moveCamera(cameraUpdate); + await waitForCameraEvents($); + + // Test that cameraMoveStartedEvent position coordinates are close to the start coordinates + // and that the other values match within tolerance. + // Skipped on iOS, because the received position is often the end position + // instead of the start position. Bug in native SDK. + if (Platform.isAndroid) { + checkCameraCoordinatesMatch(cameraMoveStartedPosition, camera); + expect(cameraMoveStartedPosition.bearing, closeTo(camera.bearing, 30)); + expect(cameraMoveStartedPosition.tilt, camera.tilt); + expect(cameraMoveStartedPosition.zoom, closeTo(camera.zoom, 0.1)); + } + + // Test that cameraMoveEvent position coordinates are between the start and the end coordinates + // and that the other values match within tolerance. + const double tolerance = 0.003; + expect(cameraMovePosition.target.latitude, + greaterThanOrEqualTo(camera.target.latitude - tolerance)); + expect(cameraMovePosition.target.latitude, + lessThanOrEqualTo(expectedPosition.target.latitude + tolerance)); + expect(cameraMovePosition.target.longitude, + greaterThanOrEqualTo(camera.target.longitude - tolerance)); + expect(cameraMovePosition.target.longitude, + lessThanOrEqualTo(expectedPosition.target.longitude + tolerance)); + expect(cameraMovePosition.bearing, closeTo(camera.bearing, 30)); + expect(cameraMovePosition.tilt, camera.tilt); + expect(cameraMovePosition.zoom, closeTo(camera.zoom, 0.1)); + + // Test that cameraIdleEvent position coordinates are close to the provided coordinates + // and that the other values match within tolerance. + checkCameraCoordinatesMatch(cameraIdlePosition, expectedPosition); + expect(cameraIdlePosition.bearing, closeTo(camera.bearing, 30)); + expect(cameraIdlePosition.tilt, camera.tilt); + expect(cameraIdlePosition.zoom, closeTo(camera.zoom, 0.1)); + }); + + patrol('Test moveCamera() and animateCamera() with various options', + (PatrolIntegrationTester $) async { + /// Initialize navigation with the event listener functions. + final GoogleNavigationViewController controller = + await startNavigationWithoutDestination( + $, + simulateLocation: true, + onCameraIdle: onCameraIdle, + ); + + // Create a wrapper for moveCamera() that waits until the move is finished. + Future moveCamera(CameraUpdate update) async { + resetCameraEventCompleters(); + await controller.moveCamera(update); + await cameraIdleCompleter.future + .timeout(const Duration(seconds: 10), onTimeout: onTimeout); + } + + // Create a wrapper for animateCamera() that waits until move is finished + // using cameraIdle event on iOS and onFinished listener on Android. + Future animateCamera(CameraUpdate update) async { + resetCameraEventCompleters(); + + // Create onFinished callback function that is used on Android + // to test that the callback comes in. + void onFinished(bool finished) { + expect(finished, true); + } + + // Animate camera to the set position with reduced duration. + await controller.animateCamera( + update, + duration: const Duration(milliseconds: 50), + onFinished: onFinished, + ); + + // Wait until the cameraIdle event comes in. + await cameraIdleCompleter.future.timeout( + const Duration(seconds: 10), + onTimeout: onTimeout, + ); + } + + final List Function(CameraUpdate update)> cameraMethods = + Function(CameraUpdate update)>[moveCamera, animateCamera]; + + const double startLat = startLocationLat + 1; + const double startLon = startLocationLon + 1; + const LatLng target = + LatLng(latitude: startLat + 1, longitude: startLon + 1); + + final CameraUpdate start = + CameraUpdate.newCameraPosition(const CameraPosition( + target: LatLng( + latitude: startLat, + longitude: startLon, + ), + zoom: 9, + )); + + // Move camera back to the start. + Future moveCameraToStart() async { + resetCameraEventCompleters(); + await controller.moveCamera(start); + await cameraIdleCompleter.future.timeout( + const Duration(seconds: 10), + onTimeout: onTimeout, + ); + } + + final CameraUpdate updateCameraPosition = + CameraUpdate.newCameraPosition(const CameraPosition( + bearing: 5, + target: target, + tilt: 30, + zoom: 20.0, + )); + + // Test the CameraUpdate.newCameraPosition() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateCameraPosition); + + // Test that the camera position matches the provided position. + checkCameraCoordinatesMatch( + cameraIdlePosition, updateCameraPosition.cameraPosition!); + expect(cameraIdlePosition.bearing, + closeTo(updateCameraPosition.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.tilt, + closeTo(updateCameraPosition.cameraPosition!.tilt, 0.1)); + expect(cameraIdlePosition.zoom, + closeTo(updateCameraPosition.cameraPosition!.zoom, 0.1)); + + await moveCameraToStart(); + } + + final CameraUpdate updateNewLatLng = CameraUpdate.newLatLng(target); + + // Test the CameraUpdate.newLatLng() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateNewLatLng); + + // Test that the camera target matches the provided target. + expect(cameraIdlePosition.target.latitude, + closeTo(updateNewLatLng.latLng!.latitude, 0.02)); + expect(cameraIdlePosition.target.longitude, + closeTo(updateNewLatLng.latLng!.longitude, 0.02)); + + // Test that the other values haven't changed + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + expect(cameraIdlePosition.zoom, closeTo(start.cameraPosition!.zoom, 0.1)); + + await moveCameraToStart(); + } + + final CameraUpdate updateLatLngBounds = + CameraUpdate.newLatLngBounds(LatLngBounds( + southwest: target, + northeast: LatLng( + latitude: target.latitude + 1, + longitude: target.longitude + 1, + ))); + + // Test the CameraUpdate.newLatLngBounds() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateLatLngBounds); + + // Test that the camera target matches the centre of the LatLngBounds. + expect(cameraIdlePosition.target.latitude, + closeTo(updateLatLngBounds.bounds!.center.latitude, 0.02)); + expect(cameraIdlePosition.target.longitude, + closeTo(updateLatLngBounds.bounds!.center.longitude, 0.02)); + + // Test that the other values, excluding zoom, haven't changed. + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + + await moveCameraToStart(); + } + + final CameraUpdate updateLatLngZoom = + CameraUpdate.newLatLngZoom(target, 12); + + // Test the CameraUpdate.newLatLngZoom() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateLatLngZoom); + + // Test that the camera target and zoom match the provided target and zoom. + expect( + cameraIdlePosition.target.latitude, closeTo(target.latitude, 0.02)); + expect( + cameraIdlePosition.target.longitude, closeTo(target.longitude, 0.02)); + expect(cameraIdlePosition.zoom, closeTo(updateLatLngZoom.zoom!, 0.1)); + + // Test that the the other values haven't changed + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + + await moveCameraToStart(); + } + + // Scroll to the northeast. + final CameraUpdate updateScrollBy = CameraUpdate.scrollBy(300, -300); + + // Test the CameraUpdate.scrollBy() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateScrollBy); + + // Test that the camera position has moved to the northeast. + expect(cameraIdlePosition.target.latitude, + greaterThan(start.cameraPosition!.target.latitude)); + expect(cameraIdlePosition.target.longitude, + greaterThan(start.cameraPosition!.target.longitude)); + + // Test that the the other values haven't changed. + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + expect(cameraIdlePosition.zoom, closeTo(start.cameraPosition!.zoom, 0.1)); + + await moveCameraToStart(); + } + + final CameraUpdate updateZoomByAmount = + CameraUpdate.zoomBy(5, focus: const Offset(50, 50)); + + // Test the CameraUpdate.zoomBy() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateZoomByAmount); + + // Test that the [focus] parameter caused the camera position to change. + expect(cameraIdlePosition.target.latitude, + isNot(closeTo(target.latitude, 0.02))); + expect(cameraIdlePosition.target.longitude, + isNot(closeTo(target.longitude, 0.02))); + + // Test that the zoom has changed to the specified value. + expect( + cameraIdlePosition.zoom, + closeTo(start.cameraPosition!.zoom + updateZoomByAmount.zoomByAmount!, + 0.1)); + + // Test that the other values haven't changed. + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + + await moveCameraToStart(); + } + + final CameraUpdate updateZoomIn = CameraUpdate.zoomIn(); + + // Test the CameraUpdate.zoomIn() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateZoomIn); + + // Test that the zoom has changed to the specified value. + expect( + cameraIdlePosition.zoom, + closeTo( + start.cameraPosition!.zoom + updateZoomIn.zoomByAmount!, 0.1)); + + // Test that the other values haven't changed. + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.target.latitude, + closeTo(start.cameraPosition!.target.latitude, 0.02)); + expect(cameraIdlePosition.target.longitude, + closeTo(start.cameraPosition!.target.longitude, 0.02)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + + await moveCameraToStart(); + } + + final CameraUpdate updateZoomOut = CameraUpdate.zoomOut(); + + // Test the CameraUpdate.zoomOut() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateZoomOut); + + // Test that the zoom has changed to the specified value. + expect( + cameraIdlePosition.zoom, + closeTo( + start.cameraPosition!.zoom + updateZoomOut.zoomByAmount!, 0.1)); + + // Test that the target and camera tilt haven't changed. + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.target.latitude, + closeTo(start.cameraPosition!.target.latitude, 0.02)); + expect(cameraIdlePosition.target.longitude, + closeTo(start.cameraPosition!.target.longitude, 0.02)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + + await moveCameraToStart(); + } + + final CameraUpdate updateZoomTo = CameraUpdate.zoomTo(11); + + // Test the CameraUpdate.zoomBy() with moveCamera and animateCamera commands. + for (final Future Function(CameraUpdate update) cameraMethod + in cameraMethods) { + await cameraMethod(updateZoomTo); + + // Test that the zoom has changed to the specified value. + expect(cameraIdlePosition.zoom, closeTo(updateZoomTo.zoom!, 0.1)); + + // Test that the target and camera tilt haven't changed. + expect(cameraIdlePosition.bearing, + closeTo(start.cameraPosition!.bearing, 0.1)); + expect(cameraIdlePosition.target.latitude, + closeTo(start.cameraPosition!.target.latitude, 0.02)); + expect(cameraIdlePosition.target.longitude, + closeTo(start.cameraPosition!.target.longitude, 0.02)); + expect(cameraIdlePosition.tilt, closeTo(start.cameraPosition!.tilt, 0.1)); + + await moveCameraToStart(); + } + }); +} diff --git a/example/integration_test/map_test.dart b/example/integration_test/t06_map_test.dart similarity index 67% rename from example/integration_test/map_test.dart rename to example/integration_test/t06_map_test.dart index f0ab2a5..86cf955 100644 --- a/example/integration_test/map_test.dart +++ b/example/integration_test/t06_map_test.dart @@ -26,26 +26,10 @@ import 'package:flutter/material.dart'; import 'shared.dart'; void main() { - patrolTest('Test map types', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - viewControllerCompleter.complete(controller); - }, - ), - ); - + patrol('Test map types', (PatrolIntegrationTester $) async { + /// Set up navigation. final GoogleNavigationViewController viewController = - await viewControllerCompleter.future; + await startNavigationWithoutDestination($); // Test default type. expect(await viewController.getMapType(), MapType.normal); @@ -63,7 +47,7 @@ void main() { } }); - patrolTest('Test platform view creation params', + patrol('Test platform view creation params', (PatrolIntegrationTester $) async { final Completer controllerCompleter = Completer(); @@ -108,23 +92,11 @@ void main() { } }); - patrolTest('Test map UI settings', (PatrolIntegrationTester $) async { - final Completer controllerCompleter = - Completer(); - - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController viewController) { - controllerCompleter.complete(viewController); - }, - ), - ); - + patrol('Test map UI settings', (PatrolIntegrationTester $) async { + /// Set up navigation without initialization to test isMyLocationEnabled + /// is false before initialization is done. final GoogleNavigationViewController controller = - await controllerCompleter.future; + await startNavigationWithoutDestination($, initializeNavigation: false); /// Test the default values match with what has been documented in the /// API documentation in google_maps_navigation.dart file. @@ -141,38 +113,38 @@ void main() { final List results = [true, false, true]; for (final bool result in results) { - await controller.enableMyLocation(enabled: result); + await controller.setMyLocationEnabled(result); expect(await controller.isMyLocationEnabled(), result); - await controller.settings.enableMyLocationButton(enabled: result); + await controller.settings.setMyLocationButtonEnabled(result); expect(await controller.settings.isMyLocationButtonEnabled(), result); - await controller.settings.enableZoomGestures(enabled: result); + await controller.settings.setZoomGesturesEnabled(result); expect(await controller.settings.isZoomGesturesEnabled(), result); - await controller.settings.enableCompass(enabled: result); + await controller.settings.setCompassEnabled(result); expect(await controller.settings.isCompassEnabled(), result); - await controller.settings.enableRotateGestures(enabled: result); + await controller.settings.setRotateGesturesEnabled(result); expect(await controller.settings.isRotateGesturesEnabled(), result); - await controller.settings.enableScrollGestures(enabled: result); + await controller.settings.setScrollGesturesEnabled(result); expect(await controller.settings.isScrollGesturesEnabled(), result); await controller.settings - .enableScrollGesturesDuringRotateOrZoom(enabled: result); + .setScrollGesturesDuringRotateOrZoomEnabled(result); expect( await controller.settings.isScrollGesturesEnabledDuringRotateOrZoom(), result); - await controller.settings.enableTiltGestures(enabled: result); + await controller.settings.setTiltGesturesEnabled(result); expect(await controller.settings.isTiltGesturesEnabled(), result); - await controller.settings.enableTraffic(enabled: result); + await controller.settings.setTrafficEnabled(result); expect(await controller.settings.isTrafficEnabled(), result); if (Platform.isAndroid) { - await controller.settings.enableZoomControls(enabled: result); + await controller.settings.setZoomControlsEnabled(result); expect(await controller.settings.isZoomControlsEnabled(), result); } } @@ -186,7 +158,7 @@ void main() { expect(e, const TypeMatcher()); } try { - await controller.settings.enableZoomControls(enabled: true); + await controller.settings.setZoomControlsEnabled(true); fail('Expected to get UnsupportedError'); } on Object catch (e) { expect(e, const TypeMatcher()); @@ -198,7 +170,7 @@ void main() { expect(e, const TypeMatcher()); } try { - await controller.settings.enableMapToolbar(enabled: true); + await controller.settings.setMapToolbarEnabled(true); fail('Expected to get UnsupportedError'); } on Object catch (e) { expect(e, const TypeMatcher()); @@ -206,7 +178,30 @@ void main() { } }); - patrolTest('Test map style', (PatrolIntegrationTester $) async { + patrol('Test map style', (PatrolIntegrationTester $) async { + /// Set up navigation. + final GoogleNavigationViewController viewController = + await startNavigationWithoutDestination($); + + // Test that valid json doens't throw exception. + await viewController.setMapStyle( + '[{"elementType":"geometry","stylers":[{"color":"#ffffff"}]}]'); + + // Test that null value doens't throw exception. + await viewController.setMapStyle(null); + + // Test that invalid json throws exception. + try { + await viewController.setMapStyle('not_json'); + fail('expected to get MapStyleException'); + } on MapStyleException catch (e) { + expect(e, isNotNull); + } + }); + + patrol('Test min max zoom level', (PatrolIntegrationTester $) async { + /// For some reason the functionality works on Android example app, but it doesn't work + /// during the testing. Will skip Android testing for now. final Completer viewControllerCompleter = Completer(); @@ -227,19 +222,50 @@ void main() { final GoogleNavigationViewController viewController = await viewControllerCompleter.future; - // Test that valid json doens't throw exception. - await viewController.setMapStyle( - '[{"elementType":"geometry","stylers":[{"color":"#ffffff"}]}]'); + // Test that valid zoom values don't throw exception. + await viewController.setMinZoomPreference(10); + await viewController.setMaxZoomPreference(11); - // Test that null value doens't throw exception. - await viewController.setMapStyle(null); + // Test that min max values were changed. + double newMinZoomPreference = await viewController.getMinZoomPreference(); + double newMaxZoomPreference = await viewController.getMaxZoomPreference(); - // Test that invalid json throws exception. + expect(newMinZoomPreference, 10.0); + expect(newMaxZoomPreference, 11.0); + + // Reset zoom limits. + await viewController.resetMinMaxZoomPreference(); + + // Test that min max values were reset. + final double resetedMinZoom = await viewController.getMinZoomPreference(); + final double resetedMaxZoom = await viewController.getMaxZoomPreference(); + + expect(resetedMinZoom, isNot(10.0)); + expect(resetedMaxZoom, isNot(11.0)); + + // Test that invalid value throws exception. try { - await viewController.setMapStyle('not_json'); - fail('expected to get MapStyleException'); - } on MapStyleException catch (e) { + await viewController.setMinZoomPreference(40); + fail('expected to get ZoomPreferenceException'); + } on MinZoomRangeException catch (e) { + expect(e, isNotNull); + } + try { + await viewController.setMaxZoomPreference(1); + fail('expected to get ZoomPreferenceException'); + } on MaxZoomRangeException catch (e) { expect(e, isNotNull); } + + // Try to set out of bounds values. + await viewController.setMinZoomPreference(0); + await viewController.setMaxZoomPreference(50); + + newMinZoomPreference = await viewController.getMinZoomPreference(); + newMaxZoomPreference = await viewController.getMaxZoomPreference(); + + // Expect the same values. The actual zoom level will be limited by the map. + expect(newMinZoomPreference, 0.0); + expect(newMaxZoomPreference, 50.0); }); } diff --git a/example/integration_test/event_listener_test.dart b/example/integration_test/t07_event_listener_test.dart similarity index 67% rename from example/integration_test/event_listener_test.dart rename to example/integration_test/t07_event_listener_test.dart index 2125e47..0acab6c 100644 --- a/example/integration_test/event_listener_test.dart +++ b/example/integration_test/t07_event_listener_test.dart @@ -20,38 +20,14 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction -import 'dart:async'; import 'dart:io'; -import 'package:flutter/material.dart'; import 'shared.dart'; void main() { - patrolTest('Test navigation OnRemainingTimeOrDistanceChanged event listener', + patrol('Test navigation OnRemainingTimeOrDistanceChanged event listener', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - await checkTermsAndConditionsAcceptance($); - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); + /// Set up navigation. + await startNavigationWithoutDestination($); /// Set up the listener and the test. GoogleMapsNavigator.setOnRemainingTimeOrDistanceChangedListener( @@ -94,32 +70,10 @@ void main() { await $.pumpAndSettle(); }); - patrolTest('Test navigation OnRouteChanged event listener', + patrol('Test navigation OnRouteChanged event listener', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - await checkTermsAndConditionsAcceptance($); - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); + /// Set up navigation. + await startNavigationWithoutDestination($); /// Set up the listener and the test. GoogleMapsNavigator.setOnRouteChangedListener(expectAsync0( @@ -162,31 +116,10 @@ void main() { await $.pumpAndSettle(); }); - patrolTest('Test navigation RoadSnappedLocationUpdated event listener', + patrol('Test navigation RoadSnappedLocationUpdated event listener', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); - await $.pumpAndSettle(); + /// Sert up navigation. + await startNavigationWithoutDestination($); /// Simulate location (1298 California St) await GoogleMapsNavigator.simulator.setUserLocation(const LatLng( @@ -238,30 +171,10 @@ void main() { } }); - patrolTest('Test navigation onArrival event listener', + patrol('Test navigation onArrival event listener', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; - - /// Initialize navigation. - await GoogleMapsNavigator.initializeNavigationSession(); + /// Set uo navigation. + await startNavigationWithoutDestination($); /// Set up the listener and the test. GoogleMapsNavigator.setOnArrivalListener(expectAsync1( @@ -308,30 +221,12 @@ void main() { /// Rerouting listener is Android only. if (Platform.isAndroid) { - patrolTest('Test navigation onRerouting event listener', + patrol('Test navigation onRerouting event listener', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAndTosAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - controller.enableMyLocation(enabled: true); - viewControllerCompleter.complete(controller); - }, - ), - ); - - await viewControllerCompleter.future; + /// Set up navigation. + await startNavigationWithoutDestination($); - /// Initialize navigation and set up the rerouting listener with the test. - await GoogleMapsNavigator.initializeNavigationSession(); + /// Set up the rerouting listener with the test. GoogleMapsNavigator.setOnReroutingListener(expectAsync1( (OnArrivalEvent event) {}, count: 1, diff --git a/example/integration_test/marker_polygon_polyline_circle_test.dart b/example/integration_test/t08_marker_polygon_polyline_circle_test.dart similarity index 87% rename from example/integration_test/marker_polygon_polyline_circle_test.dart rename to example/integration_test/t08_marker_polygon_polyline_circle_test.dart index ece40b5..4a363e0 100644 --- a/example/integration_test/marker_polygon_polyline_circle_test.dart +++ b/example/integration_test/t08_marker_polygon_polyline_circle_test.dart @@ -20,47 +20,32 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction -import 'dart:async'; import 'dart:io'; import 'dart:math'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; import 'shared.dart'; void main() { - patrolTest('Marker tests', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - viewControllerCompleter.complete(controller); - }, - ), - ); - + patrol('Marker tests', (PatrolIntegrationTester $) async { + /// Set up navigation. final GoogleNavigationViewController viewController = - await viewControllerCompleter.future; + await startNavigationWithoutDestination($); - // Helsinki marker options. - const MarkerOptions helsinkiOfficeMarkerOptions = MarkerOptions( + // markerOne options. + const MarkerOptions markerOneOptions = MarkerOptions( position: LatLng(latitude: 60.34856639667419, longitude: 25.03459821831162), infoWindow: InfoWindow( title: 'Helsinki Office', - snippet: 'HelsinkiMarker', + snippet: 'markerOne', ), ); // Add marker and save response to [addedMarkersList]. - final List addedMarkersList = await viewController - .addMarkers([helsinkiOfficeMarkerOptions]); + final List addedMarkersList = + await viewController.addMarkers([markerOneOptions]); expect(addedMarkersList.length, 1); final Marker? addedMarker = addedMarkersList.first; @@ -79,44 +64,50 @@ void main() { expect(marker.options.anchor.v, 1.0); expect(marker.options.draggable, false); expect(marker.options.flat, false); + expect(marker.options.icon, ImageDescriptor.defaultImage); expect(marker.options.consumeTapEvents, false); - expect(marker.options.position, helsinkiOfficeMarkerOptions.position); + expect(marker.options.position, markerOneOptions.position); expect(marker.options.rotation, 0.0); - expect(marker.options.infoWindow.title, - helsinkiOfficeMarkerOptions.infoWindow.title); + expect( + marker.options.infoWindow.title, markerOneOptions.infoWindow.title); expect(marker.options.infoWindow.snippet, - helsinkiOfficeMarkerOptions.infoWindow.snippet); + markerOneOptions.infoWindow.snippet); expect(marker.options.infoWindow.anchor.u, 0.5); expect(marker.options.infoWindow.anchor.v, 0.0); expect(marker.options.visible, true); expect(marker.options.zIndex, 0.0); } - // Updated Oulu marker options. - const MarkerOptions ouluOfficeMarkerOptions = MarkerOptions( + /// Create a marker icon. + final ByteData imageBytes = await rootBundle.load('assets/marker1.png'); + final ImageDescriptor customIcon = + await registerBitmapImage(bitmap: imageBytes, imagePixelRatio: 2); + + // markerTwo options. + final MarkerOptions markerTwoOptions = MarkerOptions( alpha: 0.5, - anchor: MarkerAnchor(u: 0.1, v: 0.2), + anchor: const MarkerAnchor(u: 0.1, v: 0.2), draggable: true, flat: true, + icon: customIcon, consumeTapEvents: true, - position: - LatLng(latitude: 65.01193816057041, longitude: 25.46790635614996), + position: const LatLng( + latitude: 65.01193816057041, longitude: 25.46790635614996), rotation: 70, - infoWindow: InfoWindow( + infoWindow: const InfoWindow( title: 'Oulu Office', - snippet: 'OuluMarker', + snippet: 'markerTwo', anchor: MarkerAnchor(u: 0.3, v: 0.4), ), visible: false, zIndex: 2, ); - final Marker ouluMarker = - addedMarker.copyWith(options: ouluOfficeMarkerOptions); + final Marker markerTwo = addedMarker.copyWith(options: markerTwoOptions); // Update marker and save response. final List updatedMarkersList = - await viewController.updateMarkers([ouluMarker]); + await viewController.updateMarkers([markerTwo]); expect(updatedMarkersList.length, 1); final Marker? updatedMarker = updatedMarkersList.first; @@ -132,44 +123,45 @@ void main() { /// Test updated marker options against updateMarkers and getMarkers responses. for (final Marker marker in markers) { expect(marker.markerId, addedMarker.markerId); - expect(marker.options.alpha, ouluOfficeMarkerOptions.alpha); + expect(marker.options.alpha, markerTwoOptions.alpha); expect(marker.options.anchor.u, - closeTo(ouluOfficeMarkerOptions.anchor.u, tolerance)); + closeTo(markerTwoOptions.anchor.u, tolerance)); expect(marker.options.anchor.v, - closeTo(ouluOfficeMarkerOptions.anchor.v, tolerance)); - expect(marker.options.draggable, ouluOfficeMarkerOptions.draggable); - expect(marker.options.flat, ouluOfficeMarkerOptions.flat); - expect(marker.options.consumeTapEvents, - ouluOfficeMarkerOptions.consumeTapEvents); + closeTo(markerTwoOptions.anchor.v, tolerance)); + expect(marker.options.draggable, markerTwoOptions.draggable); + expect(marker.options.flat, markerTwoOptions.flat); + expect(marker.options.icon, markerTwoOptions.icon); + expect( + marker.options.consumeTapEvents, markerTwoOptions.consumeTapEvents); expect(marker.options.infoWindow.anchor.u, - closeTo(ouluOfficeMarkerOptions.infoWindow.anchor.u, tolerance)); + closeTo(markerTwoOptions.infoWindow.anchor.u, tolerance)); expect(marker.options.infoWindow.anchor.v, - closeTo(ouluOfficeMarkerOptions.infoWindow.anchor.v, tolerance)); - expect(marker.options.position, ouluOfficeMarkerOptions.position); - expect(marker.options.rotation, ouluOfficeMarkerOptions.rotation); + closeTo(markerTwoOptions.infoWindow.anchor.v, tolerance)); + expect(marker.options.position, markerTwoOptions.position); + expect(marker.options.rotation, markerTwoOptions.rotation); expect(marker.options.infoWindow.snippet, - ouluOfficeMarkerOptions.infoWindow.snippet); - expect(marker.options.infoWindow.title, - ouluOfficeMarkerOptions.infoWindow.title); - expect(marker.options.visible, ouluOfficeMarkerOptions.visible); - expect(marker.options.zIndex, ouluOfficeMarkerOptions.zIndex); + markerTwoOptions.infoWindow.snippet); + expect( + marker.options.infoWindow.title, markerTwoOptions.infoWindow.title); + expect(marker.options.visible, markerTwoOptions.visible); + expect(marker.options.zIndex, markerTwoOptions.zIndex); } - // Jyväsylä marker options. - const MarkerOptions jyvaskylaMarkerOptions = MarkerOptions( + // markerThree options. + const MarkerOptions markerThreeOptions = MarkerOptions( position: LatLng(latitude: 62.25743381335948, longitude: 25.779330148583174), infoWindow: InfoWindow( title: 'Jyväskylä', - snippet: 'Jyväskylä', + snippet: 'markerThree', ), ); /// Test addMarkers() adds markers in correct order. final List removeMarkerList = await viewController.addMarkers([ - jyvaskylaMarkerOptions, - helsinkiOfficeMarkerOptions, + markerThreeOptions, + markerOneOptions, ]); final List getRemoveMarkerList = await viewController.getMarkers(); @@ -214,8 +206,8 @@ void main() { expect(getRemovedMarkerList.first!.options.infoWindow.title, 'Jyväskylä'); // Add two markers. - List clearMarkerList = await viewController.addMarkers( - [helsinkiOfficeMarkerOptions, ouluOfficeMarkerOptions]); + List clearMarkerList = await viewController + .addMarkers([markerOneOptions, markerTwoOptions]); List getClearMarkerList = await viewController.getMarkers(); expect(clearMarkerList.length, 2); @@ -240,8 +232,8 @@ void main() { expect(getClearMarkerList, isEmpty); /// Test clear() function clears also markers. - clearMarkerList = await viewController.addMarkers( - [helsinkiOfficeMarkerOptions, ouluOfficeMarkerOptions]); + clearMarkerList = await viewController + .addMarkers([markerOneOptions, markerTwoOptions]); getClearMarkerList = await viewController.getMarkers(); expect(clearMarkerList.length, 2); expect(getClearMarkerList.length, 2); @@ -251,26 +243,10 @@ void main() { expect(getClearMarkerList, isEmpty); }); - patrolTest('Test polylines', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - viewControllerCompleter.complete(controller); - }, - ), - ); - + patrol('Test polylines', (PatrolIntegrationTester $) async { + /// Set up navigation. final GoogleNavigationViewController viewController = - await viewControllerCompleter.future; + await startNavigationWithoutDestination($); await viewController.addPolylines( [ @@ -479,26 +455,10 @@ void main() { expect(receivedPolylines8.length, 0); }); - patrolTest('Polygon tests', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - viewControllerCompleter.complete(controller); - }, - ), - ); - + patrol('Polygon tests', (PatrolIntegrationTester $) async { + /// Set up navigation. final GoogleNavigationViewController viewController = - await viewControllerCompleter.future; + await startNavigationWithoutDestination($); /// Creates square, 4 coordinates, from top left and bottom right coordinates. List createSquare(LatLng topLeft, LatLng bottomRight) { @@ -781,26 +741,10 @@ void main() { expect(getPolygons, isEmpty); }); - patrolTest('Circle tests', (PatrolIntegrationTester $) async { - final Completer viewControllerCompleter = - Completer(); - - await checkLocationDialogAcceptance($); - - /// Display navigation view. - final Key key = GlobalKey(); - await pumpNavigationView( - $, - GoogleMapsNavigationView( - key: key, - onViewCreated: (GoogleNavigationViewController controller) { - viewControllerCompleter.complete(controller); - }, - ), - ); - + patrol('Circle tests', (PatrolIntegrationTester $) async { + /// Set up navigation. final GoogleNavigationViewController viewController = - await viewControllerCompleter.future; + await startNavigationWithoutDestination($); // Add circle on the current camera position. final CameraPosition position = await viewController.getCameraPosition(); diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9b41e7d..b3aaa73 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -25,6 +25,6 @@ arm64 MinimumOSVersion - 11.0 + 12.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index defe2f0..8813e05 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -40,8 +40,6 @@ target 'Runner' do end end -source 'https://github.com/CocoaPods/Specs.git' - post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 14a5d72..61079a6 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -23,7 +23,10 @@ import GoogleNavigation _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - var mapsApiKey = findMapApiKeyFromDartDefines("MAPS_API_KEY") ?? ProcessInfo.processInfo.environment["MAPS_API_KEY"] ?? "" + // 1. Try to find the Maps API key from the environment variables. + // 2. Try to find the Maps API key from the Dart defines. + // 3. Use the default Maps API key "YOUR_API_KEY". + var mapsApiKey = ProcessInfo.processInfo.environment["MAPS_API_KEY"] ?? findMapApiKeyFromDartDefines("MAPS_API_KEY") ?? "" if (mapsApiKey.isEmpty) { mapsApiKey = "YOUR_API_KEY" } @@ -31,8 +34,9 @@ import GoogleNavigation GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - - func findMapApiKeyFromDartDefines(_ defineKey: String) -> String? { + + // Helper function to find the Maps API key from the Dart defines + private func findMapApiKeyFromDartDefines(_ defineKey: String) -> String? { if (Bundle.main.infoDictionary!["DART_DEFINES"] == nil) { return nil } diff --git a/example/ios/RunnerTests/ConvertTests.swift b/example/ios/RunnerTests/ConvertTests.swift index df2f3e0..331426e 100644 --- a/example/ios/RunnerTests/ConvertTests.swift +++ b/example/ios/RunnerTests/ConvertTests.swift @@ -19,6 +19,24 @@ import XCTest @testable import google_maps_navigation +class MockImageRegistry: ImageRegistry { + let image = UIImage() + + override func findRegisteredImage(imageId: String) -> RegisteredImage? { + if imageId == "default" { + return nil + } else { + return RegisteredImage( + imageId: imageId, + image: image, + imagePixelRatio: 1.0, + width: nil, + height: nil + ) + } + } +} + class ConvertTests: XCTestCase { func testMapTypeConversion() { XCTAssertEqual(Convert.convertMapType(mapType: .hybrid), GMSMapViewType.hybrid) @@ -217,12 +235,14 @@ class ConvertTests: XCTestCase { anchor: MarkerAnchorDto(u: 0.6, v: 0.8) ), visible: false, - zIndex: 4.0 + zIndex: 4.0, + icon: ImageDescriptorDto(registeredImageId: "default", imagePixelRatio: 1.0) ) ) + let imageRegistry = MockImageRegistry() let markerController = MarkerController(markerId: markerDto.markerId) - markerController.update(from: markerDto) + markerController.update(from: markerDto, imageRegistry: imageRegistry) let gmsMarker = markerController.gmsMarker XCTAssertEqual(Double(gmsMarker.opacity), markerDto.options.alpha) @@ -240,6 +260,7 @@ class ConvertTests: XCTestCase { XCTAssertEqual(gmsMarker.snippet, markerDto.options.infoWindow.snippet) XCTAssertEqual(gmsMarker.title, markerDto.options.infoWindow.title) XCTAssertEqual(gmsMarker.zIndex, Int32(markerDto.options.zIndex)) + XCTAssertNil(gmsMarker.icon) markerDto = markerController.toMarkerDto() @@ -257,6 +278,7 @@ class ConvertTests: XCTestCase { XCTAssertEqual(markerDto.options.infoWindow.title, gmsMarker.title) XCTAssertEqual(markerDto.options.infoWindow.snippet, gmsMarker.snippet) XCTAssertEqual(Int32(markerDto.options.zIndex), gmsMarker.zIndex) + XCTAssertNil(markerDto.options.icon.registeredImageId) } func testUpdateGMSMarker() { @@ -276,12 +298,14 @@ class ConvertTests: XCTestCase { anchor: MarkerAnchorDto(u: 0.6, v: 0.8) ), visible: false, - zIndex: 4.0 + zIndex: 4.0, + icon: ImageDescriptorDto(registeredImageId: "default", imagePixelRatio: 1.0) ) ) let markerController = MarkerController(markerId: marker.markerId) - markerController.update(from: marker) + let imageRegistry = MockImageRegistry() + markerController.update(from: marker, imageRegistry: imageRegistry) let updatedMarker = MarkerDto( markerId: "1345", @@ -299,11 +323,12 @@ class ConvertTests: XCTestCase { anchor: MarkerAnchorDto(u: 0.6, v: 0.8) ), visible: false, - zIndex: 4.0 + zIndex: 4.0, + icon: ImageDescriptorDto(registeredImageId: "Image_0", imagePixelRatio: 1.0) ) ) - markerController.update(from: updatedMarker) + markerController.update(from: updatedMarker, imageRegistry: imageRegistry) let gmsMarker = markerController.gmsMarker XCTAssertEqual(Double(gmsMarker.opacity), updatedMarker.options.alpha) @@ -333,6 +358,7 @@ class ConvertTests: XCTestCase { XCTAssertEqual(gmsMarker.snippet, updatedMarker.options.infoWindow.snippet) XCTAssertEqual(gmsMarker.title, updatedMarker.options.infoWindow.title) XCTAssertEqual(gmsMarker.zIndex, Int32(updatedMarker.options.zIndex)) + XCTAssertEqual(gmsMarker.icon, imageRegistry.image) } func testPigeonPolygonToGmsPolygon() { @@ -477,7 +503,7 @@ class ConvertTests: XCTestCase { func testConvertLatLngItem() { let latLngPoint = LatLngDto(latitude: 44.0, longitude: 55.0) - let coordinate = Convert.convertLatLng(point: latLngPoint) + let coordinate = Convert.convertLatLngFromDto(point: latLngPoint) XCTAssertEqual(latLngPoint.latitude, coordinate.latitude) XCTAssertEqual(latLngPoint.longitude, coordinate.longitude) @@ -516,12 +542,11 @@ class ConvertTests: XCTestCase { zoomControlsEnabled: false ) - let navigationViewOptions = NavigationViewOptionsDto(navigationUIEnabled: true) + let navigationViewOptions = + NavigationViewOptionsDto(navigationUIEnabledPreference: NavigationUIEnabledPreferenceDto + .automatic) - let configuration = Convert.convertMapOptions( - mapOptions, - navigationViewOptions: navigationViewOptions - ) + let configuration = Convert.convertMapOptions(mapOptions) // Make sure these match because comparison between different enums is complicated. XCTAssert(mapOptions.mapType == .normal) diff --git a/example/lib/main.dart b/example/lib/main.dart index 18678c6..c293fd7 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -14,9 +14,11 @@ // ignore_for_file: public_member_api_docs +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:google_maps_navigation/google_maps_navigation.dart'; import 'package:permission_handler/permission_handler.dart'; import 'pages/circles.dart'; import 'pages/pages.dart'; @@ -57,11 +59,18 @@ class _NavigationDemoState extends State { bool _locationPermitted = false; bool _notificationsPermitted = false; + String _navSDKVersion = ''; @override void initState() { _requestPermissions(); super.initState(); + unawaited(_checkSDKVersion()); + } + + Future _checkSDKVersion() async { + // Get the Navigation SDK version. + _navSDKVersion = await GoogleMapsNavigator.getNavSDKVersion(); } Future _pushPage(BuildContext context, ExamplePage page) async { @@ -101,11 +110,16 @@ class _NavigationDemoState extends State { if (index == 0) { return Card( child: Container( - height: 40, + padding: const EdgeInsets.symmetric(vertical: 4.0), alignment: Alignment.center, - child: Text(Platform.isIOS - ? 'Location ${_locationPermitted ? 'granted' : 'denied'} • Notifications ${_notificationsPermitted ? 'granted' : 'denied'}' - : 'Location ${_locationPermitted ? 'granted' : 'denied'} '), + child: Column( + children: [ + Text(Platform.isIOS + ? 'Location ${_locationPermitted ? 'granted' : 'denied'} • Notifications ${_notificationsPermitted ? 'granted' : 'denied'}' + : 'Location ${_locationPermitted ? 'granted' : 'denied'} '), + Text('Navigation SDK version: $_navSDKVersion'), + ], + ), ), ); } diff --git a/example/lib/pages/camera.dart b/example/lib/pages/camera.dart index b00d885..5d1f794 100644 --- a/example/lib/pages/camera.dart +++ b/example/lib/pages/camera.dart @@ -36,11 +36,18 @@ class _CameraPageState extends ExamplePageState { double _focusY = 0; bool _navigationRunning = false; late final GoogleNavigationViewController _navigationViewController; + late double _minZoomLevel; + late double _maxZoomLevel; + bool _showCameraUpdates = false; + String _latestCameraUpdate = ''; + bool _isFollowingLocation = false; // ignore: use_setters_to_change_properties Future _onViewCreated(GoogleNavigationViewController controller) async { _navigationViewController = controller; calculateFocusCenter(); + _minZoomLevel = await _navigationViewController.getMinZoomPreference(); + _maxZoomLevel = await _navigationViewController.getMaxZoomPreference(); setState(() {}); } @@ -120,11 +127,66 @@ class _CameraPageState extends ExamplePageState { context, Stack(children: [ GoogleMapsNavigationView( - onViewCreated: _onViewCreated, - ), - getOverlayOptionsButton(context, onPressed: () => toggleOverlay()) + initialNavigationUIEnabledPreference: + NavigationUIEnabledPreference.disabled, + onViewCreated: _onViewCreated, + onCameraMoveStarted: _onCameraMoveStarted, + onCameraMove: _onCameraMove, + onCameraIdle: _onCameraIdle, + onCameraStartedFollowingLocation: _onCameraStartedFollowingLocation, + onCameraStoppedFollowingLocation: + _onCameraStoppedFollowingLocation), + getOverlayOptionsButton(context, onPressed: () => toggleOverlay()), + if (_showCameraUpdates) + Container( + width: 180, + padding: const EdgeInsets.all(8), + decoration: const BoxDecoration(color: Colors.white), + child: Text( + _latestCameraUpdate, + ), + ) ])); + void _onCameraMoveStarted(CameraPosition position, bool gesture) { + if (_showCameraUpdates) { + showMessage(gesture + ? 'Camera move started by gesture' + : 'Camera move started by action'); + } + } + + void _onCameraMove(CameraPosition position) { + final String cameraState = + _isFollowingLocation ? 'Camera following' : 'Camera moving'; + final String positionStr = + 'Position: ${position.target.latitude.toStringAsFixed(2)}, ${position.target.longitude.toStringAsFixed(2)}'; + setState(() { + _latestCameraUpdate = '$cameraState\n$positionStr'; + }); + } + + void _onCameraIdle(CameraPosition position) { + setState(() { + _latestCameraUpdate = + 'Camera idle\nPosition: ${position.target.latitude.toStringAsFixed(2)}, ${position.target.longitude.toStringAsFixed(2)}'; + }); + } + + // Android only. + void _onCameraStartedFollowingLocation(CameraPosition position) { + setState(() { + _isFollowingLocation = true; + }); + } + + // Android only. + void _onCameraStoppedFollowingLocation(CameraPosition position) { + setState(() { + _isFollowingLocation = false; + }); + } + void showMessage(String message) { hideMessage(); if (isOverlayVisible) { @@ -145,6 +207,36 @@ class _CameraPageState extends ExamplePageState { } } + Future _setMinZoomLevel(double newMinZoomLevel) async { + try { + await _navigationViewController.setMinZoomPreference(newMinZoomLevel); + setState(() { + _minZoomLevel = newMinZoomLevel; + }); + } catch (e) { + showMessage(e.toString()); + } + } + + Future _setMaxZoomLevel(double newMaxZoomLevel) async { + try { + await _navigationViewController.setMaxZoomPreference(newMaxZoomLevel); + setState(() { + _maxZoomLevel = newMaxZoomLevel; + }); + } catch (e) { + showMessage(e.toString()); + } + } + + Future _resetZoomLevels() async { + await _navigationViewController.resetMinMaxZoomPreference(); + _minZoomLevel = await _navigationViewController.getMinZoomPreference(); + _maxZoomLevel = await _navigationViewController.getMaxZoomPreference(); + + setState(() {}); + } + @override Widget buildOverlayContent(BuildContext context) { final ButtonStyle threeButtonRowStyle = Theme.of(context) @@ -200,6 +292,15 @@ class _CameraPageState extends ExamplePageState { }); }, ), + SwitchListTile( + title: const Text('Show camera updates'), + value: _showCameraUpdates, + onChanged: (bool value) { + setState(() { + _showCameraUpdates = value; + }); + }, + ), const SizedBox(height: 24.0), Wrap( alignment: WrapAlignment.center, @@ -487,6 +588,33 @@ class _CameraPageState extends ExamplePageState { }, child: const Text('Get camera position'), ), + const SizedBox(height: 24.0), + Text('Min zoom level: ${_minZoomLevel.round()}'), + Slider( + value: _minZoomLevel, + min: googleMapsMinZoomLevel, + max: googleMapsMaxZoomLevel, + divisions: + (googleMapsMaxZoomLevel - googleMapsMinZoomLevel).toInt(), + label: _minZoomLevel.round().toString(), + onChanged: (double value) { + _setMinZoomLevel(value); + }), + Text('Max zoom level: ${_maxZoomLevel.round()}'), + Slider( + value: _maxZoomLevel, + min: googleMapsMinZoomLevel, + max: googleMapsMaxZoomLevel, + divisions: + (googleMapsMaxZoomLevel - googleMapsMinZoomLevel).toInt(), + label: _maxZoomLevel.round().toString(), + onChanged: (double value) { + _setMaxZoomLevel(value); + }), + ElevatedButton( + onPressed: () => _resetZoomLevels(), + child: const Text('Reset zoom levels'), + ), ], ); } diff --git a/example/lib/pages/circles.dart b/example/lib/pages/circles.dart index 19f484b..aa34247 100644 --- a/example/lib/pages/circles.dart +++ b/example/lib/pages/circles.dart @@ -210,6 +210,8 @@ class _CirclesPageState extends ExamplePageState { initialCameraPosition: const CameraPosition( target: LatLng(latitude: 37.422, longitude: -122.084), zoom: 12), + initialNavigationUIEnabledPreference: + NavigationUIEnabledPreference.disabled, onViewCreated: _onViewCreated, onCircleClicked: _onCircleClicked, )), diff --git a/example/lib/pages/map.dart b/example/lib/pages/map.dart index 4b61623..f8575e4 100644 --- a/example/lib/pages/map.dart +++ b/example/lib/pages/map.dart @@ -35,6 +35,7 @@ class _MapPageState extends ExamplePageState { late final GoogleNavigationViewController _navigationViewController; late bool isMyLocationEnabled = false; late bool isMyLocationButtonEnabled = true; + late bool consumeMyLocationButtonClickEvent = false; late bool isZoomGesturesEnabled = true; late bool isZoomControlsEnabled = true; late bool isCompassEnabled = true; @@ -73,6 +74,21 @@ class _MapPageState extends ExamplePageState { setState(() {}); } + void _showMessage(String message) { + ScaffoldMessenger.of(context).removeCurrentSnackBar(); + final SnackBar snackBar = SnackBar( + duration: const Duration(milliseconds: 2000), content: Text(message)); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } + + void _onMyLocationClicked(MyLocationClickedEvent event) { + _showMessage('My location clicked'); + } + + void _onMyLocationButtonClicked(MyLocationButtonClickedEvent event) { + _showMessage('My location button clicked'); + } + @override Widget build(BuildContext context) { final ButtonStyle mapTypeStyle = ElevatedButton.styleFrom( @@ -86,6 +102,10 @@ class _MapPageState extends ExamplePageState { children: [ GoogleMapsNavigationView( onViewCreated: _onViewCreated, + onMyLocationClicked: _onMyLocationClicked, + onMyLocationButtonClicked: _onMyLocationButtonClicked, + initialNavigationUIEnabledPreference: + NavigationUIEnabledPreference.disabled, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), @@ -120,7 +140,9 @@ class _MapPageState extends ExamplePageState { ), if (mapType == MapType.normal) Padding( - padding: const EdgeInsets.all(8.0), + padding: isMyLocationEnabled && isMyLocationButtonEnabled + ? const EdgeInsets.only(top: 50.0, right: 8.0) + : const EdgeInsets.all(8.0), child: Align( alignment: Alignment.topRight, child: Column( @@ -155,7 +177,7 @@ class _MapPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableCompass(enabled: newValue); + .setCompassEnabled(newValue); final bool enabled = await _navigationViewController.settings.isCompassEnabled(); setState(() { @@ -169,7 +191,7 @@ class _MapPageState extends ExamplePageState { value: isMyLocationEnabled, controlAffinity: ListTileControlAffinity.leading, onChanged: (bool newValue) async { - await _navigationViewController.enableMyLocation(enabled: newValue); + await _navigationViewController.setMyLocationEnabled(newValue); final bool enabled = await _navigationViewController.isMyLocationEnabled(); setState(() { @@ -184,7 +206,7 @@ class _MapPageState extends ExamplePageState { onChanged: isMyLocationEnabled ? (bool newValue) async { await _navigationViewController.settings - .enableMyLocationButton(enabled: newValue); + .setMyLocationButtonEnabled(newValue); final bool enabled = await _navigationViewController.settings .isMyLocationButtonEnabled(); setState(() { @@ -193,10 +215,26 @@ class _MapPageState extends ExamplePageState { } : null, visualDensity: VisualDensity.compact), + SwitchListTile( + title: const Text('Consume my location button click'), + value: consumeMyLocationButtonClickEvent, + controlAffinity: ListTileControlAffinity.leading, + onChanged: isMyLocationEnabled && isMyLocationButtonEnabled + ? (bool newValue) async { + await _navigationViewController.settings + .setConsumeMyLocationButtonClickEventsEnabled(newValue); + final bool enabled = await _navigationViewController.settings + .isConsumeMyLocationButtonClickEventsEnabled(); + setState(() { + consumeMyLocationButtonClickEvent = enabled; + }); + } + : null, + visualDensity: VisualDensity.compact), SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableZoomGestures(enabled: newValue); + .setZoomGesturesEnabled(newValue); final bool enabled = await _navigationViewController.settings .isZoomGesturesEnabled(); setState(() { @@ -209,7 +247,7 @@ class _MapPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableZoomControls(enabled: newValue); + .setZoomControlsEnabled(newValue); final bool enabled = await _navigationViewController.settings .isZoomControlsEnabled(); setState(() { @@ -221,7 +259,7 @@ class _MapPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableRotateGestures(enabled: newValue); + .setRotateGesturesEnabled(newValue); final bool enabled = await _navigationViewController.settings .isRotateGesturesEnabled(); setState(() { @@ -233,7 +271,7 @@ class _MapPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableScrollGestures(enabled: newValue); + .setScrollGesturesEnabled(newValue); final bool enabled = await _navigationViewController.settings .isScrollGesturesEnabled(); setState(() { @@ -245,7 +283,7 @@ class _MapPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableScrollGesturesDuringRotateOrZoom(enabled: newValue); + .setScrollGesturesDuringRotateOrZoomEnabled(newValue); final bool enabled = await _navigationViewController.settings .isScrollGesturesEnabledDuringRotateOrZoom(); setState(() { @@ -257,7 +295,7 @@ class _MapPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableTiltGestures(enabled: newValue); + .setTiltGesturesEnabled(newValue); final bool enabled = await _navigationViewController.settings .isTiltGesturesEnabled(); setState(() { @@ -269,7 +307,7 @@ class _MapPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableTraffic(enabled: newValue); + .setTrafficEnabled(newValue); final bool enabled = await _navigationViewController.settings.isTrafficEnabled(); setState(() { @@ -277,7 +315,7 @@ class _MapPageState extends ExamplePageState { }); }, title: const Text('Enable traffic'), - value: isTrafficEnabled) + value: isTrafficEnabled), ]); } } diff --git a/example/lib/pages/markers.dart b/example/lib/pages/markers.dart index 21fa3ef..2ee4d69 100644 --- a/example/lib/pages/markers.dart +++ b/example/lib/pages/markers.dart @@ -15,7 +15,10 @@ // ignore_for_file: public_member_api_docs import 'dart:io'; +import 'dart:typed_data'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; import 'package:google_maps_navigation/google_maps_navigation.dart'; import '../widgets/widgets.dart'; @@ -30,6 +33,7 @@ class MarkersPage extends ExamplePage { class _MarkersPageState extends ExamplePageState { late final GoogleNavigationViewController _navigationViewController; + ImageDescriptor? _registeredCustomIcon; List _markers = []; Marker? _selectedMarker; @@ -39,7 +43,7 @@ class _MarkersPageState extends ExamplePageState { final List _alphas = [1.0, 0.3]; // ignore: use_setters_to_change_properties - void _onViewCreated(GoogleNavigationViewController controller) { + Future _onViewCreated(GoogleNavigationViewController controller) async { _navigationViewController = controller; } @@ -135,6 +139,52 @@ class _MarkersPageState extends ExamplePageState { _selectedMarker!.options.copyWith(zIndex: newZIndex)); } + Future _getOrCreateCustomImageFromAsset() async { + if (_registeredCustomIcon != null) { + // Custom image already registered. + return _registeredCustomIcon!; + } + + // Example how to load mipmapped bitmap for asset. + const AssetImage assetImage = AssetImage('assets/marker1.png'); + final ImageConfiguration configuration = + createLocalImageConfiguration(context); + final AssetBundleImageKey assetBundleImageKey = + await assetImage.obtainKey(configuration); + final double imagePixelRatio = assetBundleImageKey.scale; + final ByteData imageBytes = await rootBundle.load(assetBundleImageKey.name); + _registeredCustomIcon = await registerBitmapImage( + bitmap: imageBytes, imagePixelRatio: imagePixelRatio); + return _registeredCustomIcon!; + } + + Future _unRegisterUnusedCustomImage() async { + if (_registeredCustomIcon != null) { + // Do not unregister marker image if it is still used by some marker. + if (_markers.any((Marker marker) => + marker.options.icon.registeredImageId == + _registeredCustomIcon!.registeredImageId)) { + return; + } + await unregisterImage(_registeredCustomIcon!); + _registeredCustomIcon = null; + } + } + + Future _toggleCustomIcon() async { + assert(_selectedMarker != null, 'No marker selected'); + if (_selectedMarker!.options.icon.registeredImageId == null) { + final ImageDescriptor customMarkerIcon = + await _getOrCreateCustomImageFromAsset(); + await _updateSelectedMarkerWithOptions( + _selectedMarker!.options.copyWith(icon: customMarkerIcon)); + } else { + await _updateSelectedMarkerWithOptions(_selectedMarker!.options + .copyWith(icon: ImageDescriptor.defaultImage)); + await _unRegisterUnusedCustomImage(); + } + } + void _onMarkerClicked(String markerId) { final Marker marker = _markers.firstWhere((Marker marker) => marker.markerId == markerId); @@ -180,6 +230,8 @@ class _MarkersPageState extends ExamplePageState { Expanded( child: GoogleMapsNavigationView( onViewCreated: _onViewCreated, + initialNavigationUIEnabledPreference: + NavigationUIEnabledPreference.disabled, onMarkerClicked: _onMarkerClicked, onMarkerDrag: _onMarkerDrag, onMarkerDragStart: _onMarkerDragStart, @@ -259,6 +311,16 @@ class _MarkersPageState extends ExamplePageState { _selectedMarker == null ? null : () => _setZIndex(), child: Text('Z-index: ${_selectedMarker?.options.zIndex}'), ), + ElevatedButton( + style: style, + onPressed: _selectedMarker == null + ? null + : _selectedMarker == null + ? null + : () => _toggleCustomIcon(), + child: Text( + 'Icon: ${_selectedMarker?.options.icon.registeredImageId != null ? 'Custom' : 'Default'}'), + ), ElevatedButton( style: style, onPressed: _markers.isNotEmpty @@ -277,7 +339,7 @@ class _MarkersPageState extends ExamplePageState { SwitchListTile( onChanged: (bool newValue) async { await _navigationViewController.settings - .enableMapToolbar(enabled: newValue); + .setMapToolbarEnabled(newValue); _isMapToolbarEnabled = await _navigationViewController .settings .isMapToolbarEnabled(); diff --git a/example/lib/pages/navigation.dart b/example/lib/pages/navigation.dart index 634485e..4b89c86 100644 --- a/example/lib/pages/navigation.dart +++ b/example/lib/pages/navigation.dart @@ -13,9 +13,12 @@ // limitations under the License. import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:google_maps_navigation/google_maps_navigation.dart'; + +import '../routes_api/routes_api.dart'; import '../utils/utils.dart'; import '../widgets/widgets.dart'; @@ -41,6 +44,9 @@ enum SimulationState { /// Simulation running. running, + /// Simulation running with outdated route. + runningOutdated, + /// Simulation paused. paused, @@ -57,10 +63,13 @@ class _NavigationPageState extends ExamplePageState { /// Camera location used to initialize the map view on simulator if location /// is not available by the given timeout [_userLocationTimeoutMS]. - final LatLng cameraLocationMIT = - const LatLng(latitude: 42.3601, longitude: -71.094013); + static const LatLng cameraLocationMIT = + LatLng(latitude: 42.3601, longitude: -71.094013); static const int _userLocationTimeoutMS = 1500; + /// Speed multiplier used for simulation. + static const double simulationSpeedMultiplier = 5; + /// Navigation view controller used to interact with the navigation view. GoogleNavigationViewController? _navigationViewController; @@ -71,12 +80,15 @@ class _NavigationPageState extends ExamplePageState { int _remainingDistance = 0; int _onRouteChangedEventCallCount = 0; int _onRoadSnappedLocationUpdatedEventCallCount = 0; + int _onRoadSnappedRawLocationUpdatedEventCallCount = 0; int _onTrafficUpdatedEventCallCount = 0; int _onReroutingEventCallCount = 0; + int _onGpsAvailabilityEventCallCount = 0; int _onArrivalEventCallCount = 0; int _onSpeedingUpdatedEventCallCount = 0; int _onRecenterButtonClickedEventCallCount = 0; int _onRemainingTimeOrDistanceChangedEventCallCount = 0; + int _onNavigationUIEnabledChangedEventCallCount = 0; bool _navigationHeaderEnabled = true; bool _navigationFooterEnabled = true; @@ -85,12 +97,13 @@ class _NavigationPageState extends ExamplePageState { bool _recenterButtonEnabled = true; bool _speedometerEnabled = false; bool _speedLimitIconEnabled = false; - bool _indicentCardsEnabled = false; + bool _trafficIndicentCardsEnabled = false; bool _termsAndConditionsAccepted = false; bool _locationPermissionsAccepted = false; bool _validRoute = false; + bool _errorOnSetDestinations = false; bool _navigatorInitialized = false; bool _guidanceRunning = false; bool _showRemainingTimeAndDistanceLabels = false; @@ -98,6 +111,9 @@ class _NavigationPageState extends ExamplePageState { NavigationTravelMode _travelMode = NavigationTravelMode.driving; final List _waypoints = []; + /// If true, route tokens and Routes API are used to calculate the route. + bool _routeTokensEnabled = false; + /// Used to track if navigator has been initialized at least once. /// In this example app navigator can be cleaned up and re-initialized. /// This variable is used to make sure that navigator is initialized before @@ -108,12 +124,15 @@ class _NavigationPageState extends ExamplePageState { StreamSubscription? _speedUpdatedSubscription; StreamSubscription? _onArrivalSubscription; StreamSubscription? _onReRoutingSubscription; + StreamSubscription? _onGpsAvailabilitySubscription; StreamSubscription? _trafficUpdatedSubscription; StreamSubscription? _onRouteChangedSubscription; StreamSubscription? _remainingTimeOrDistanceChangedSubscription; StreamSubscription? _roadSnappedLocationUpdatedSubscription; + StreamSubscription? + _roadSnappedRawLocationUpdatedSubscription; int _nextWaypointIndex = 0; @@ -127,6 +146,7 @@ class _NavigationPageState extends ExamplePageState { void dispose() { _clearListeners(); GoogleMapsNavigator.cleanup(); + clearRegisteredImages(); super.dispose(); } @@ -144,6 +164,21 @@ class _NavigationPageState extends ExamplePageState { } } + Future _setRouteTokensEnabled(bool value) async { + setState(() { + // Route tokens are only supported for the driving mode in this example app. + _travelMode = NavigationTravelMode.driving; + _validRoute = false; + _routeTokensEnabled = value; + }); + final bool success = await _updateNavigationDestinations(); + if (success) { + setState(() { + _validRoute = true; + }); + } + } + Future _initializeNavigator() async { assert(_termsAndConditionsAccepted, 'Terms must be accepted'); assert( @@ -166,11 +201,11 @@ class _NavigationPageState extends ExamplePageState { /// location is not available after timeout. Future _setDefaultUserLocationAfterDelay() async { Future.delayed(const Duration(milliseconds: _userLocationTimeoutMS), - () { + () async { if (mounted && _userLocation == null) { - setState(() { - _userLocation = cameraLocationMIT; - }); + _userLocation = await _navigationViewController?.getMyLocation() ?? + cameraLocationMIT; + setState(() {}); } }); } @@ -247,6 +282,9 @@ class _NavigationPageState extends ExamplePageState { GoogleMapsNavigator.setOnArrivalListener(_onArrivalEvent); _onReRoutingSubscription = GoogleMapsNavigator.setOnReroutingListener(_onReroutingEvent); + _onGpsAvailabilitySubscription = + await GoogleMapsNavigator.setOnGpsAvailabilityListener( + _onGpsAvailabilityEvent); _trafficUpdatedSubscription = GoogleMapsNavigator.setTrafficUpdatedListener(_onTrafficUpdatedEvent); _onRouteChangedSubscription = @@ -259,6 +297,9 @@ class _NavigationPageState extends ExamplePageState { _roadSnappedLocationUpdatedSubscription = await GoogleMapsNavigator.setRoadSnappedLocationUpdatedListener( _onRoadSnappedLocationUpdatedEvent); + _roadSnappedRawLocationUpdatedSubscription = + await GoogleMapsNavigator.setRoadSnappedRawLocationUpdatedListener( + _onRoadSnappedRawLocationUpdatedEvent); } void _clearListeners() { @@ -271,6 +312,9 @@ class _NavigationPageState extends ExamplePageState { _onReRoutingSubscription?.cancel(); _onReRoutingSubscription = null; + _onGpsAvailabilitySubscription?.cancel(); + _onGpsAvailabilitySubscription = null; + _trafficUpdatedSubscription?.cancel(); _trafficUpdatedSubscription = null; @@ -282,6 +326,9 @@ class _NavigationPageState extends ExamplePageState { _roadSnappedLocationUpdatedSubscription?.cancel(); _roadSnappedLocationUpdatedSubscription = null; + + _roadSnappedRawLocationUpdatedSubscription?.cancel(); + _roadSnappedRawLocationUpdatedSubscription = null; } void _onRoadSnappedLocationUpdatedEvent( @@ -289,12 +336,26 @@ class _NavigationPageState extends ExamplePageState { if (!mounted) { return; } + setState(() { _userLocation = event.location; _onRoadSnappedLocationUpdatedEventCallCount += 1; }); } + // Note: Raw location updates are not available on iOS. + void _onRoadSnappedRawLocationUpdatedEvent( + RoadSnappedRawLocationUpdatedEvent event) { + if (!mounted) { + return; + } + + setState(() { + _userLocation = event.location; + _onRoadSnappedRawLocationUpdatedEventCallCount += 1; + }); + } + void _onRemainingTimeOrDistanceChangedEvent( RemainingTimeOrDistanceChangedEvent event) { if (!mounted) { @@ -311,11 +372,8 @@ class _NavigationPageState extends ExamplePageState { if (!mounted) { return; } - if (_simulationState == SimulationState.running || - _simulationState == SimulationState.paused || - _simulationState == SimulationState.unknown) { - debugPrint('Route changed, start simulating the new route.'); - _startSimulation(resetSimulationLocation: false); + if (_simulationState == SimulationState.running) { + _simulationState = SimulationState.runningOutdated; } setState(() { _onRouteChangedEventCallCount += 1; @@ -334,6 +392,12 @@ class _NavigationPageState extends ExamplePageState { }); } + void _onGpsAvailabilityEvent(GpsAvailabilityUpdatedEvent event) { + setState(() { + _onGpsAvailabilityEventCallCount += 1; + }); + } + void _onArrivalEvent( OnArrivalEvent event, ) { @@ -361,7 +425,7 @@ class _NavigationPageState extends ExamplePageState { setState(() { _navigationViewController = controller; }); - await controller.enableMyLocation(enabled: true); + await controller.setMyLocationEnabled(true); if (_guidanceRunning) { // Guidance is running, enable navigation UI. @@ -388,8 +452,8 @@ class _NavigationPageState extends ExamplePageState { await _navigationViewController!.isSpeedometerEnabled(); final bool speedLimitIconEnabled = await _navigationViewController!.isSpeedLimitIconEnabled(); - final bool indicentCardsEnabled = - await _navigationViewController!.isIncidentCardsEnabled(); + final bool trafficIndicentCardsEnabled = + await _navigationViewController!.isTrafficIncidentCardsEnabled(); setState(() { _navigationHeaderEnabled = navigationHeaderEnabled; @@ -399,7 +463,7 @@ class _NavigationPageState extends ExamplePageState { _recenterButtonEnabled = recenterButtonEnabled; _speedometerEnabled = speedometerEnabled; _speedLimitIconEnabled = speedLimitIconEnabled; - _indicentCardsEnabled = indicentCardsEnabled; + _trafficIndicentCardsEnabled = trafficIndicentCardsEnabled; }); } } @@ -411,12 +475,19 @@ class _NavigationPageState extends ExamplePageState { }); } + void _onNavigationUIEnabledChanged(bool enabled) { + setState(() { + _navigationUIEnabled = enabled; + _onNavigationUIEnabledChangedEventCallCount += 1; + }); + } + Future _startGuidedNavigation() async { assert(_navigationViewController != null); if (!_navigatorInitialized) { await _initializeNavigator(); } - await _navigationViewController?.enableNavigationUI(enabled: true); + await _navigationViewController?.setNavigationUIEnabled(true); await _startGuidance(); await _navigationViewController?.followMyLocation(CameraPerspective.tilted); } @@ -427,6 +498,8 @@ class _NavigationPageState extends ExamplePageState { // Cleanup navigation session. // This will also clear destinations, stop simulation, stop guidance await GoogleMapsNavigator.cleanup(); + await _removeNewWaypointMarker(); + await _removeDestinationWaypointMarkers(); _waypoints.clear(); // Reset navigation perspective to top down north up. @@ -438,7 +511,7 @@ class _NavigationPageState extends ExamplePageState { // disabled right after cleanup. unawaited(Future.delayed( const Duration(milliseconds: _disableNavigationUIDelay), () async { - await _navigationViewController!.enableNavigationUI(enabled: false); + await _navigationViewController!.setNavigationUIEnabled(false); })); // Make sure that navigation initialization state is up-to-date. @@ -449,20 +522,24 @@ class _NavigationPageState extends ExamplePageState { _validRoute = false; _guidanceRunning = false; _simulationState = SimulationState.notRunning; + _nextWaypointIndex = 0; + _remainingDistance = 0; + _remainingTime = 0; }); } Marker? _newWaypointMarker; + final List _destinationWaypointMarkers = []; - MarkerOptions _buildMarkerOptions(LatLng target) { + MarkerOptions _buildNewWaypointMarkerOptions(LatLng target) { return MarkerOptions( infoWindow: const InfoWindow(title: 'Destination'), position: LatLng(latitude: target.latitude, longitude: target.longitude)); } - Future _updateWaypointMarker(LatLng target) async { - final MarkerOptions markerOptions = _buildMarkerOptions(target); + Future _updateNewWaypointMarker(LatLng target) async { + final MarkerOptions markerOptions = _buildNewWaypointMarkerOptions(target); if (_newWaypointMarker == null) { // Add new marker. final List addedMarkers = await _navigationViewController! @@ -487,7 +564,7 @@ class _NavigationPageState extends ExamplePageState { setState(() {}); } - Future _removeWaypointMarker() async { + Future _removeNewWaypointMarker() async { if (_newWaypointMarker != null) { await _navigationViewController! .removeMarkers([_newWaypointMarker!]); @@ -496,8 +573,20 @@ class _NavigationPageState extends ExamplePageState { } } + Future _removeDestinationWaypointMarkers() async { + if (_destinationWaypointMarkers.isNotEmpty) { + await _navigationViewController! + .removeMarkers(_destinationWaypointMarkers); + _destinationWaypointMarkers.clear(); + + // Unregister custom marker images + await clearRegisteredImages(); + setState(() {}); + } + } + Future _onMapClicked(LatLng location) async { - await _updateWaypointMarker(location); + await _updateNewWaypointMarker(location); } Future _addWaypoint() async { @@ -513,23 +602,48 @@ class _NavigationPageState extends ExamplePageState { longitude: _newWaypointMarker!.options.position.longitude, ), )); - await _removeWaypointMarker(); - unawaited(showCalculatingRouteMessage()); - final bool success = await _updateNavigationDestinations(); - if (success) { - await _navigationViewController!.enableNavigationUI(enabled: true); + // Convert new waypoint marker to destination marker. + await _convertNewWaypointMarkerToDestinationMarker(_nextWaypointIndex); + await _updateNavigationDestinationsAndNavigationViewState(); + } + setState(() {}); + } - if (!_guidanceRunning) { - await _navigationViewController!.showRouteOverview(); - } + /// Helper method that first updates destinations and then + /// updates navigation view state to show the route overview. + Future _updateNavigationDestinationsAndNavigationViewState() async { + final bool success = await _updateNavigationDestinations(); + if (success) { + await _navigationViewController!.setNavigationUIEnabled(true); - setState(() { - _validRoute = true; - }); + if (!_guidanceRunning) { + await _navigationViewController!.showRouteOverview(); } + setState(() { + _validRoute = true; + }); } - setState(() {}); + } + + Future _convertNewWaypointMarkerToDestinationMarker( + final int index) async { + final String title = 'Waypoint $index'; + final ImageDescriptor waypointMarkerImage = + await registerWaypointMarkerImage( + index, MediaQuery.of(context).devicePixelRatio); + final List destinationMarkers = + await _navigationViewController!.updateMarkers([ + _newWaypointMarker!.copyWith( + options: _newWaypointMarker!.options.copyWith( + infoWindow: InfoWindow(title: title), + anchor: const MarkerAnchor(u: 0.5, v: 1.2), + icon: waypointMarkerImage, + ), + ) + ]); + _destinationWaypointMarkers.add(destinationMarkers.first!); + _newWaypointMarker = null; } Future showCalculatingRouteMessage() async { @@ -539,13 +653,31 @@ class _NavigationPageState extends ExamplePageState { } } + /// This method is called by the _onArrivalEvent event handler when the user + /// has arrived to a waypoint. Future _arrivedToWaypoint(NavigationWaypoint waypoint) async { - // Remove the arrived waypoint from the list. - _waypoints.removeWhere((NavigationWaypoint w) => w.title == waypoint.title); + debugPrint('Arrived to waypoint: ${waypoint.title}'); + + // Remove the first waypoint from the list. + if (_waypoints.isNotEmpty) { + _waypoints.removeAt(0); + } + // Remove the first destination marker from the list. + if (_destinationWaypointMarkers.isNotEmpty) { + final Marker markerToRemove = _destinationWaypointMarkers.first; + await _navigationViewController!.removeMarkers([markerToRemove]); + + // Unregister custom marker image. + await unregisterImage(markerToRemove.options.icon); + + _destinationWaypointMarkers.removeAt(0); + } await GoogleMapsNavigator.continueToNextDestination(); if (_waypoints.isEmpty) { + debugPrint('Arrived to last waypoint, stopping navigation.'); + // If there is no next waypoint, it means we have arrived at the last // destination. Hence, stop navigation. await _stopGuidedNavigation(); @@ -555,6 +687,7 @@ class _NavigationPageState extends ExamplePageState { } Future _clearNavigationWaypoints() async { + // Stopping guided navigation will also clear the waypoints. await _stopGuidedNavigation(); setState(() { _waypoints.clear(); @@ -570,58 +703,139 @@ class _NavigationPageState extends ExamplePageState { await _initializeNavigator(); } - final Destinations msg = Destinations( + // If route tokens are enabled, build destinations with route tokens. + final Destinations? destinations = _routeTokensEnabled + ? (await _buildDestinationsWithRoutesApi()) + : _buildDestinations(); + + if (destinations == null) { + // Failed to build destinations. + // This can happen if route tokens are enabled and route token could + // not be fetched. + setState(() { + _errorOnSetDestinations = true; + }); + return false; + } + + try { + final NavigationRouteStatus navRouteStatus = + await GoogleMapsNavigator.setDestinations(destinations); + + switch (navRouteStatus) { + case NavigationRouteStatus.statusOk: + // Route is valid. Return true as success. + setState(() { + _errorOnSetDestinations = false; + }); + return true; + case NavigationRouteStatus.internalError: + showMessage( + 'Unexpected internal error occured. Please restart the app.'); + case NavigationRouteStatus.routeNotFound: + showMessage('The route could not be calculated.'); + case NavigationRouteStatus.networkError: + showMessage( + 'Working network connection is required to calculate the route.'); + case NavigationRouteStatus.quotaExceeded: + showMessage('Insufficient API quota to use the navigation.'); + case NavigationRouteStatus.quotaCheckFailed: + showMessage( + 'API quota check failed, cannot authorize the navigation.'); + case NavigationRouteStatus.apiKeyNotAuthorized: + showMessage('A valid API key is required to use the navigation.'); + case NavigationRouteStatus.statusCanceled: + showMessage( + 'The route calculation was canceled in favor of a newer one.'); + case NavigationRouteStatus.duplicateWaypointsError: + showMessage( + 'The route could not be calculated because of duplicate waypoints.'); + case NavigationRouteStatus.noWaypointsError: + showMessage( + 'The route could not be calculated because no waypoints were provided.'); + case NavigationRouteStatus.locationUnavailable: + showMessage( + 'No user location is available. Did you allow location permission?'); + case NavigationRouteStatus.waypointError: + showMessage('Invalid waypoints provided.'); + case NavigationRouteStatus.travelModeUnsupported: + showMessage( + 'The route could not calculated for the given travel mode.'); + case NavigationRouteStatus.unknown: + showMessage( + 'The route could not be calculated due to an unknown error.'); + case NavigationRouteStatus.locationUnknown: + showMessage( + 'The route could not be calculated, because the user location is unknown.'); + } + } on RouteTokenMalformedException catch (_) { + showMessage('Malformed route token'); + } on SessionNotInitializedException catch (_) { + showMessage('Cannot set destinations, session not initialized'); + } + setState(() { + _errorOnSetDestinations = true; + }); + return false; + } + + /// Helper function to retry setting navigation settings if there was an error + /// on the previous attempt. Sometimes the error is transient and retrying + /// the operation can succeed, for example in situations where device location + /// is not yet available. + Future _retryToUpdateNavigationDestinations() async { + setState(() { + _errorOnSetDestinations = false; + }); + await _updateNavigationDestinationsAndNavigationViewState(); + } + + Destinations? _buildDestinations() { + // Show delayed calculating route message. + unawaited(showCalculatingRouteMessage()); + + return Destinations( waypoints: _waypoints, displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), routingOptions: RoutingOptions(travelMode: _travelMode), ); + } + + Future _buildDestinationsWithRoutesApi() async { + assert(_routeTokensEnabled); - final NavigationRouteStatus navRouteStatus = - await GoogleMapsNavigator.setDestinations(msg); - - switch (navRouteStatus) { - case NavigationRouteStatus.statusOk: - // Route is valid. Return true as success. - return true; - case NavigationRouteStatus.internalError: - showMessage( - 'Unexpected internal error occured. Please restart the app.'); - case NavigationRouteStatus.routeNotFound: - showMessage('The route could not be calculated.'); - case NavigationRouteStatus.networkError: - showMessage( - 'Working network connection is required to calculate the route.'); - case NavigationRouteStatus.quotaExceeded: - showMessage('Insufficient API quota to use the navigation.'); - case NavigationRouteStatus.quotaCheckFailed: - showMessage('API quota check failed, cannot authorize the navigation.'); - case NavigationRouteStatus.apiKeyNotAuthorized: - showMessage('A valid API key is required to use the navigation.'); - case NavigationRouteStatus.statusCanceled: - showMessage( - 'The route calculation was canceled in favor of a newer one.'); - case NavigationRouteStatus.duplicateWaypointsError: - showMessage( - 'The route could not be calculated because of duplicate waypoints.'); - case NavigationRouteStatus.noWaypointsError: - showMessage( - 'The route could not be calculated because no waypoints were provided.'); - case NavigationRouteStatus.locationUnavailable: - showMessage( - 'No user location is available. Did you allow location permission?'); - case NavigationRouteStatus.waypointError: - showMessage('Invalid waypoints provided.'); - case NavigationRouteStatus.travelModeUnsupported: - showMessage( - 'The route could not calculated for the given travel mode.'); - case NavigationRouteStatus.unknown: - showMessage( - 'The route could not be calculated due to an unknown error.'); - case NavigationRouteStatus.locationUnknown: - showMessage( - 'The route could not be calculated, because the user location is unknown.'); + showMessage('Using route token from Routes API.'); + + List routeTokens = []; + try { + routeTokens = await getRouteToken( + [ + // Add users location as start location for getting routetoken. + NavigationWaypoint.withLatLngTarget( + title: 'Origin', target: _userLocation), + ..._waypoints, + ], + ); + } catch (e) { + showMessage('Failed to get route tokens from Routes API. $e'); + return null; } - return false; + + if (routeTokens.isEmpty) { + showMessage('Failed to get route tokens from Routes API.'); + return null; + } else if (routeTokens.length > 1) { + showMessage( + 'More than one route token received from Routes API. Using the first one.'); + } + + return Destinations( + waypoints: _waypoints, + displayOptions: NavigationDisplayOptions(showDestinationMarkers: false), + routeTokenOptions: RouteTokenOptions( + routeToken: routeTokens.first, // Uses first fetched route token. + travelMode: _travelMode, + )); } Future _startGuidance() async { @@ -646,18 +860,17 @@ class _NavigationPageState extends ExamplePageState { } } - Future _startSimulation({bool resetSimulationLocation = true}) async { + Future _startSimulation() async { if (_waypoints.isNotEmpty) { - if (resetSimulationLocation) { - final LatLng? myLocation = - await _navigationViewController!.getMyLocation(); - if (myLocation != null) { - await GoogleMapsNavigator.simulator.setUserLocation(myLocation); - } + final LatLng? myLocation = + _userLocation ?? await _navigationViewController!.getMyLocation(); + if (myLocation != null) { + await GoogleMapsNavigator.simulator.setUserLocation(myLocation); } + await GoogleMapsNavigator.simulator .simulateLocationsAlongExistingRouteWithOptions( - SimulationOptions(speedMultiplier: 5), + SimulationOptions(speedMultiplier: simulationSpeedMultiplier), ); setState(() { @@ -721,34 +934,38 @@ class _NavigationPageState extends ExamplePageState { Column(children: [ _travelModeSelection, Expanded( - child: - _navigatorInitializedAtLeastOnce && _userLocation != null - ? GoogleMapsNavigationView( - onViewCreated: _onViewCreated, - onMapClicked: _onMapClicked, - onMapLongClicked: _onMapClicked, - onRecenterButtonClicked: - _onRecenterButtonClickedEvent, - initialCameraPosition: CameraPosition( - // Initialize map to user location. - target: _userLocation!, - zoom: 15, - ), - initialNavigationUiEnabled: _guidanceRunning, - ) - : const Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('Waiting navigator and user location'), - SizedBox(height: 10), - SizedBox( - width: 30, - height: 30, - child: CircularProgressIndicator()) - ], - ), - ), + child: _navigatorInitializedAtLeastOnce && + _userLocation != null + ? GoogleMapsNavigationView( + onViewCreated: _onViewCreated, + onMapClicked: _onMapClicked, + onMapLongClicked: _onMapClicked, + onRecenterButtonClicked: + _onRecenterButtonClickedEvent, + onNavigationUIEnabledChanged: + _onNavigationUIEnabledChanged, + initialCameraPosition: CameraPosition( + // Initialize map to user location. + target: _userLocation!, + zoom: 15, + ), + initialNavigationUIEnabledPreference: _guidanceRunning + ? NavigationUIEnabledPreference.automatic + : NavigationUIEnabledPreference.disabled, + ) + : const Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('Waiting navigator and user location'), + SizedBox(height: 10), + SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator()) + ], + ), + ), ), if (_navigationViewController != null) bottomControls ]), @@ -778,6 +995,25 @@ class _NavigationPageState extends ExamplePageState { padding: const EdgeInsets.all(15), child: Column( children: [ + if (_errorOnSetDestinations && _waypoints.isNotEmpty) ...[ + const Text('Error while setting destinations'), + ElevatedButton( + onPressed: _retryToUpdateNavigationDestinations, + child: const Text('Retry'), + ), + ], + if (_guidanceRunning && + _simulationState == SimulationState.runningOutdated) + Wrap( + alignment: WrapAlignment.center, + spacing: 10, + children: [ + const Text('Simulation is running with outdated route'), + ElevatedButton( + onPressed: () => _startSimulation(), + child: const Text('Update simulation'), + ), + ]), if (_waypoints.isNotEmpty) Wrap( alignment: WrapAlignment.center, @@ -802,12 +1038,12 @@ class _NavigationPageState extends ExamplePageState { if (_guidanceRunning && _simulationState == SimulationState.unknown) ElevatedButton( - onPressed: () => - _startSimulation(resetSimulationLocation: false), + onPressed: () => _startSimulation(), child: const Text('Resume simulation state'), ), if (_guidanceRunning && (_simulationState == SimulationState.running || + _simulationState == SimulationState.runningOutdated || _simulationState == SimulationState.paused)) ElevatedButton( onPressed: () => _stopSimulation(), @@ -888,6 +1124,15 @@ class _NavigationPageState extends ExamplePageState { trailing: Text( _onRoadSnappedLocationUpdatedEventCallCount.toString()), )), + if (Platform.isAndroid) + Card( + child: ListTile( + title: const Text( + 'On road snapped raw location updated event call count'), + trailing: Text( + _onRoadSnappedRawLocationUpdatedEventCallCount + .toString()), + )), Card( child: ListTile( title: const Text('On traffic updated event call count'), @@ -898,6 +1143,12 @@ class _NavigationPageState extends ExamplePageState { title: const Text('On rerouting event call count'), trailing: Text(_onReroutingEventCallCount.toString()), )), + if (Platform.isAndroid) + Card( + child: ListTile( + title: const Text('On GPS availability event call count'), + trailing: Text(_onGpsAvailabilityEventCallCount.toString()), + )), Card( child: ListTile( title: const Text('On arrival event call count'), @@ -922,6 +1173,13 @@ class _NavigationPageState extends ExamplePageState { trailing: Text(_onRemainingTimeOrDistanceChangedEventCallCount .toString()), )), + Card( + child: ListTile( + title: const Text( + 'On navigation UI enabled changed event call count'), + trailing: Text( + _onNavigationUIEnabledChangedEventCallCount.toString()), + )), ], )); }, @@ -1009,6 +1267,21 @@ class _NavigationPageState extends ExamplePageState { : null, child: const Text('Display travelled route'), ), + ExampleSwitch( + title: 'Show remaining time and distance', + initialValue: _showRemainingTimeAndDistanceLabels, + onChanged: (bool newValue) async { + setState(() { + _showRemainingTimeAndDistanceLabels = newValue; + }); + }), + ExampleSwitch( + title: 'Use route tokens', + initialValue: _routeTokensEnabled, + onChanged: _guidanceRunning + ? null + : (bool value) => _setRouteTokensEnabled(value), + ), ], ), const SizedBox(height: 10) @@ -1056,20 +1329,12 @@ class _NavigationPageState extends ExamplePageState { !_navigatorInitialized || _navigationViewController == null), children: [ - ExampleSwitch( - title: 'Show remaining time and distance', - initialValue: _showRemainingTimeAndDistanceLabels, - onChanged: (bool newValue) async { - setState(() { - _showRemainingTimeAndDistanceLabels = newValue; - }); - }), ExampleSwitch( title: 'Enable guidance header', initialValue: _navigationHeaderEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableNavigationHeader(enabled: newValue); + .setNavigationHeaderEnabled(newValue); setState(() { _navigationHeaderEnabled = newValue; }); @@ -1079,7 +1344,7 @@ class _NavigationPageState extends ExamplePageState { initialValue: _navigationFooterEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableNavigationFooter(enabled: newValue); + .setNavigationFooterEnabled(newValue); setState(() { _navigationFooterEnabled = newValue; }); @@ -1089,7 +1354,7 @@ class _NavigationPageState extends ExamplePageState { initialValue: _navigationTripProgressBarEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableNavigationTripProgressBar(enabled: newValue); + .setNavigationTripProgressBarEnabled(newValue); setState(() { _navigationTripProgressBarEnabled = newValue; }); @@ -1099,7 +1364,7 @@ class _NavigationPageState extends ExamplePageState { initialValue: _navigationUIEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableNavigationUI(enabled: newValue); + .setNavigationUIEnabled(newValue); setState(() { _navigationUIEnabled = newValue; }); @@ -1109,7 +1374,7 @@ class _NavigationPageState extends ExamplePageState { initialValue: _recenterButtonEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableRecenterButton(enabled: newValue); + .setRecenterButtonEnabled(newValue); setState(() { _recenterButtonEnabled = newValue; }); @@ -1119,7 +1384,7 @@ class _NavigationPageState extends ExamplePageState { initialValue: _speedLimitIconEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableSpeedLimitIcon(enable: newValue); + .setSpeedLimitIconEnabled(newValue); setState(() { _speedLimitIconEnabled = newValue; }); @@ -1129,19 +1394,19 @@ class _NavigationPageState extends ExamplePageState { initialValue: _speedometerEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableSpeedometer(enable: newValue); + .setSpeedometerEnabled(newValue); setState(() { _speedometerEnabled = newValue; }); }), ExampleSwitch( - title: 'Display incident cards', - initialValue: _indicentCardsEnabled, + title: 'Display traffic incident cards', + initialValue: _trafficIndicentCardsEnabled, onChanged: (bool newValue) async { await _navigationViewController! - .enableIncidentCards(enable: newValue); + .setTrafficIncidentCardsEnabled(newValue); setState(() { - _indicentCardsEnabled = newValue; + _trafficIndicentCardsEnabled = newValue; }); }), ]), @@ -1196,8 +1461,10 @@ class _NavigationPageState extends ExamplePageState { Widget _buildTravelModeChoice(NavigationTravelMode mode, IconData icon) { final bool isSelected = mode == _travelMode; + final bool enabled = + !_routeTokensEnabled || mode == NavigationTravelMode.driving; return InkWell( - onTap: () => _changeTravelMode(mode), + onTap: enabled ? () => _changeTravelMode(mode) : null, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -1206,9 +1473,11 @@ class _NavigationPageState extends ExamplePageState { child: Icon( icon, size: 30, - color: isSelected - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.secondary, + color: enabled + ? (isSelected + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.secondary) + : Theme.of(context).colorScheme.secondary.withAlpha(128), )), if (isSelected) Container( diff --git a/example/lib/pages/navigation_without_map.dart b/example/lib/pages/navigation_without_map.dart index 625f1a6..a62fcf9 100644 --- a/example/lib/pages/navigation_without_map.dart +++ b/example/lib/pages/navigation_without_map.dart @@ -38,24 +38,15 @@ class _NavigationWithoutMapPageState bool sessionInitialized = false; bool routeCalculated = false; bool guidanceRunning = false; - StreamSubscription? - _navigationSessionEventSubscription; @override void initState() { super.initState(); - checkTermsAcceptance(); + unawaited(_initialize()); } - @override - void dispose() { - _navigationSessionEventSubscription?.cancel(); - super.dispose(); - } - - void _onNavigationSessionEvent( - NavigationSessionEvent navigationSessionEvent) { - showMessage(navigationSessionEvent.message); + Future _initialize() async { + await checkTermsAcceptance(); } Future checkTermsAcceptance() async { @@ -91,9 +82,6 @@ class _NavigationWithoutMapPageState Future initializeNavigationSession() async { try { - _navigationSessionEventSubscription = - GoogleMapsNavigator.setNavigationSessionEventListener( - _onNavigationSessionEvent); await GoogleMapsNavigator.initializeNavigationSession(); setState(() { sessionInitialized = true; diff --git a/example/lib/pages/polygons.dart b/example/lib/pages/polygons.dart index 55e1684..6ce07ad 100644 --- a/example/lib/pages/polygons.dart +++ b/example/lib/pages/polygons.dart @@ -261,6 +261,8 @@ class _PolygonsPageState extends ExamplePageState { initialCameraPosition: const CameraPosition( target: LatLng(latitude: 37.422, longitude: -122.084), zoom: 12), + initialNavigationUIEnabledPreference: + NavigationUIEnabledPreference.disabled, onViewCreated: _onViewCreated, onPolygonClicked: _onPolygonClicked, )), diff --git a/example/lib/pages/polylines.dart b/example/lib/pages/polylines.dart index 04be1ac..bd244de 100644 --- a/example/lib/pages/polylines.dart +++ b/example/lib/pages/polylines.dart @@ -215,6 +215,8 @@ class _PolylinesPageState extends ExamplePageState { initialCameraPosition: const CameraPosition( target: LatLng(latitude: 37.422, longitude: -122.084), zoom: 12), + initialNavigationUIEnabledPreference: + NavigationUIEnabledPreference.disabled, onViewCreated: _onViewCreated, onPolylineClicked: _onPolylineClicked, )), diff --git a/example/lib/pages/widget_initialization.dart b/example/lib/pages/widget_initialization.dart index e527cc0..0172f5b 100644 --- a/example/lib/pages/widget_initialization.dart +++ b/example/lib/pages/widget_initialization.dart @@ -49,13 +49,14 @@ class _ViewInitializationPageState double? _initialMaxZoomPreference; bool _initialZoomControlsEnabled = true; LatLngBounds? _initialCameraTargetBounds; - bool _initialNavigationUiEnabled = false; + NavigationUIEnabledPreference _initialNavigationUIEnabledPreference = + NavigationUIEnabledPreference.automatic; TextDirection? _layoutDirection; bool _isMinZoomPreferenceEnabled = false; bool _isMaxZoomPreferenceEnabled = false; /// Navigation state - bool navigationSessionHasBeenInitialized = false; + bool _navigationInitialized = false; @override void initState() { @@ -63,9 +64,16 @@ class _ViewInitializationPageState _updateNavigationInitializationState(); } + @override + void dispose() { + if (_navigationInitialized) { + GoogleMapsNavigator.cleanup(); + } + super.dispose(); + } + Future _updateNavigationInitializationState() async { - navigationSessionHasBeenInitialized = - await GoogleMapsNavigator.isInitialized(); + _navigationInitialized = await GoogleMapsNavigator.isInitialized(); setState(() {}); } @@ -99,7 +107,8 @@ class _ViewInitializationPageState initialMaxZoomPreference: _initialMaxZoomPreference, initialZoomControlsEnabled: _initialZoomControlsEnabled, initialCameraTargetBounds: _initialCameraTargetBounds, - initialNavigationUiEnabled: _initialNavigationUiEnabled, + initialNavigationUIEnabledPreference: + _initialNavigationUIEnabledPreference, layoutDirection: _layoutDirection, ), ), @@ -114,6 +123,17 @@ class _ViewInitializationPageState return max(_initialMinZoomPreference ?? googleMapsMinZoomLevel, value); } + Future _startNavigation() async { + if (!await GoogleMapsNavigator.areTermsAccepted()) { + await GoogleMapsNavigator.showTermsAndConditionsDialog( + 'test_title', + 'test_company_name', + ); + } + await GoogleMapsNavigator.initializeNavigationSession(); + await _updateNavigationInitializationState(); + } + @override Widget build(BuildContext context) => buildPage( context, @@ -326,13 +346,19 @@ class _ViewInitializationPageState style: TextStyle(fontWeight: FontWeight.bold), ), children: [ - ExampleDropdownButton( - title: 'Navigation UI Enabled', - value: _initialNavigationUiEnabled, - items: const [true, false], - onChanged: (bool? newValue) { + ExampleDropdownButton( + title: 'Navigation UI Enabled preference', + value: _initialNavigationUIEnabledPreference, + items: const [ + NavigationUIEnabledPreference.automatic, + NavigationUIEnabledPreference.disabled + ], + onChanged: + (NavigationUIEnabledPreference? newValue) { setState(() { - _initialNavigationUiEnabled = newValue ?? false; + _initialNavigationUIEnabledPreference = + newValue ?? + NavigationUIEnabledPreference.automatic; }); }, ), @@ -345,15 +371,12 @@ class _ViewInitializationPageState alignment: WrapAlignment.center, spacing: 10, children: [ - if (!navigationSessionHasBeenInitialized) - ElevatedButton( - onPressed: !navigationSessionHasBeenInitialized - ? GoogleMapsNavigator.initializeNavigationSession - : _updateNavigationInitializationState, - child: const Text('Init navigation'), - ), - if (navigationSessionHasBeenInitialized) - const Text('Navigation initialized'), + ElevatedButton( + onPressed: !_navigationInitialized ? _startNavigation : null, + child: Text(_navigationInitialized + ? 'Initialized' + : 'Init navigation'), + ), ElevatedButton( onPressed: () => _changePage(), child: const Text('Open map'), @@ -378,7 +401,7 @@ class _InitializedViewPage extends StatelessWidget { required this.initialMaxZoomPreference, required this.initialZoomControlsEnabled, required this.initialCameraTargetBounds, - required this.initialNavigationUiEnabled, + required this.initialNavigationUIEnabledPreference, required this.layoutDirection, }); @@ -396,7 +419,7 @@ class _InitializedViewPage extends StatelessWidget { final double? initialMaxZoomPreference; final bool initialZoomControlsEnabled; final LatLngBounds? initialCameraTargetBounds; - final bool initialNavigationUiEnabled; + final NavigationUIEnabledPreference initialNavigationUIEnabledPreference; @override Widget build(BuildContext context) { @@ -427,7 +450,8 @@ class _InitializedViewPage extends StatelessWidget { initialMaxZoomPreference: initialMaxZoomPreference, initialZoomControlsEnabled: initialZoomControlsEnabled, initialCameraTargetBounds: initialCameraTargetBounds, - initialNavigationUiEnabled: initialNavigationUiEnabled, + initialNavigationUIEnabledPreference: + initialNavigationUIEnabledPreference, ), ); } diff --git a/example/lib/routes_api/routes_api.dart b/example/lib/routes_api/routes_api.dart new file mode 100644 index 0000000..1c1fcb0 --- /dev/null +++ b/example/lib/routes_api/routes_api.dart @@ -0,0 +1,105 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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. + +import 'dart:convert'; +import 'package:google_maps_navigation/google_maps_navigation.dart'; +import 'package:http/http.dart' as http; + +// Note: This Routes API implementation is ment to be used only to +// support the example app, and only includes the bare minimum to get +// the route tokens. + +const String _routesApiUrl = 'https://routes.googleapis.com/'; +const String _computeRoutesUrl = '$_routesApiUrl/directions/v2:computeRoutes'; +const String _mapsApiKey = String.fromEnvironment('MAPS_API_KEY'); + +/// Queries the Google Maps Routes API and returns a list of route tokens. +/// +/// [waypoints] is a list of [NavigationWaypoint] representing the route waypoints. +/// [travelMode] is a string representing the travel mode. +/// Returns a list of route tokens or throws an error if the request fails. +Future> getRouteToken(List waypoints) async { + assert(_mapsApiKey.isNotEmpty, + 'MAPS_API_KEY is not provided. Please pass it as a Dart define during the app build.'); + assert(waypoints.length >= 2, + 'At least two waypoints (origin and destination) are required.'); + + final Uri apiUrl = Uri.parse(_computeRoutesUrl); + + final Map requestBody = { + 'origin': _toRoutesApiWaypoint(waypoints.first), + 'destination': _toRoutesApiWaypoint(waypoints.last), + 'intermediates': waypoints + .sublist(1, waypoints.length - 1) + .map((NavigationWaypoint wp) => _toRoutesApiWaypoint(wp, via: true)) + .toList(), + 'travelMode': 'DRIVE', + 'routingPreference': 'TRAFFIC_AWARE', + }; + + final Map headers = { + 'X-Goog-Api-Key': _mapsApiKey, + 'X-Goog-Fieldmask': 'routes.routeToken', + 'Content-Type': 'application/json', + }; + + final http.Response response = + await http.post(apiUrl, headers: headers, body: jsonEncode(requestBody)); + + if (response.statusCode == 200) { + final Map responseData = + jsonDecode(response.body) as Map; + final List? routeTokens = responseData['routes'] as List?; + + if (routeTokens == null) { + throw Exception('Failed to get route tokens'); + } + + return routeTokens + .map((dynamic route) => + (route as Map)['routeToken'] as String) + .toList(); + } else { + throw Exception( + 'Failed to get route tokens: ${response.reasonPhrase}:\n${response.body}'); + } +} + +/// Converts a [NavigationWaypoint] to a waypoint request format supported +/// by the Routes API. +Map _toRoutesApiWaypoint(NavigationWaypoint waypoint, + {bool via = false}) { + assert(waypoint.target != null || waypoint.placeID != null, + 'Invalid NavigationWaypoint: Either target or placeID must be provided.'); + final Map output = { + 'via': via, + }; + if (waypoint.placeID != null) { + output['placeId'] = waypoint.placeID; + } else if (waypoint.target != null) { + final Map location = { + 'latLng': { + 'latitude': waypoint.target!.latitude, + 'longitude': waypoint.target!.longitude + } + }; + + if (waypoint.preferredSegmentHeading != null) { + location['heading'] = waypoint.preferredSegmentHeading; + } + + output['location'] = location; + } + return output; +} diff --git a/example/lib/utils/constants.dart b/example/lib/utils/constants.dart new file mode 100644 index 0000000..7170a66 --- /dev/null +++ b/example/lib/utils/constants.dart @@ -0,0 +1,23 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +/// Minimum zoom level to use for zoom options. +/// +/// Effective zoom level can change depending on map type and device used. +const double googleMapsMinZoomLevel = 2; + +/// Maximum zoom level to use for zoom options. +/// +/// Effective zoom level can change depending on map type and device used. +const double googleMapsMaxZoomLevel = 21; diff --git a/example/lib/utils/utils.dart b/example/lib/utils/utils.dart index 90a054b..bdd6abc 100644 --- a/example/lib/utils/utils.dart +++ b/example/lib/utils/utils.dart @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +export 'constants.dart'; export 'location_permissions.dart'; export 'overlay_options.dart'; export 'remaining_distance.dart'; export 'remaining_time.dart'; export 'snackbar.dart'; export 'terms_of_service.dart'; +export 'waypoint_marker.dart'; diff --git a/example/lib/utils/waypoint_marker.dart b/example/lib/utils/waypoint_marker.dart new file mode 100644 index 0000000..b60b56f --- /dev/null +++ b/example/lib/utils/waypoint_marker.dart @@ -0,0 +1,96 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:google_maps_navigation/google_maps_navigation.dart'; + +const Size _markerSize = Size(80, 80); // Example size in logical pixels + +/// Creates a waypoint marker with the given waypoint number and registers it. +/// +/// Returns an [ImageDescriptor] for the registered image. +Future registerWaypointMarkerImage( + int waypointNumber, double imagePixelRatio) async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final Canvas canvas = Canvas(recorder); + final _WaypointMarkerPainter painter = _WaypointMarkerPainter(waypointNumber); + + painter.paint(canvas, _markerSize); + + final ui.Image image = await recorder + .endRecording() + .toImage(_markerSize.width.floor(), _markerSize.height.floor()); + + final ByteData? bytes = + await image.toByteData(format: ui.ImageByteFormat.png); + + // Call registerBitmapImage with ByteData + return registerBitmapImage(bitmap: bytes!, imagePixelRatio: imagePixelRatio); +} + +class _WaypointMarkerPainter extends CustomPainter { + _WaypointMarkerPainter(this.waypointNumber); + final int waypointNumber; + + @override + void paint(Canvas canvas, Size size) { + const double strokeWidth = 6.0; + + // Draw the background circle + final Paint circlePaint = Paint() + ..color = Colors.blue + ..style = PaintingStyle.fill; + final Offset center = Offset(size.width / 2, size.height / 2); + final double radius = size.width / 2 - strokeWidth / 2; + canvas.drawCircle(center, radius, circlePaint); + + // Draw the border + final Paint borderPaint = Paint() + ..color = Colors.blue[800]! + ..style = PaintingStyle.stroke + ..strokeWidth = strokeWidth; + canvas.drawCircle(center, radius, borderPaint); + + // Draw the waypoint number + final TextSpan textSpan = TextSpan( + text: waypointNumber.toString(), + style: const TextStyle( + color: Colors.white, + fontSize: 50, + fontWeight: FontWeight.bold, + ), + ); + final TextPainter textPainter = TextPainter( + text: textSpan, + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + ); + textPainter.layout( + maxWidth: size.width, + ); + final Offset textOffset = Offset( + center.dx - textPainter.width / 2, + center.dy - textPainter.height / 2, + ); + textPainter.paint(canvas, textOffset); + } + + @override + bool shouldRepaint(_WaypointMarkerPainter oldDelegate) => false; + @override + bool shouldRebuildSemantics(_WaypointMarkerPainter oldDelegate) => false; +} diff --git a/example/lib/widgets/camera_position_editor.dart b/example/lib/widgets/camera_position_editor.dart index 788840f..cb11bf9 100644 --- a/example/lib/widgets/camera_position_editor.dart +++ b/example/lib/widgets/camera_position_editor.dart @@ -18,6 +18,7 @@ import 'package:flutter/material.dart'; import 'package:google_maps_navigation/google_maps_navigation.dart'; +import '../utils/utils.dart'; import 'widgets.dart'; /// An editor for [CameraPosition] values. @@ -101,8 +102,8 @@ class _ExampleCameraPositionEditorState _updateCameraPosition(); }, title: 'Zoom', - min: 0, - max: 25, + min: googleMapsMinZoomLevel, + max: googleMapsMaxZoomLevel, ), ExampleSlider( value: cameraPosition.tilt, diff --git a/example/lib/widgets/page.dart b/example/lib/widgets/page.dart index 0725e72..6231101 100644 --- a/example/lib/widgets/page.dart +++ b/example/lib/widgets/page.dart @@ -192,10 +192,11 @@ abstract class ExamplePageState extends State padding: const EdgeInsets.all(16), color: Theme.of(context).snackBarTheme.backgroundColor, child: SafeArea( + top: false, child: Text( - message, - style: Theme.of(context).snackBarTheme.contentTextStyle, - )), + message, + style: Theme.of(context).snackBarTheme.contentTextStyle, + )), ), ), ), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 2053e72..f3767d7 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: sdk: flutter google_maps_navigation: path: ../ + http: ^1.1.2 permission_handler: ^11.1.0 dev_dependencies: @@ -34,7 +35,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - patrol: ">=3.2.0 <3.3.0" + patrol: ">=3.3.0 <3.4.0" pub_semver: ^2.1.4 flutter: @@ -42,6 +43,7 @@ flutter: assets: - assets/night_style.json - assets/sepia_style.json + - assets/marker1.png patrol: app_name: Navigation Example diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 75e2ae2..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 Google LLC -// -// 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 -// -// https://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. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -// ignore: avoid_relative_lib_imports -import '../lib/main.dart'; - -void main() { - testWidgets('verify that example is rendered', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MaterialApp(home: NavigationDemo())); - - // Verify that example is rendered. - expect( - find.byWidgetPredicate( - (Widget widget) => - widget is Text && - widget.data!.startsWith('Google Maps Navigation examples'), - ), - findsOneWidget, - ); - }); -} diff --git a/ios/Classes/Convert+MapConfiguration.swift b/ios/Classes/Convert+MapConfiguration.swift index 1d3ac2e..3c18c6d 100644 --- a/ios/Classes/Convert+MapConfiguration.swift +++ b/ios/Classes/Convert+MapConfiguration.swift @@ -16,8 +16,17 @@ import Foundation import GoogleMaps extension Convert { - static func convertMapOptions(_ mapOptions: MapOptionsDto, - navigationViewOptions: NavigationViewOptionsDto) + static func convertNavigationUIEnabledPreference(preference: NavigationUIEnabledPreferenceDto) + -> NavigationUIEnabledPreference { + switch preference { + case .automatic: + return .automatic + case .disabled: + return .disabled + } + } + + static func convertMapOptions(_ mapOptions: MapOptionsDto) -> MapConfiguration { let cameraTargetBounds: GMSCoordinateBounds? if let bounds = mapOptions.cameraTargetBounds { @@ -37,9 +46,7 @@ extension Convert { scrollGesturesEnabledDuringRotateOrZoom: mapOptions.scrollGesturesEnabledDuringRotateOrZoom, cameraTargetBounds: cameraTargetBounds, minZoomPreference: mapOptions.minZoomPreference.map { Float($0) }, - maxZoomPreference: mapOptions.maxZoomPreference.map { Float($0) }, - - navigationUIEnabled: navigationViewOptions.navigationUIEnabled + maxZoomPreference: mapOptions.maxZoomPreference.map { Float($0) } ) } } diff --git a/ios/Classes/Convert+Navigation.swift b/ios/Classes/Convert+Navigation.swift deleted file mode 100644 index c7389bd..0000000 --- a/ios/Classes/Convert+Navigation.swift +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2023 Google LLC -// -// 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 -// -// https://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. - -import Foundation -import GoogleNavigation - -extension Convert { - static func convertNavigationWayPoint(_ gmsNavigationWaypoint: GMSNavigationWaypoint) - -> NavigationWaypointDto { - .init( - title: gmsNavigationWaypoint.title, - target: .init( - latitude: gmsNavigationWaypoint.coordinate.latitude, - longitude: gmsNavigationWaypoint.coordinate.longitude - ), - placeID: gmsNavigationWaypoint.placeID, - preferSameSideOfRoad: gmsNavigationWaypoint.preferSameSideOfRoad, - preferredSegmentHeading: Int64(gmsNavigationWaypoint.preferredHeading) - ) - } - - static func convertWaypoints(_ waypoints: [NavigationWaypointDto?]) - -> [GMSNavigationWaypoint] { - waypoints - .map { waypoint -> GMSNavigationWaypoint? in - guard let waypoint else { return nil } - if let latitude = waypoint.target?.latitude, let longitude = waypoint.target?.longitude { - if let preferSameSideOfRoad = waypoint.preferSameSideOfRoad { - return GMSNavigationWaypoint( - location: .init(latitude: latitude, longitude: longitude), - title: waypoint.title, - preferSameSideOfRoad: preferSameSideOfRoad - ) - } else if let preferredSegmentHeading = waypoint.preferredSegmentHeading { - return GMSNavigationWaypoint( - location: .init(latitude: latitude, longitude: longitude), - title: waypoint.title, - preferredSegmentHeading: Int32(preferredSegmentHeading) - ) - } - return GMSNavigationWaypoint( - location: CLLocationCoordinate2D( - latitude: latitude, - longitude: longitude - ), - title: waypoint.title - ) - } - if let placeID = waypoint.placeID { - return GMSNavigationWaypoint( - placeID: placeID, - title: waypoint.title - ) - } - return nil - } - .compactMap { $0 } - } - - static func convertRoutingOptions(_ routingOptions: RoutingOptionsDto?) - -> GMSNavigationRoutingOptions { - let options = GMSNavigationMutableRoutingOptions() - - if let routingStraregy = routingOptions?.routingStrategy { - switch routingStraregy { - case .defaultBest: - options.routingStrategy = .defaultBest - case .deltaToTargetDistance: - options.routingStrategy = .deltaToTargetDistance - case .shorter: - options.routingStrategy = .shorter - } - } - - if let alternateRoutesStrategy = routingOptions? - .alternateRoutesStrategy { - switch alternateRoutesStrategy { - case .all: - options.alternateRoutesStrategy = .all - case .one: - options.alternateRoutesStrategy = .one - case .none: - options.alternateRoutesStrategy = .none - } - } - - if let targetDistanceMeters = routingOptions? - .targetDistanceMeters { - options.targetDistancesMeters = targetDistanceMeters - .compactMap { $0 } - .map { NSNumber(value: $0) } - } - - return options - } - - static func convertRouteStatus(_ gmsRouteStatus: GMSRouteStatus) - -> RouteStatusDto { - switch gmsRouteStatus { - case .apiKeyNotAuthorized: return .apiKeyNotAuthorized - case .OK: return .statusOk - case .canceled: return .statusCanceled - case .duplicateWaypointsError: return .duplicateWaypointsError - case .internalError: return .internalError - case .locationUnavailable: return .locationUnavailable - case .networkError: return .networkError - case .noRouteFound: return .routeNotFound - case .noWaypointsError: return .noWaypointsError - case .quotaExceeded: return .quotaExceeded - case .travelModeUnsupported: return .travelModeUnsupported - case .waypointError: return .waypointError - @unknown default: - return .unknown - } - } - - static func convertTravelMode(_ travelMode: TravelModeDto?) - -> GMSNavigationTravelMode { - guard let travelMode else { - return .driving // defaults to driving - } - - switch travelMode { - case .driving: return .driving - case .cycling: return .cycling - case .walking: return .walking - case .taxi: return .taxicab - case .twoWheeler: return .twoWheeler - } - } - - static func convertSpeedAlertSeverity(gmsSpeedAlertSeverity: GMSNavigationSpeedAlertSeverity) - -> SpeedAlertSeverityDto { - switch gmsSpeedAlertSeverity { - case .unknown: return .unknown - case .notSpeeding: return .notSpeeding - case .minor: return .minor - case .major: return .major - @unknown default: - return .unknown - } - } - - static func convertSpeedAlertSeverity(speedAlertSeverity: SpeedAlertSeverityDto) - -> GMSNavigationSpeedAlertSeverity { - switch speedAlertSeverity { - case .unknown: return .unknown - case .notSpeeding: return .notSpeeding - case .minor: return .minor - case .major: return .major - } - } - - static func convertNavigationAudioGuidanceType(_ navigationAudioGuidanceType: AudioGuidanceTypeDto) - -> GMSNavigationVoiceGuidance { - switch navigationAudioGuidanceType { - case .alertsAndGuidance: return .alertsAndGuidance - case .alertsOnly: return .alertsOnly - case .silent: return .silent - } - } - - static func convertPath(_ path: GMSPath) -> [LatLngDto] { - var coordinates = [LatLngDto]() - guard path.count() != 0 else { return coordinates } - for i in 0 ... (path.count() - 1) { - coordinates.append( - LatLngDto( - latitude: path.coordinate(at: i).latitude, - longitude: path.coordinate(at: i).longitude - ) - ) - } - return coordinates - } - - static func convertRouteSegment(_ routeLeg: GMSRouteLeg) -> RouteSegmentDto { - RouteSegmentDto( - destinationLatLng: LatLngDto( - latitude: routeLeg.destinationCoordinate.latitude, - longitude: routeLeg.destinationCoordinate.longitude - ), - latLngs: { - guard let path = routeLeg.path else { return nil } - return Self.convertPath(path) - }(), - destinationWaypoint: { - guard let waypoint = routeLeg.destinationWaypoint else { return nil } - return Self.convertNavigationWayPoint(waypoint) - }() - ) - } -} diff --git a/ios/Classes/Convert.swift b/ios/Classes/Convert.swift index 501a4aa..08aa544 100644 --- a/ios/Classes/Convert.swift +++ b/ios/Classes/Convert.swift @@ -72,17 +72,27 @@ enum Convert { ) } - static func convertLatLng(point: LatLngDto) -> CLLocationCoordinate2D { + static func convertLatLngFromDto(point: LatLngDto) -> CLLocationCoordinate2D { CLLocationCoordinate2D( latitude: point.latitude, longitude: point.longitude ) } + static func convertLatLngToDto(point: CLLocationCoordinate2D) -> LatLngDto { + LatLngDto(latitude: point.latitude, longitude: point.longitude) + } + static func convertLatLngBounds(bounds: LatLngBoundsDto) -> GMSCoordinateBounds { GMSCoordinateBounds( - coordinate: convertLatLng(point: bounds.northeast), - coordinate: convertLatLng(point: bounds.southwest) + coordinate: CLLocationCoordinate2D( + latitude: bounds.northeast.latitude, + longitude: bounds.northeast.longitude + ), + coordinate: CLLocationCoordinate2D( + latitude: bounds.southwest.latitude, + longitude: bounds.southwest.longitude + ) ) } @@ -117,4 +127,190 @@ enum Convert { } return point } + + static func convertNavigationWayPoint(_ gmsNavigationWaypoint: GMSNavigationWaypoint) + -> NavigationWaypointDto { + .init( + title: gmsNavigationWaypoint.title, + target: .init( + latitude: gmsNavigationWaypoint.coordinate.latitude, + longitude: gmsNavigationWaypoint.coordinate.longitude + ), + placeID: gmsNavigationWaypoint.placeID, + preferSameSideOfRoad: gmsNavigationWaypoint.preferSameSideOfRoad, + preferredSegmentHeading: Int64(gmsNavigationWaypoint.preferredHeading) + ) + } + + static func convertWaypoints(_ waypoints: [NavigationWaypointDto?]) + -> [GMSNavigationWaypoint] { + waypoints + .map { waypoint -> GMSNavigationWaypoint? in + guard let waypoint else { return nil } + if let latitude = waypoint.target?.latitude, let longitude = waypoint.target?.longitude { + if let preferSameSideOfRoad = waypoint.preferSameSideOfRoad { + return GMSNavigationWaypoint( + location: .init(latitude: latitude, longitude: longitude), + title: waypoint.title, + preferSameSideOfRoad: preferSameSideOfRoad + ) + } else if let preferredSegmentHeading = waypoint.preferredSegmentHeading { + return GMSNavigationWaypoint( + location: .init(latitude: latitude, longitude: longitude), + title: waypoint.title, + preferredSegmentHeading: Int32(preferredSegmentHeading) + ) + } + return GMSNavigationWaypoint( + location: CLLocationCoordinate2D( + latitude: latitude, + longitude: longitude + ), + title: waypoint.title + ) + } + if let placeID = waypoint.placeID { + return GMSNavigationWaypoint( + placeID: placeID, + title: waypoint.title + ) + } + return nil + } + .compactMap { $0 } + } + + static func convertRoutingOptions(_ routingOptions: RoutingOptionsDto?) + -> GMSNavigationRoutingOptions { + let options = GMSNavigationMutableRoutingOptions() + + if let routingStraregy = routingOptions?.routingStrategy { + switch routingStraregy { + case .defaultBest: + options.routingStrategy = .defaultBest + case .deltaToTargetDistance: + options.routingStrategy = .deltaToTargetDistance + case .shorter: + options.routingStrategy = .shorter + } + } + + if let alternateRoutesStrategy = routingOptions? + .alternateRoutesStrategy { + switch alternateRoutesStrategy { + case .all: + options.alternateRoutesStrategy = .all + case .one: + options.alternateRoutesStrategy = .one + case .none: + options.alternateRoutesStrategy = .none + } + } + + if let targetDistanceMeters = routingOptions? + .targetDistanceMeters { + options.targetDistancesMeters = targetDistanceMeters + .compactMap { $0 } + .map { NSNumber(value: $0) } + } + + return options + } + + static func convertRouteStatus(_ gmsRouteStatus: GMSRouteStatus) + -> RouteStatusDto { + switch gmsRouteStatus { + case .apiKeyNotAuthorized: return .apiKeyNotAuthorized + case .OK: return .statusOk + case .canceled: return .statusCanceled + case .duplicateWaypointsError: return .duplicateWaypointsError + case .internalError: return .internalError + case .locationUnavailable: return .locationUnavailable + case .networkError: return .networkError + case .noRouteFound: return .routeNotFound + case .noWaypointsError: return .noWaypointsError + case .quotaExceeded: return .quotaExceeded + case .travelModeUnsupported: return .travelModeUnsupported + case .waypointError: return .waypointError + @unknown default: + return .unknown + } + } + + static func convertTravelMode(_ travelMode: TravelModeDto?) + -> GMSNavigationTravelMode { + guard let travelMode else { + return .driving // defaults to driving + } + + switch travelMode { + case .driving: return .driving + case .cycling: return .cycling + case .walking: return .walking + case .taxi: return .taxicab + case .twoWheeler: return .twoWheeler + } + } + + static func convertSpeedAlertSeverity(gmsSpeedAlertSeverity: GMSNavigationSpeedAlertSeverity) + -> SpeedAlertSeverityDto { + switch gmsSpeedAlertSeverity { + case .unknown: return .unknown + case .notSpeeding: return .notSpeeding + case .minor: return .minor + case .major: return .major + @unknown default: + return .unknown + } + } + + static func convertSpeedAlertSeverity(speedAlertSeverity: SpeedAlertSeverityDto) + -> GMSNavigationSpeedAlertSeverity { + switch speedAlertSeverity { + case .unknown: return .unknown + case .notSpeeding: return .notSpeeding + case .minor: return .minor + case .major: return .major + } + } + + static func convertNavigationAudioGuidanceType(_ navigationAudioGuidanceType: AudioGuidanceTypeDto) + -> GMSNavigationVoiceGuidance { + switch navigationAudioGuidanceType { + case .alertsAndGuidance: return .alertsAndGuidance + case .alertsOnly: return .alertsOnly + case .silent: return .silent + } + } + + static func convertPath(_ path: GMSPath) -> [LatLngDto] { + var coordinates = [LatLngDto]() + guard path.count() != 0 else { return coordinates } + for i in 0 ... (path.count() - 1) { + coordinates.append( + LatLngDto( + latitude: path.coordinate(at: i).latitude, + longitude: path.coordinate(at: i).longitude + ) + ) + } + return coordinates + } + + static func convertRouteSegment(_ routeLeg: GMSRouteLeg) -> RouteSegmentDto { + RouteSegmentDto( + destinationLatLng: LatLngDto( + latitude: routeLeg.destinationCoordinate.latitude, + longitude: routeLeg.destinationCoordinate.longitude + ), + latLngs: { + guard let path = routeLeg.path else { return nil } + return Self.convertPath(path) + }(), + destinationWaypoint: { + guard let waypoint = routeLeg.destinationWaypoint else { return nil } + return Self.convertNavigationWayPoint(waypoint) + }() + ) + } } diff --git a/ios/Classes/GMSPolyline+Util.swift b/ios/Classes/GMSPolyline+Util.swift index 5463aff..932453d 100644 --- a/ios/Classes/GMSPolyline+Util.swift +++ b/ios/Classes/GMSPolyline+Util.swift @@ -29,7 +29,7 @@ extension GMSPolyline { func update(from pigeonPolyline: PolylineDto) { if let points = pigeonPolyline.options.points?.compactMap({ $0 }) { let path = GMSMutablePath() - points.forEach { point in + for point in points { path.addLatitude(point.latitude, longitude: point.longitude) } self.path = path diff --git a/ios/Classes/GoogleMapsImageRegistryMessageHandler.swift b/ios/Classes/GoogleMapsImageRegistryMessageHandler.swift new file mode 100644 index 0000000..8f399cd --- /dev/null +++ b/ios/Classes/GoogleMapsImageRegistryMessageHandler.swift @@ -0,0 +1,51 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import Flutter +import Foundation + +class GoogleMapsImageRegistryMessageHandler: ImageRegistryApi { + let imageRegistry: ImageRegistry + + init(imageRegistry: ImageRegistry) { + self.imageRegistry = imageRegistry + } + + func registerBitmapImage(imageId: String, bytes: FlutterStandardTypedData, + imagePixelRatio: Double, + width: Double?, + height: Double?) throws -> ImageDescriptorDto { + try imageRegistry.registerBitmapImage( + imageId: imageId, + bytes: bytes.data, + imagePixelRatio: imagePixelRatio, + width: width, + height: height + ) + } + + func unregisterImage(imageDescriptor: ImageDescriptorDto) throws { + if let registeredImageId = imageDescriptor.registeredImageId { + imageRegistry.unregisterImage(imageId: registeredImageId) + } + } + + func getRegisteredImages() throws -> [ImageDescriptorDto] { + imageRegistry.registeredImages.map { $0.toImageDescriptorDto() } + } + + func clearRegisteredImages() throws { + imageRegistry.clearRegisteredImages() + } +} diff --git a/ios/Classes/GoogleMapsNavigationPlugin.swift b/ios/Classes/GoogleMapsNavigationPlugin.swift index b259e5b..2d85f35 100644 --- a/ios/Classes/GoogleMapsNavigationPlugin.swift +++ b/ios/Classes/GoogleMapsNavigationPlugin.swift @@ -28,6 +28,9 @@ public class GoogleMapsNavigationPlugin: NSObject, FlutterPlugin { private static var navigationSessionManager: GoogleMapsNavigationSessionManager? private static var navigationInspectorHandler: GoogleMapsNavigationInspectorHandler? + private static var imageRegistryMessageHandler: GoogleMapsImageRegistryMessageHandler? + private static var imageRegistry: ImageRegistry? + public static func register(with registrar: FlutterPluginRegistrar) { // Navigation View handling viewRegistry = GoogleMapsNavigationViewRegistry() @@ -43,9 +46,11 @@ public class GoogleMapsNavigationPlugin: NSObject, FlutterPlugin { guard navigationViewEventApi != nil else { return } + imageRegistry = ImageRegistry() let factory = GoogleMapsNavigationViewFactory( viewRegistry: viewRegistry!, - navigationViewEventApi: navigationViewEventApi! + navigationViewEventApi: navigationViewEventApi!, + imageRegistry: imageRegistry! ) registrar.register(factory, withId: "google_maps_navigation") @@ -72,5 +77,12 @@ public class GoogleMapsNavigationPlugin: NSObject, FlutterPlugin { binaryMessenger: registrar.messenger(), api: navigationInspector ) + + imageRegistryMessageHandler = + GoogleMapsImageRegistryMessageHandler(imageRegistry: imageRegistry!) + ImageRegistryApiSetup.setUp( + binaryMessenger: registrar.messenger(), + api: imageRegistryMessageHandler + ) } } diff --git a/ios/Classes/GoogleMapsNavigationSessionManager.swift b/ios/Classes/GoogleMapsNavigationSessionManager.swift index d6d22ee..2ecb04f 100644 --- a/ios/Classes/GoogleMapsNavigationSessionManager.swift +++ b/ios/Classes/GoogleMapsNavigationSessionManager.swift @@ -30,6 +30,22 @@ enum GoogleMapsNavigationSessionManagerError: Error { case notSupported } +// Expose the navigator to the google_maps_driver side. +// DriverApi initialization requires navigator. +public class ExposedGoogleMapsNavigator: NSObject { + public static func getNavigator() throws -> GMSNavigator { + try GoogleMapsNavigationSessionManager.shared.getNavigator() + } + + public static func getRoadSnappedLocationProvider() throws -> GMSRoadSnappedLocationProvider? { + try GoogleMapsNavigationSessionManager.shared.getSession().roadSnappedLocationProvider + } + + public static func enableRoadSnappedLocationUpdates() { + GoogleMapsNavigationSessionManager.shared.enableRoadSnappedLocationUpdates() + } +} + class GoogleMapsNavigationSessionManager: NSObject { enum RoutingOptionsTarget { case navigator @@ -44,13 +60,18 @@ class GoogleMapsNavigationSessionManager: NSObject { private var _session: GMSNavigationSession? - private func getNavigator() throws -> GMSNavigator { + func getNavigator() throws -> GMSNavigator { guard let _session else { throw GoogleMapsNavigationSessionManagerError.sessionNotInitialized } guard let navigator = _session.navigator else { throw GoogleMapsNavigationSessionManagerError.termsNotAccepted } return navigator } + func getSession() throws -> GMSNavigationSession { + guard let _session else { throw GoogleMapsNavigationSessionManagerError.sessionNotInitialized } + return _session + } + private func getSimulator() throws -> GMSLocationSimulator { guard let _session else { throw GoogleMapsNavigationSessionManagerError.sessionNotInitialized } guard let simulator = _session.locationSimulator @@ -68,13 +89,16 @@ class GoogleMapsNavigationSessionManager: NSObject { // Create a navigation session and initializes listeners. // If navigator is already created, only re-initialize listeners. - func createNavigationSession() throws { + func createNavigationSession(_ abnormalTerminationReportingEnabled: Bool) throws { // Align API behavior with Android: // Check the terms and conditions before the location permission check below. if !areTermsAccepted() { throw GoogleMapsNavigationSessionManagerError.termsNotAccepted } + // Enable or disable abnormal termination reporting. + GMSServices.setAbnormalTerminationReportingEnabled(abnormalTerminationReportingEnabled) + // Align API behavior with Android: // Fail the session creation if the location permission hasn't been accepted. let locationManager = CLLocationManager() @@ -117,7 +141,10 @@ class GoogleMapsNavigationSessionManager: NSObject { _session?.navigator != nil } - func cleanup() { + func cleanup() throws { + if _session == nil { + throw GoogleMapsNavigationSessionManagerError.sessionNotInitialized + } _session?.locationSimulator?.stopSimulation() _session?.navigator?.clearDestinations() _session?.roadSnappedLocationProvider?.remove(self) @@ -139,7 +166,6 @@ class GoogleMapsNavigationSessionManager: NSObject { guard view.setSession(session) else { throw GoogleMapsNavigationSessionManagerError.initializeFailure } - view.enableNavigationUI(true) } func attachNavigationSessionToMapView(mapView: GoogleMapsNavigationView) { @@ -171,6 +197,10 @@ class GoogleMapsNavigationSessionManager: NSObject { GMSNavigationServices.resetTermsAndConditionsAccepted() } + func getNavSDKVersion() -> String { + GMSNavigationServices.navSDKVersion() + } + /// Navigation. func startGuidance() throws { try getNavigator().isGuidanceActive = true @@ -184,7 +214,7 @@ class GoogleMapsNavigationSessionManager: NSObject { try getNavigator().isGuidanceActive } - /// If the session has view attached, enable given display options. + // If the session has view attached, enable given display options. private func handleDisplayOptionsIfNeeded(options: NavigationDisplayOptionsDto) { _viewRegistry?.getAllRegisteredViews().forEach { view in if let showDestinationMarkers = options.showDestinationMarkers { @@ -199,29 +229,45 @@ class GoogleMapsNavigationSessionManager: NSObject { } } - func setDestinations(msg: DestinationsDto, + func setDestinations(destinations: DestinationsDto, completion: @escaping (Result) -> Void) { do { - /// Set the routing options globally for navigator or restore the defaults - try setRoutingOptionsGlobals(msg.routingOptions, for: .navigator) - guard msg.routingOptions != nil else { - /// Set destinations for navigator. + // If the session has view attached, enable given display options. + handleDisplayOptionsIfNeeded(options: destinations.displayOptions) + + // Set the destinations for the navigator with a route token. + if let options = destinations.routeTokenOptions { try getNavigator() .setDestinations( - Convert.convertWaypoints(msg.waypoints) + Convert.convertWaypoints(destinations.waypoints), + routeToken: options.routeToken, + callback: { routeStatus in + completion(.success(Convert.convertRouteStatus(routeStatus))) + } + ) + _session?.travelMode = Convert.convertTravelMode(options.travelMode) + return + } + + // Set the routing options globally for navigator or restore the defaults + try setRoutingOptionsGlobals(destinations.routingOptions, for: .navigator) + + guard destinations.routingOptions != nil else { + // Set destinations for navigator. + try getNavigator() + .setDestinations( + Convert.convertWaypoints(destinations.waypoints) ) { routeStatus in completion(.success(Convert.convertRouteStatus(routeStatus))) } return } - /// If the session has view attached, enable given display options. - handleDisplayOptionsIfNeeded(options: msg.displayOptions) - /// Set destinations for navigator with routing options. + // Set destinations for navigator with routing options. try getNavigator() .setDestinations( - Convert.convertWaypoints(msg.waypoints), - routingOptions: Convert.convertRoutingOptions(msg.routingOptions), + Convert.convertWaypoints(destinations.waypoints), + routingOptions: Convert.convertRoutingOptions(destinations.routingOptions), callback: { routeStatus in completion(.success(Convert.convertRouteStatus(routeStatus))) } @@ -412,12 +458,12 @@ class GoogleMapsNavigationSessionManager: NSObject { } /// Listeners - func enableRoadSnappedLocationUpdates() throws { + func enableRoadSnappedLocationUpdates() { LocationManager.shared.startUpdatingLocation() _session?.roadSnappedLocationProvider?.startUpdatingLocation() } - func disableRoadSnappedLocationUpdates() throws { + func disableRoadSnappedLocationUpdates() { LocationManager.shared.stopUpdatingLocation() _session?.roadSnappedLocationProvider?.stopUpdatingLocation() } @@ -435,15 +481,14 @@ extension GoogleMapsNavigationSessionManager: GMSRoadSnappedLocationProviderList /// Send road snapped location update back to flutter code. func locationProvider(_ locationProvider: GMSRoadSnappedLocationProvider, didUpdate location: CLLocation) { - _navigationSessionEventApi?.onRoadSnappedLocationUpdated(msg: + _navigationSessionEventApi?.onRoadSnappedLocationUpdated( + location: .init( - location: - .init( - latitude: location.coordinate.latitude, - longitude: location.coordinate.longitude - ) + latitude: location.coordinate.latitude, + longitude: location.coordinate.longitude ), - completion: { _ in }) + completion: { _ in } + ) } } @@ -463,21 +508,21 @@ extension GoogleMapsNavigationSessionManager: GMSNavigatorListener { func navigator(_ navigator: GMSNavigator, didArriveAt waypoint: GMSNavigationWaypoint) { _navigationSessionEventApi?.onArrival( - msg: .init(waypoint: Convert.convertNavigationWayPoint(waypoint)), + waypoint: Convert.convertNavigationWayPoint(waypoint), completion: { _ in } ) } func navigatorDidChangeRoute(_ navigator: GMSNavigator) { _navigationSessionEventApi?.onRouteChanged( - msg: .init(message: .empty), completion: { _ in } ) } func navigator(_ navigator: GMSNavigator, didUpdateRemainingTime time: TimeInterval) { _navigationSessionEventApi?.onRemainingTimeOrDistanceChanged( - msg: .init(remainingTime: time, remainingDistance: navigator.distanceToNextDestination), + remainingTime: time, + remainingDistance: navigator.distanceToNextDestination, completion: { _ in } ) } @@ -485,7 +530,8 @@ extension GoogleMapsNavigationSessionManager: GMSNavigatorListener { func navigator(_ navigator: GMSNavigator, didUpdateRemainingDistance distance: CLLocationDistance) { _navigationSessionEventApi?.onRemainingTimeOrDistanceChanged( - msg: .init(remainingTime: navigator.timeToNextDestination, remainingDistance: distance), + remainingTime: navigator.timeToNextDestination, + remainingDistance: distance, completion: { _ in } ) } diff --git a/ios/Classes/GoogleMapsNavigationSessionMessageHandler.swift b/ios/Classes/GoogleMapsNavigationSessionMessageHandler.swift index 5182a5e..d1c5a32 100644 --- a/ios/Classes/GoogleMapsNavigationSessionMessageHandler.swift +++ b/ios/Classes/GoogleMapsNavigationSessionMessageHandler.swift @@ -30,6 +30,7 @@ class GoogleMapsNavigationSessionMessageHandler: NavigationSessionApi { shouldOnlyShowDriverAwarenessDisclaimer: Bool, completion: @escaping (Result) -> Void) { if shouldOnlyShowDriverAwarenessDisclaimer { + // TODO: Disable driver awareness disclaimer on iOS due to the bug in the native side SDK completion(Result.failure(GoogleMapsNavigationSessionManagerError.notSupported)) return } @@ -51,9 +52,15 @@ class GoogleMapsNavigationSessionMessageHandler: NavigationSessionApi { try GoogleMapsNavigationSessionManager.shared.resetTermsAccepted() } - func createNavigationSession(completion: @escaping (Result) -> Void) { + func getNavSDKVersion() -> String { + GoogleMapsNavigationSessionManager.shared.getNavSDKVersion() + } + + func createNavigationSession(abnormalTerminationReportingEnabled: Bool, + completion: @escaping (Result) -> Void) { do { - try GoogleMapsNavigationSessionManager.shared.createNavigationSession() + try GoogleMapsNavigationSessionManager.shared + .createNavigationSession(abnormalTerminationReportingEnabled) completion(.success(())) } catch { completion(.failure(error)) @@ -65,7 +72,7 @@ class GoogleMapsNavigationSessionMessageHandler: NavigationSessionApi { } func cleanup() throws { - GoogleMapsNavigationSessionManager.shared.cleanup() + try GoogleMapsNavigationSessionManager.shared.cleanup() } /// Navigation actions @@ -81,9 +88,12 @@ class GoogleMapsNavigationSessionMessageHandler: NavigationSessionApi { try GoogleMapsNavigationSessionManager.shared.stopGuidance() } - func setDestinations(msg: DestinationsDto, + func setDestinations(destinations: DestinationsDto, completion: @escaping (Result) -> Void) { - GoogleMapsNavigationSessionManager.shared.setDestinations(msg: msg, completion: completion) + GoogleMapsNavigationSessionManager.shared.setDestinations( + destinations: destinations, + completion: completion + ) } func clearDestinations() throws { @@ -206,11 +216,11 @@ class GoogleMapsNavigationSessionMessageHandler: NavigationSessionApi { /// Listeners func enableRoadSnappedLocationUpdates() throws { - try GoogleMapsNavigationSessionManager.shared.enableRoadSnappedLocationUpdates() + GoogleMapsNavigationSessionManager.shared.enableRoadSnappedLocationUpdates() } func disableRoadSnappedLocationUpdates() throws { - try GoogleMapsNavigationSessionManager.shared.disableRoadSnappedLocationUpdates() + GoogleMapsNavigationSessionManager.shared.disableRoadSnappedLocationUpdates() } func registerRemainingTimeOrDistanceChangedListener(remainingTimeThresholdSeconds: Int64, diff --git a/ios/Classes/GoogleMapsNavigationView.swift b/ios/Classes/GoogleMapsNavigationView.swift index 663bdb0..31a1560 100644 --- a/ios/Classes/GoogleMapsNavigationView.swift +++ b/ios/Classes/GoogleMapsNavigationView.swift @@ -24,6 +24,8 @@ enum GoogleMapsNavigationViewError: Error { case circleNotFound case awaitViewReadyCalledMultipleTimes case mapStyleError + case minZoomGreaterThanMaxZoom + case maxZoomLessThanMinZoom } class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelegate { @@ -37,8 +39,13 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega private var _gmsPolylines: [GMSPolyline] = [] private var _gmsCircles: [GMSCircle] = [] private var _mapConfiguration: MapConfiguration! + private var _navigationUIEnabledPreference: NavigationUIEnabledPreference! + private var _mapViewReady: Bool = false private var _mapReadyCallback: ((Result) -> Void)? + private var _imageRegistry: ImageRegistry + private var _consumeMyLocationButtonClickEventsEnabled: Bool = false + private var _listenCameraChanges = false var isAttachedToSession: Bool = false func view() -> UIView { @@ -49,11 +56,13 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega viewIdentifier viewId: Int64, viewRegistry registry: GoogleMapsNavigationViewRegistry, navigationViewEventApi: NavigationViewEventApi, - mapConfiguration: MapConfiguration) { + navigationUIEnabledPreference: NavigationUIEnabledPreference, + mapConfiguration: MapConfiguration, imageRegistry: ImageRegistry) { _viewId = viewId _viewRegistry = registry _navigationViewEventApi = navigationViewEventApi _mapConfiguration = mapConfiguration + _imageRegistry = imageRegistry let mapViewOptions = GMSMapViewOptions() _mapConfiguration.apply(to: mapViewOptions, withFrame: frame) @@ -61,6 +70,10 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega _mapConfiguration.apply(to: _navigationView) super.init() + + _navigationUIEnabledPreference = navigationUIEnabledPreference + applyNavigationUIEnabledPreference() + registry.registerView(viewId: viewId, view: self) _navigationView.delegate = self _navigationView.viewSettledDelegate = self @@ -79,8 +92,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega mapView: self ) - // Set initial navigation UI state. - enableNavigationUI(_mapConfiguration?.navigationUIEnabled ?? isAttachedToSession) + applyNavigationUIEnabledPreference() _navigationView.needsUpdateConstraints() @@ -92,6 +104,17 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } } + func applyNavigationUIEnabledPreference() { + var navigationUIEnabled = false + // Set initial navigation UI state. + if GoogleMapsNavigationSessionManager.shared.isInitialized() { + if _navigationUIEnabledPreference == .automatic { + navigationUIEnabled = true + } + } + setNavigationUIEnabled(navigationUIEnabled) + } + func awaitMapReady(callback: @escaping (Result) -> Void) { if _mapViewReady { callback(.success(())) @@ -150,7 +173,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } } - func enableMyLocation(enabled: Bool) throws { + func setMyLocationEnabled(_ enabled: Bool) throws { _navigationView.isMyLocationEnabled = enabled try updateMyLocationButton() } @@ -171,7 +194,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } } - func enableMyLocationButton(enabled: Bool) throws { + func setMyLocationButtonEnabled(_ enabled: Bool) throws { _myLocationButton = enabled try updateMyLocationButton() } @@ -183,39 +206,39 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega .isMyLocationEnabled && _myLocationButton } - func enableZoomGestures(enabled: Bool) throws { + func setZoomGesturesEnabled(_ enabled: Bool) throws { _navigationView.settings.zoomGestures = enabled } - func enableZoomControls(enabled: Bool) throws { + func setZoomControlsEnabled(_ enabled: Bool) throws { throw GoogleMapsNavigationViewError.notSupported } - func enableCompass(enabled: Bool) throws { + func setCompassEnabled(_ enabled: Bool) throws { _navigationView.settings.compassButton = enabled } - func enableRotateGestures(enabled: Bool) throws { + func setRotateGesturesEnabled(_ enabled: Bool) throws { _navigationView.settings.rotateGestures = enabled } - func enableScrollGestures(enabled: Bool) throws { + func setScrollGesturesEnabled(_ enabled: Bool) throws { _navigationView.settings.scrollGestures = enabled } - func enableScrollGesturesDuringRotateOrZoom(enabled: Bool) throws { + func setScrollGesturesDuringRotateOrZoomEnabled(_ enabled: Bool) throws { _navigationView.settings.allowScrollGesturesDuringRotateOrZoom = enabled } - func enableTiltGestures(enabled: Bool) throws { + func setTiltGesturesEnabled(_ enabled: Bool) throws { _navigationView.settings.tiltGestures = enabled } - func enableMapToolbar(enabled: Bool) throws { + func setMapToolbarEnabled(_ enabled: Bool) throws { throw GoogleMapsNavigationViewError.notSupported } - func enableTraffic(enabled: Bool) throws { + func setTrafficEnabled(_ enabled: Bool) throws { _navigationView.isTrafficEnabled = enabled } @@ -355,7 +378,20 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega func setSession(_ session: GMSNavigationSession) -> Bool { // Navigation UI delegate needs to be set after attaching // the session to the map view. + + let navigationWasEnabled = _navigationView.isNavigationEnabled + let result = _navigationView.enableNavigation(with: session) + + if navigationWasEnabled != _navigationView.isNavigationEnabled { + // Navigation UI got enabled, send enabled change event. + _navigationViewEventApi + .onNavigationUIEnabledChanged( + viewId: _viewId, + navigationUIEnabled: _navigationView.isNavigationEnabled + ) { _ in } + } + _navigationView.navigationUIDelegate = self isAttachedToSession = true return result @@ -377,7 +413,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega _navigationView.settings.isNavigationTripProgressBarEnabled } - func enableNavigationTripProgressBar(_ enabled: Bool) { + func setNavigationTripProgressBarEnabled(_ enabled: Bool) { _navigationView.settings.isNavigationTripProgressBarEnabled = enabled } @@ -385,7 +421,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega _navigationView.settings.isNavigationHeaderEnabled } - func enableNavigationHeader(_ enabled: Bool) { + func setNavigationHeaderEnabled(_ enabled: Bool) { _navigationView.settings.isNavigationHeaderEnabled = enabled } @@ -393,7 +429,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega _navigationView.settings.isNavigationFooterEnabled } - func enableNavigationFooter(_ enabled: Bool) { + func setNavigationFooterEnabled(_ enabled: Bool) { _navigationView.settings.isNavigationFooterEnabled = enabled } @@ -401,7 +437,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega _navigationView.settings.isRecenterButtonEnabled } - func enableRecenterButton(_ enabled: Bool) { + func setRecenterButtonEnabled(_ enabled: Bool) { _navigationView.settings.isRecenterButtonEnabled = enabled } @@ -409,33 +445,35 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega _navigationView.shouldDisplaySpeedLimit } - func enableSpeedLimitIcon(_ enable: Bool) { - _navigationView.shouldDisplaySpeedLimit = enable + func setSpeedLimitIconEnabled(_ enabled: Bool) { + _navigationView.shouldDisplaySpeedLimit = enabled } func isSpeedometerEnabled() -> Bool { _navigationView.shouldDisplaySpeedometer } - func enableSpeedometer(_ enable: Bool) { - _navigationView.shouldDisplaySpeedometer = enable + func setSpeedometerEnabled(_ enabled: Bool) { + _navigationView.shouldDisplaySpeedometer = enabled } - func isIncidentCardsEnabled() -> Bool { + func isTrafficIncidentCardsEnabled() -> Bool { _navigationView.settings.showsIncidentCards } - func enableIncidentCards(_ enable: Bool) { - _navigationView.settings.showsIncidentCards = enable + func setTrafficIncidentCardsEnabled(_ enabled: Bool) { + _navigationView.settings.showsIncidentCards = enabled } func isNavigationUIEnabled() -> Bool { _navigationView.isNavigationEnabled } - func enableNavigationUI(_ enabled: Bool) { + func setNavigationUIEnabled(_ enabled: Bool) { if _navigationView.isNavigationEnabled != enabled { _navigationView.isNavigationEnabled = enabled + _navigationViewEventApi + .onNavigationUIEnabledChanged(viewId: _viewId, navigationUIEnabled: enabled) { _ in } if !enabled { let camera = _navigationView.camera @@ -449,6 +487,34 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } } + func getMinZoomPreference() -> Float { + _navigationView.minZoom + } + + func getMaxZoomPreference() -> Float { + _navigationView.maxZoom + } + + func resetMinMaxZoomPreference() { + _navigationView.setMinZoom(kGMSMinZoomLevel, maxZoom: kGMSMaxZoomLevel) + } + + func setMinZoomPreference(minZoomPreference: Float) throws { + if minZoomPreference > _navigationView.maxZoom { + throw GoogleMapsNavigationViewError.minZoomGreaterThanMaxZoom + } + + _navigationView.setMinZoom(minZoomPreference, maxZoom: _navigationView.maxZoom) + } + + func setMaxZoomPreference(maxZoomPreference: Float) throws { + if maxZoomPreference < _navigationView.minZoom { + throw GoogleMapsNavigationViewError.maxZoomLessThanMinZoom + } + + _navigationView.setMinZoom(_navigationView.minZoom, maxZoom: maxZoomPreference) + } + func getMarkers() -> [MarkerDto] { _markerControllers.map { $0.toMarkerDto() } } @@ -458,7 +524,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega .compactMap { $0 } .map { marker in let markerController = MarkerController(markerId: marker.markerId) - markerController.update(from: marker) + markerController.update(from: marker, imageRegistry: _imageRegistry) // Handle visibility property on iOS by removing/not putting the marker // on the map. markerController.gmsMarker.map = marker.isVisible() ? _navigationView : nil @@ -473,7 +539,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega .compactMap { $0 } .compactMap { updatedMarker in let markerController = try findMarkerController(markerId: updatedMarker.markerId) - markerController.update(from: updatedMarker) + markerController.update(from: updatedMarker, imageRegistry: _imageRegistry) // Handle visibility property on iOS by removing/not putting the marker // on the map. markerController.gmsMarker.map = updatedMarker.isVisible() ? _navigationView : nil @@ -493,7 +559,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } func clearMarkers() { - _markerControllers.forEach { markerController in + for markerController in _markerControllers { markerController.gmsMarker.map = nil } _markerControllers.removeAll() @@ -544,7 +610,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } func clearPolygons() { - _gmsPolygons.forEach { gmsPolygon in + for gmsPolygon in _gmsPolygons { gmsPolygon.map = nil } _gmsPolygons.removeAll() @@ -595,7 +661,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } func clearPolylines() { - _gmsPolylines.forEach { gmsPolyline in + for gmsPolyline in _gmsPolylines { gmsPolyline.map = nil } _gmsPolylines.removeAll() @@ -646,7 +712,7 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega } func clearCircles() { - _gmsCircles.forEach { gmsCircle in + for gmsCircle in _gmsCircles { gmsCircle.map = nil } _gmsCircles.removeAll() @@ -665,12 +731,11 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega private func sendMarkerEvent(marker: GMSMarker, eventType: MarkerEventTypeDto) { do { let markerController = try findMarkerController(gmsMarker: marker) + _navigationViewEventApi.onMarkerEvent( - msg: .init( - viewId: _viewId, - markerId: markerController.markerId, - eventType: eventType - ), + viewId: _viewId, + markerId: markerController.markerId, + eventType: eventType, completion: { _ in } ) } catch { @@ -683,14 +748,13 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega do { let markerController = try findMarkerController(gmsMarker: marker) _navigationViewEventApi.onMarkerDragEvent( - msg: .init( - viewId: _viewId, - markerId: markerController.markerId, - eventType: eventType, - position: .init( - latitude: markerController.gmsMarker.position.latitude, - longitude: markerController.gmsMarker.position.longitude - ) + viewId: _viewId, + markerId: markerController.markerId, + eventType: eventType, + position: .init( + latitude: markerController.gmsMarker.position.latitude, + longitude: markerController.gmsMarker.position.longitude + ), completion: { _ in } ) @@ -698,6 +762,20 @@ class GoogleMapsNavigationView: NSObject, FlutterPlatformView, ViewSettledDelega // Fail silently. } } + + func setConsumeMyLocationButtonClickEventsEnabled(enabled: Bool) { + _consumeMyLocationButtonClickEventsEnabled = enabled + } + + func isConsumeMyLocationButtonClickEventsEnabled() -> Bool { + _consumeMyLocationButtonClickEventsEnabled + } + + func registerOnCameraChangedListener() { + // Camera listeners cannot be controlled at runtime, so use this + // boolean to control if camera changes are sent over the event channel. + _listenCameraChanges = true + } } extension GoogleMapsNavigationView: GMSMapViewNavigationUIDelegate { @@ -771,26 +849,63 @@ extension GoogleMapsNavigationView: GMSMapViewDelegate { func mapView(_ mapView: GMSMapView, didTap overlay: GMSOverlay) { if let polygon = overlay as? GMSPolygon { _navigationViewEventApi.onPolygonClicked( - msg: .init( - viewId: _viewId, - polygonId: polygon.getPolygonId() - ), + viewId: _viewId, + polygonId: polygon.getPolygonId(), completion: { _ in } ) } else if let polyline = overlay as? GMSPolyline { _navigationViewEventApi.onPolylineClicked( - msg: .init( - viewId: _viewId, - polylineId: polyline.getPolylineId() - ), + viewId: _viewId, + polylineId: polyline.getPolylineId(), completion: { _ in } ) } else if let circle = overlay as? GMSCircle { _navigationViewEventApi.onCircleClicked( - msg: .init( - viewId: _viewId, - circleId: circle.getCircleId() - ), + viewId: _viewId, + circleId: circle.getCircleId(), + completion: { _ in } + ) + } + } + + func mapView(_ mapView: GMSMapView, didTapMyLocation location: CLLocationCoordinate2D) { + _navigationViewEventApi.onMyLocationClicked(viewId: _viewId, completion: { _ in }) + } + + func didTapMyLocationButton(for mapView: GMSMapView) -> Bool { + _navigationViewEventApi.onMyLocationButtonClicked(viewId: _viewId, completion: { _ in }) + return _consumeMyLocationButtonClickEventsEnabled + } + + func mapView(_ mapView: GMSMapView, willMove gesture: Bool) { + if _listenCameraChanges { + let position = Convert.convertCameraPosition(position: mapView.camera) + _navigationViewEventApi.onCameraChanged( + viewId: _viewId, + eventType: gesture ? .moveStartedByGesture : .moveStartedByApi, + position: position, + completion: { _ in } + ) + } + } + + func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) { + if _listenCameraChanges { + _navigationViewEventApi.onCameraChanged( + viewId: _viewId, + eventType: .onCameraIdle, + position: Convert.convertCameraPosition(position: position), + completion: { _ in } + ) + } + } + + func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) { + if _listenCameraChanges { + _navigationViewEventApi.onCameraChanged( + viewId: _viewId, + eventType: .onCameraMove, + position: Convert.convertCameraPosition(position: position), completion: { _ in } ) } diff --git a/ios/Classes/GoogleMapsNavigationViewFactory.swift b/ios/Classes/GoogleMapsNavigationViewFactory.swift index 3622f6b..60dfcea 100644 --- a/ios/Classes/GoogleMapsNavigationViewFactory.swift +++ b/ios/Classes/GoogleMapsNavigationViewFactory.swift @@ -18,11 +18,13 @@ import UIKit class GoogleMapsNavigationViewFactory: NSObject, FlutterPlatformViewFactory { private var viewRegistry: GoogleMapsNavigationViewRegistry private var navigationViewEventApi: NavigationViewEventApi + private var imageRegistry: ImageRegistry init(viewRegistry: GoogleMapsNavigationViewRegistry, - navigationViewEventApi: NavigationViewEventApi) { + navigationViewEventApi: NavigationViewEventApi, imageRegistry: ImageRegistry) { self.viewRegistry = viewRegistry self.navigationViewEventApi = navigationViewEventApi + self.imageRegistry = imageRegistry super.init() } @@ -38,17 +40,18 @@ class GoogleMapsNavigationViewFactory: NSObject, FlutterPlatformViewFactory { fatalError("Failed to decode NavigationViewCreationOptionsDto") } - let mapConfiguration = Convert.convertMapOptions( - params.mapOptions, - navigationViewOptions: params.navigationViewOptions - ) + let mapConfiguration = Convert.convertMapOptions(params.mapOptions) return GoogleMapsNavigationView( frame: frame, viewIdentifier: viewId, viewRegistry: viewRegistry, navigationViewEventApi: navigationViewEventApi, - mapConfiguration: mapConfiguration + navigationUIEnabledPreference: Convert + .convertNavigationUIEnabledPreference(preference: params.navigationViewOptions + .navigationUIEnabledPreference), + mapConfiguration: mapConfiguration, + imageRegistry: imageRegistry ) } } diff --git a/ios/Classes/GoogleMapsNavigationViewMessageHandler.swift b/ios/Classes/GoogleMapsNavigationViewMessageHandler.swift index 4c28313..5d5e080 100644 --- a/ios/Classes/GoogleMapsNavigationViewMessageHandler.swift +++ b/ios/Classes/GoogleMapsNavigationViewMessageHandler.swift @@ -49,8 +49,8 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { } } - func enableMyLocation(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableMyLocation(enabled: enabled) + func setMyLocationEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setMyLocationEnabled(enabled) } func getMapType(viewId: Int64) throws -> MapTypeDto { @@ -66,44 +66,44 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { try getView(viewId).setMapStyle(styleJson: styleJson) } - func enableMyLocationButton(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableMyLocationButton(enabled: enabled) + func setMyLocationButtonEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setMyLocationButtonEnabled(enabled) } - func enableZoomGestures(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableZoomGestures(enabled: enabled) + func setZoomGesturesEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setZoomGesturesEnabled(enabled) } - func enableZoomControls(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableZoomControls(enabled: enabled) + func setZoomControlsEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setZoomControlsEnabled(enabled) } - func enableCompass(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableCompass(enabled: enabled) + func setCompassEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setCompassEnabled(enabled) } - func enableRotateGestures(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableRotateGestures(enabled: enabled) + func setRotateGesturesEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setRotateGesturesEnabled(enabled) } - func enableScrollGestures(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableScrollGestures(enabled: enabled) + func setScrollGesturesEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setScrollGesturesEnabled(enabled) } - func enableScrollGesturesDuringRotateOrZoom(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableScrollGesturesDuringRotateOrZoom(enabled: enabled) + func setScrollGesturesDuringRotateOrZoomEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setScrollGesturesDuringRotateOrZoomEnabled(enabled) } - func enableTiltGestures(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableTiltGestures(enabled: enabled) + func setTiltGesturesEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setTiltGesturesEnabled(enabled) } - func enableMapToolbar(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableMapToolbar(enabled: enabled) + func setMapToolbarEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setMapToolbarEnabled(enabled) } - func enableTraffic(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableTraffic(enabled: enabled) + func setTrafficEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setTrafficEnabled(enabled) } func isMyLocationEnabled(viewId: Int64) throws -> Bool { @@ -190,7 +190,7 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { func animateCameraToLatLng(viewId: Int64, point: LatLngDto, duration: Int64?, completion: @escaping (Result) -> Void) { do { - try getView(viewId).animateCameraToLatLng(point: Convert.convertLatLng(point: point)) + try getView(viewId).animateCameraToLatLng(point: Convert.convertLatLngFromDto(point: point)) // No callback supported, just return immediately completion(.success(true)) @@ -222,7 +222,7 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { completion: @escaping (Result) -> Void) { do { try getView(viewId).animateCameraToLatLngZoom( - point: Convert.convertLatLng(point: point), + point: Convert.convertLatLngFromDto(point: point), zoom: zoom ) @@ -281,7 +281,7 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { } func moveCameraToLatLng(viewId: Int64, point: LatLngDto) throws { - try getView(viewId).moveCameraToLatLng(point: Convert.convertLatLng(point: point)) + try getView(viewId).moveCameraToLatLng(point: Convert.convertLatLngFromDto(point: point)) } func moveCameraToLatLngBounds(viewId: Int64, bounds: LatLngBoundsDto, @@ -294,7 +294,7 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { func moveCameraToLatLngZoom(viewId: Int64, point: LatLngDto, zoom: Double) throws { try getView(viewId).moveCameraToLatLngZoom( - point: Convert.convertLatLng(point: point), + point: Convert.convertLatLngFromDto(point: point), zoom: zoom ) } @@ -322,36 +322,36 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { ) } - func enableNavigationHeader(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableNavigationHeader(enabled) + func setNavigationHeaderEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setNavigationHeaderEnabled(enabled) } - func enableNavigationFooter(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableNavigationFooter(enabled) + func setNavigationFooterEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setNavigationFooterEnabled(enabled) } - func enableRecenterButton(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableRecenterButton(enabled) + func setRecenterButtonEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setRecenterButtonEnabled(enabled) } - func enableSpeedLimitIcon(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableSpeedLimitIcon(enabled) + func setSpeedLimitIconEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setSpeedLimitIconEnabled(enabled) } - func enableSpeedometer(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableSpeedometer(enabled) + func setSpeedometerEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setSpeedometerEnabled(enabled) } - func enableIncidentCards(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableIncidentCards(enabled) + func setTrafficIncidentCardsEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setTrafficIncidentCardsEnabled(enabled) } func isNavigationTripProgressBarEnabled(viewId: Int64) throws -> Bool { try getView(viewId).isNavigationTripProgressBarEnabled() } - func enableNavigationTripProgressBar(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableNavigationTripProgressBar(enabled) + func setNavigationTripProgressBarEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setNavigationTripProgressBarEnabled(enabled) } func isNavigationHeaderEnabled(viewId: Int64) throws -> Bool { @@ -374,16 +374,16 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { try getView(viewId).isSpeedometerEnabled() } - func isIncidentCardsEnabled(viewId: Int64) throws -> Bool { - try getView(viewId).isIncidentCardsEnabled() + func isTrafficIncidentCardsEnabled(viewId: Int64) throws -> Bool { + try getView(viewId).isTrafficIncidentCardsEnabled() } func isNavigationUIEnabled(viewId: Int64) throws -> Bool { try getView(viewId).isNavigationUIEnabled() } - func enableNavigationUI(viewId: Int64, enabled: Bool) throws { - try getView(viewId).enableNavigationUI(enabled) + func setNavigationUIEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setNavigationUIEnabled(enabled) } func showRouteOverview(viewId: Int64) throws { @@ -473,4 +473,36 @@ class GoogleMapsNavigationViewMessageHandler: NavigationViewApi { func clear(viewId: Int64) throws { try getView(viewId).clear() } + + func setConsumeMyLocationButtonClickEventsEnabled(viewId: Int64, enabled: Bool) throws { + try getView(viewId).setConsumeMyLocationButtonClickEventsEnabled(enabled: enabled) + } + + func isConsumeMyLocationButtonClickEventsEnabled(viewId: Int64) throws -> Bool { + try getView(viewId).isConsumeMyLocationButtonClickEventsEnabled() + } + + func getMinZoomPreference(viewId: Int64) throws -> Double { + try Double(getView(viewId).getMinZoomPreference()) + } + + func getMaxZoomPreference(viewId: Int64) throws -> Double { + try Double(getView(viewId).getMaxZoomPreference()) + } + + func resetMinMaxZoomPreference(viewId: Int64) throws { + try getView(viewId).resetMinMaxZoomPreference() + } + + func setMinZoomPreference(viewId: Int64, minZoomPreference: Double) throws { + try getView(viewId).setMinZoomPreference(minZoomPreference: Float(minZoomPreference)) + } + + func setMaxZoomPreference(viewId: Int64, maxZoomPreference: Double) throws { + try getView(viewId).setMaxZoomPreference(maxZoomPreference: Float(maxZoomPreference)) + } + + func registerOnCameraChangedListener(viewId: Int64) throws { + try getView(viewId).registerOnCameraChangedListener() + } } diff --git a/ios/Classes/ImageRegistry.swift b/ios/Classes/ImageRegistry.swift new file mode 100644 index 0000000..ff835bb --- /dev/null +++ b/ios/Classes/ImageRegistry.swift @@ -0,0 +1,67 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import Foundation + +enum GoogleMapsImageRegistryError: Error { + case imageDecodingFailed +} + +class ImageRegistry { + var registeredImages: [RegisteredImage] = [] + + func registerBitmapImage(imageId: String, bytes: Data, imagePixelRatio: Double, width: Double?, + height: Double?) throws -> ImageDescriptorDto { + guard var image = UIImage(data: bytes, scale: imagePixelRatio) else { + throw GoogleMapsImageRegistryError.imageDecodingFailed + } + + if width != nil, height != nil { + image = ImageResizer.resize( + image: image, + size: CGSize(width: CGFloat(width!), height: CGFloat(height!)) + ) + } else if width != nil { + image = ImageResizer.resize(image: image, width: CGFloat(width!)) + } else if height != nil { + image = ImageResizer.resize(image: image, height: CGFloat(height!)) + } + + registeredImages.append(RegisteredImage( + imageId: imageId, + image: image, + imagePixelRatio: imagePixelRatio, + width: width, + height: height + )) + return ImageDescriptorDto( + registeredImageId: imageId, + imagePixelRatio: imagePixelRatio, + width: width, + height: height + ) + } + + func findRegisteredImage(imageId: String) -> RegisteredImage? { + registeredImages.first(where: { $0.imageId == imageId }) + } + + func unregisterImage(imageId: String) { + registeredImages.removeAll(where: { $0.imageId == imageId }) + } + + func clearRegisteredImages() { + registeredImages.removeAll() + } +} diff --git a/ios/Classes/ImageResizer.swift b/ios/Classes/ImageResizer.swift new file mode 100644 index 0000000..6c79f99 --- /dev/null +++ b/ios/Classes/ImageResizer.swift @@ -0,0 +1,48 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import Foundation + +enum ImageResizer { + static func resize(image: UIImage, width: CGFloat) -> UIImage { + let height = (width / image.size.width) * image.size.height + return resize(image: image, size: CGSize(width: width, height: height)) + } + + static func resize(image: UIImage, height: CGFloat) -> UIImage { + let width = (height / image.size.height) * image.size.width + return resize(image: image, size: CGSize(width: width, height: height)) + } + + static func resize(image: UIImage, size: CGSize) -> UIImage { + // Check if scaling is needed + guard abs((image.size.width * image.scale) - size.width) > 0 || + abs((image.size.height * image.scale) - size.height) > 0 else { + return image + } + + if abs(image.size.width / image.size.height - size.width / size.height) < 1e-2 { + // Scaled image has close to same aspect ratio, + // updating image scale instead of resizing image. + let scale = (image.scale * ((image.size.width * image.scale) / size.width)) + return UIImage(cgImage: image.cgImage!, scale: scale, orientation: image.imageOrientation) + } else { + UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) + image.draw(in: CGRect(origin: .zero, size: size)) + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return newImage! + } + } +} diff --git a/ios/Classes/MapConfiguration.swift b/ios/Classes/MapConfiguration.swift index c1d9724..dbf328c 100644 --- a/ios/Classes/MapConfiguration.swift +++ b/ios/Classes/MapConfiguration.swift @@ -15,6 +15,15 @@ import Foundation import GoogleMaps +// Determines the initial visibility of the navigation UI on map initialization. +enum NavigationUIEnabledPreference { + // Navigation UI gets enabled if the navigation + // session has already been successfully started. + case automatic + /// Navigation UI is disabled. + case disabled +} + struct MapConfiguration { // MapView related configurations var cameraPosition: GMSCameraPosition @@ -28,9 +37,6 @@ struct MapConfiguration { var cameraTargetBounds: GMSCoordinateBounds? var minZoomPreference: Float? var maxZoomPreference: Float? - - // NavigationView related configurations - var navigationUIEnabled: Bool? } extension MapConfiguration { @@ -51,11 +57,6 @@ extension MapConfiguration { minZoomPreference ?? kGMSMinZoomLevel, maxZoom: maxZoomPreference ?? kGMSMaxZoomLevel ) - - // If navigationUIEnabled is not null, process the state. - if let isNavigationEnabled = navigationUIEnabled { - mapView.isNavigationEnabled = isNavigationEnabled - } } // Applies the configuration to the given diff --git a/ios/Classes/MarkerController.swift b/ios/Classes/MarkerController.swift index 21def6a..7bf58eb 100644 --- a/ios/Classes/MarkerController.swift +++ b/ios/Classes/MarkerController.swift @@ -20,13 +20,16 @@ class MarkerController { let markerId: String let gmsMarker = GMSMarker() var consumeTapEvents = false + var registeredImage: RegisteredImage? init(markerId: String) { self.markerId = markerId } + private let defaultImageDto: ImageDescriptorDto = .init() + /// Update [GMSMarker] instance with values from pigeon [MarkerDto] class - func update(from markerDto: MarkerDto) { + func update(from markerDto: MarkerDto, imageRegistry: ImageRegistry) { gmsMarker.isDraggable = markerDto.options.draggable gmsMarker.isFlat = markerDto.options.flat @@ -49,31 +52,42 @@ class MarkerController { ) consumeTapEvents = markerDto.options.consumeTapEvents + + if let imageId = markerDto.options.icon.registeredImageId, + let registeredImage = imageRegistry.findRegisteredImage(imageId: imageId) { + gmsMarker.icon = registeredImage.image + self.registeredImage = registeredImage + } else { + gmsMarker.icon = nil + registeredImage = nil + } } func toMarkerDto() -> MarkerDto { - MarkerDto(markerId: markerId, - options: MarkerOptionsDto(alpha: Double(gmsMarker.opacity), - anchor: MarkerAnchorDto( - u: gmsMarker.groundAnchor.x, - v: gmsMarker.groundAnchor.y - ), - draggable: gmsMarker.isDraggable, - flat: gmsMarker.isFlat, - consumeTapEvents: consumeTapEvents, - position: LatLngDto( - latitude: gmsMarker.position.latitude, - longitude: gmsMarker.position.longitude - ), - rotation: gmsMarker.rotation, - infoWindow: InfoWindowDto( - title: gmsMarker.title, - snippet: gmsMarker.snippet, - anchor: MarkerAnchorDto( - u: gmsMarker.infoWindowAnchor.x, - v: gmsMarker.infoWindowAnchor.y - ) - ), visible: gmsMarker.map != nil, - zIndex: Double(gmsMarker.zIndex))) + let options = MarkerOptionsDto(alpha: Double(gmsMarker.opacity), + anchor: MarkerAnchorDto( + u: gmsMarker.groundAnchor.x, + v: gmsMarker.groundAnchor.y + ), + draggable: gmsMarker.isDraggable, + flat: gmsMarker.isFlat, + consumeTapEvents: consumeTapEvents, + position: LatLngDto( + latitude: gmsMarker.position.latitude, + longitude: gmsMarker.position.longitude + ), + rotation: gmsMarker.rotation, + infoWindow: InfoWindowDto( + title: gmsMarker.title, + snippet: gmsMarker.snippet, + anchor: MarkerAnchorDto( + u: gmsMarker.infoWindowAnchor.x, + v: gmsMarker.infoWindowAnchor.y + ) + ), visible: gmsMarker.map != nil, + zIndex: Double(gmsMarker.zIndex), + icon: registeredImage?.toImageDescriptorDto() ?? defaultImageDto) + return MarkerDto(markerId: markerId, + options: options) } } diff --git a/ios/Classes/RegisteredImage.swift b/ios/Classes/RegisteredImage.swift new file mode 100644 index 0000000..d9fb487 --- /dev/null +++ b/ios/Classes/RegisteredImage.swift @@ -0,0 +1,32 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import Foundation + +struct RegisteredImage { + let imageId: String + let image: UIImage + let imagePixelRatio: Double + let width: Double? + let height: Double? + + func toImageDescriptorDto() -> ImageDescriptorDto { + ImageDescriptorDto( + registeredImageId: imageId, + imagePixelRatio: imagePixelRatio, + width: width, + height: height + ) + } +} diff --git a/ios/Classes/messages.g.swift b/ios/Classes/messages.g.swift index a83bedf..ae668c9 100644 --- a/ios/Classes/messages.g.swift +++ b/ios/Classes/messages.g.swift @@ -60,6 +60,15 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +/// Determines the initial visibility of the navigation UI on map initialization. +enum NavigationUIEnabledPreferenceDto: Int { + /// Navigation UI gets enabled if the navigation + /// session has already been successfully started. + case automatic = 0 + /// Navigation UI is disabled. + case disabled = 1 +} + enum MapTypeDto: Int { case none = 0 case normal = 1 @@ -99,10 +108,13 @@ enum PatternTypeDto: Int { case gap = 2 } -enum NavigationSessionEventTypeDto: Int { - case arrivalEvent = 0 - case routeChanged = 1 - case errorReceived = 2 +enum CameraEventTypeDto: Int { + case moveStartedByApi = 0 + case moveStartedByGesture = 1 + case onCameraMove = 2 + case onCameraIdle = 3 + case onCameraStartedFollowingLocation = 4 + case onCameraStoppedFollowingLocation = 5 } enum AlternateRoutesStrategyDto: Int { @@ -258,19 +270,19 @@ struct MapOptionsDto { /// Generated class from Pigeon that represents data sent in messages. struct NavigationViewOptionsDto { /// Determines the initial visibility of the navigation UI on map initialization. - var navigationUIEnabled: Bool + var navigationUIEnabledPreference: NavigationUIEnabledPreferenceDto static func fromList(_ list: [Any?]) -> NavigationViewOptionsDto? { - let navigationUIEnabled = list[0] as! Bool + let navigationUIEnabledPreference = NavigationUIEnabledPreferenceDto(rawValue: list[0] as! Int)! return NavigationViewOptionsDto( - navigationUIEnabled: navigationUIEnabled + navigationUIEnabledPreference: navigationUIEnabledPreference ) } func toList() -> [Any?] { [ - navigationUIEnabled, + navigationUIEnabledPreference.rawValue, ] } } @@ -371,6 +383,7 @@ struct MarkerOptionsDto { var infoWindow: InfoWindowDto var visible: Bool var zIndex: Double + var icon: ImageDescriptorDto static func fromList(_ list: [Any?]) -> MarkerOptionsDto? { let alpha = list[0] as! Double @@ -383,6 +396,7 @@ struct MarkerOptionsDto { let infoWindow = InfoWindowDto.fromList(list[7] as! [Any?])! let visible = list[8] as! Bool let zIndex = list[9] as! Double + let icon = ImageDescriptorDto.fromList(list[10] as! [Any?])! return MarkerOptionsDto( alpha: alpha, @@ -394,7 +408,8 @@ struct MarkerOptionsDto { rotation: rotation, infoWindow: infoWindow, visible: visible, - zIndex: zIndex + zIndex: zIndex, + icon: icon ) } @@ -410,6 +425,38 @@ struct MarkerOptionsDto { infoWindow.toList(), visible, zIndex, + icon.toList(), + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ImageDescriptorDto { + var registeredImageId: String? + var imagePixelRatio: Double? + var width: Double? + var height: Double? + + static func fromList(_ list: [Any?]) -> ImageDescriptorDto? { + let registeredImageId: String? = nilOrValue(list[0]) + let imagePixelRatio: Double? = nilOrValue(list[1]) + let width: Double? = nilOrValue(list[2]) + let height: Double? = nilOrValue(list[3]) + + return ImageDescriptorDto( + registeredImageId: registeredImageId, + imagePixelRatio: imagePixelRatio, + width: width, + height: height + ) + } + + func toList() -> [Any?] { + [ + registeredImageId, + imagePixelRatio, + width, + height, ] } } @@ -464,64 +511,6 @@ struct MarkerAnchorDto { } } -/// Generated class from Pigeon that represents data sent in messages. -struct MarkerEventDto { - var viewId: Int64 - var markerId: String - var eventType: MarkerEventTypeDto - - static func fromList(_ list: [Any?]) -> MarkerEventDto? { - let viewId = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let markerId = list[1] as! String - let eventType = MarkerEventTypeDto(rawValue: list[2] as! Int)! - - return MarkerEventDto( - viewId: viewId, - markerId: markerId, - eventType: eventType - ) - } - - func toList() -> [Any?] { - [ - viewId, - markerId, - eventType.rawValue, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct MarkerDragEventDto { - var viewId: Int64 - var markerId: String - var eventType: MarkerDragEventTypeDto - var position: LatLngDto - - static func fromList(_ list: [Any?]) -> MarkerDragEventDto? { - let viewId = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let markerId = list[1] as! String - let eventType = MarkerDragEventTypeDto(rawValue: list[2] as! Int)! - let position = LatLngDto.fromList(list[3] as! [Any?])! - - return MarkerDragEventDto( - viewId: viewId, - markerId: markerId, - eventType: eventType, - position: position - ) - } - - func toList() -> [Any?] { - [ - viewId, - markerId, - eventType.rawValue, - position.toList(), - ] - } -} - /// Generated class from Pigeon that represents data sent in messages. struct PolygonDto { var polygonId: String @@ -615,29 +604,6 @@ struct PolygonHoleDto { } } -/// Generated class from Pigeon that represents data sent in messages. -struct PolygonClickedEventDto { - var viewId: Int64 - var polygonId: String - - static func fromList(_ list: [Any?]) -> PolygonClickedEventDto? { - let viewId = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let polygonId = list[1] as! String - - return PolygonClickedEventDto( - viewId: viewId, - polygonId: polygonId - ) - } - - func toList() -> [Any?] { - [ - viewId, - polygonId, - ] - } -} - /// Generated class from Pigeon that represents data sent in messages. struct StyleSpanStrokeStyleDto { var solidColor: Int64? @@ -797,29 +763,6 @@ struct PolylineOptionsDto { } } -/// Generated class from Pigeon that represents data sent in messages. -struct PolylineClickedEventDto { - var viewId: Int64 - var polylineId: String - - static func fromList(_ list: [Any?]) -> PolylineClickedEventDto? { - let viewId = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let polylineId = list[1] as! String - - return PolylineClickedEventDto( - viewId: viewId, - polylineId: polylineId - ) - } - - func toList() -> [Any?] { - [ - viewId, - polylineId, - ] - } -} - /// Generated class from Pigeon that represents data sent in messages. struct CircleDto { /// Identifies circle. @@ -897,47 +840,28 @@ struct CircleOptionsDto { } /// Generated class from Pigeon that represents data sent in messages. -struct CircleClickedEventDto { - var viewId: Int64 - var circleId: String - - static func fromList(_ list: [Any?]) -> CircleClickedEventDto? { - let viewId = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let circleId = list[1] as! String - - return CircleClickedEventDto( - viewId: viewId, - circleId: circleId - ) - } - - func toList() -> [Any?] { - [ - viewId, - circleId, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct NavigationSessionEventDto { - var type: NavigationSessionEventTypeDto - var message: String +struct RouteTokenOptionsDto { + var routeToken: String + var travelMode: TravelModeDto? - static func fromList(_ list: [Any?]) -> NavigationSessionEventDto? { - let type = NavigationSessionEventTypeDto(rawValue: list[0] as! Int)! - let message = list[1] as! String + static func fromList(_ list: [Any?]) -> RouteTokenOptionsDto? { + let routeToken = list[0] as! String + var travelMode: TravelModeDto? + let travelModeEnumVal: Int? = nilOrValue(list[1]) + if let travelModeRawValue = travelModeEnumVal { + travelMode = TravelModeDto(rawValue: travelModeRawValue)! + } - return NavigationSessionEventDto( - type: type, - message: message + return RouteTokenOptionsDto( + routeToken: routeToken, + travelMode: travelMode ) } func toList() -> [Any?] { [ - type.rawValue, - message, + routeToken, + travelMode?.rawValue, ] } } @@ -947,6 +871,7 @@ struct DestinationsDto { var waypoints: [NavigationWaypointDto?] var displayOptions: NavigationDisplayOptionsDto var routingOptions: RoutingOptionsDto? + var routeTokenOptions: RouteTokenOptionsDto? static func fromList(_ list: [Any?]) -> DestinationsDto? { let waypoints = list[0] as! [NavigationWaypointDto?] @@ -955,11 +880,16 @@ struct DestinationsDto { if let routingOptionsList: [Any?] = nilOrValue(list[2]) { routingOptions = RoutingOptionsDto.fromList(routingOptionsList) } + var routeTokenOptions: RouteTokenOptionsDto? + if let routeTokenOptionsList: [Any?] = nilOrValue(list[3]) { + routeTokenOptions = RouteTokenOptionsDto.fromList(routeTokenOptionsList) + } return DestinationsDto( waypoints: waypoints, displayOptions: displayOptions, - routingOptions: routingOptions + routingOptions: routingOptions, + routeTokenOptions: routeTokenOptions ) } @@ -968,6 +898,7 @@ struct DestinationsDto { waypoints, displayOptions.toList(), routingOptions?.toList(), + routeTokenOptions?.toList(), ] } } @@ -1241,146 +1172,6 @@ struct SpeedingUpdatedEventDto { } } -/// Generated class from Pigeon that represents data sent in messages. -struct RoadSnappedLocationUpdatedEventDto { - var location: LatLngDto - - static func fromList(_ list: [Any?]) -> RoadSnappedLocationUpdatedEventDto? { - let location = LatLngDto.fromList(list[0] as! [Any?])! - - return RoadSnappedLocationUpdatedEventDto( - location: location - ) - } - - func toList() -> [Any?] { - [ - location.toList(), - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct RoadSnappedRawLocationUpdatedEventDto { - var location: LatLngDto? - - static func fromList(_ list: [Any?]) -> RoadSnappedRawLocationUpdatedEventDto? { - var location: LatLngDto? - if let locationList: [Any?] = nilOrValue(list[0]) { - location = LatLngDto.fromList(locationList) - } - - return RoadSnappedRawLocationUpdatedEventDto( - location: location - ) - } - - func toList() -> [Any?] { - [ - location?.toList(), - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct OnArrivalEventDto { - var waypoint: NavigationWaypointDto - - static func fromList(_ list: [Any?]) -> OnArrivalEventDto? { - let waypoint = NavigationWaypointDto.fromList(list[0] as! [Any?])! - - return OnArrivalEventDto( - waypoint: waypoint - ) - } - - func toList() -> [Any?] { - [ - waypoint.toList(), - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct RemainingTimeOrDistanceChangedEventDto { - var remainingTime: Double - var remainingDistance: Double - - static func fromList(_ list: [Any?]) -> RemainingTimeOrDistanceChangedEventDto? { - let remainingTime = list[0] as! Double - let remainingDistance = list[1] as! Double - - return RemainingTimeOrDistanceChangedEventDto( - remainingTime: remainingTime, - remainingDistance: remainingDistance - ) - } - - func toList() -> [Any?] { - [ - remainingTime, - remainingDistance, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct RouteChangedEventDto { - var message: String - - static func fromList(_ list: [Any?]) -> RouteChangedEventDto? { - let message = list[0] as! String - - return RouteChangedEventDto( - message: message - ) - } - - func toList() -> [Any?] { - [ - message, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct ReroutingEventDto { - var message: String - - static func fromList(_ list: [Any?]) -> ReroutingEventDto? { - let message = list[0] as! String - - return ReroutingEventDto( - message: message - ) - } - - func toList() -> [Any?] { - [ - message, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct TrafficUpdatedEventDto { - var message: String - - static func fromList(_ list: [Any?]) -> TrafficUpdatedEventDto? { - let message = list[0] as! String - - return TrafficUpdatedEventDto( - message: message - ) - } - - func toList() -> [Any?] { - [ - message, - ] - } -} - /// Generated class from Pigeon that represents data sent in messages. struct SpeedAlertOptionsDto { var severityUpgradeDurationSeconds: Double @@ -1606,34 +1397,36 @@ private class NavigationViewApiCodecReader: FlutterStandardReader { case 130: return CircleOptionsDto.fromList(readValue() as! [Any?]) case 131: - return InfoWindowDto.fromList(readValue() as! [Any?]) + return ImageDescriptorDto.fromList(readValue() as! [Any?]) case 132: - return LatLngBoundsDto.fromList(readValue() as! [Any?]) + return InfoWindowDto.fromList(readValue() as! [Any?]) case 133: - return LatLngDto.fromList(readValue() as! [Any?]) + return LatLngBoundsDto.fromList(readValue() as! [Any?]) case 134: return LatLngDto.fromList(readValue() as! [Any?]) case 135: - return MarkerAnchorDto.fromList(readValue() as! [Any?]) + return LatLngDto.fromList(readValue() as! [Any?]) case 136: - return MarkerDto.fromList(readValue() as! [Any?]) + return MarkerAnchorDto.fromList(readValue() as! [Any?]) case 137: - return MarkerOptionsDto.fromList(readValue() as! [Any?]) + return MarkerDto.fromList(readValue() as! [Any?]) case 138: - return PatternItemDto.fromList(readValue() as! [Any?]) + return MarkerOptionsDto.fromList(readValue() as! [Any?]) case 139: - return PolygonDto.fromList(readValue() as! [Any?]) + return PatternItemDto.fromList(readValue() as! [Any?]) case 140: - return PolygonHoleDto.fromList(readValue() as! [Any?]) + return PolygonDto.fromList(readValue() as! [Any?]) case 141: - return PolygonOptionsDto.fromList(readValue() as! [Any?]) + return PolygonHoleDto.fromList(readValue() as! [Any?]) case 142: - return PolylineDto.fromList(readValue() as! [Any?]) + return PolygonOptionsDto.fromList(readValue() as! [Any?]) case 143: - return PolylineOptionsDto.fromList(readValue() as! [Any?]) + return PolylineDto.fromList(readValue() as! [Any?]) case 144: - return StyleSpanDto.fromList(readValue() as! [Any?]) + return PolylineOptionsDto.fromList(readValue() as! [Any?]) case 145: + return StyleSpanDto.fromList(readValue() as! [Any?]) + case 146: return StyleSpanStrokeStyleDto.fromList(readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -1652,51 +1445,54 @@ private class NavigationViewApiCodecWriter: FlutterStandardWriter { } else if let value = value as? CircleOptionsDto { super.writeByte(130) super.writeValue(value.toList()) - } else if let value = value as? InfoWindowDto { + } else if let value = value as? ImageDescriptorDto { super.writeByte(131) super.writeValue(value.toList()) - } else if let value = value as? LatLngBoundsDto { + } else if let value = value as? InfoWindowDto { super.writeByte(132) super.writeValue(value.toList()) - } else if let value = value as? LatLngDto { + } else if let value = value as? LatLngBoundsDto { super.writeByte(133) super.writeValue(value.toList()) } else if let value = value as? LatLngDto { super.writeByte(134) super.writeValue(value.toList()) - } else if let value = value as? MarkerAnchorDto { + } else if let value = value as? LatLngDto { super.writeByte(135) super.writeValue(value.toList()) - } else if let value = value as? MarkerDto { + } else if let value = value as? MarkerAnchorDto { super.writeByte(136) super.writeValue(value.toList()) - } else if let value = value as? MarkerOptionsDto { + } else if let value = value as? MarkerDto { super.writeByte(137) super.writeValue(value.toList()) - } else if let value = value as? PatternItemDto { + } else if let value = value as? MarkerOptionsDto { super.writeByte(138) super.writeValue(value.toList()) - } else if let value = value as? PolygonDto { + } else if let value = value as? PatternItemDto { super.writeByte(139) super.writeValue(value.toList()) - } else if let value = value as? PolygonHoleDto { + } else if let value = value as? PolygonDto { super.writeByte(140) super.writeValue(value.toList()) - } else if let value = value as? PolygonOptionsDto { + } else if let value = value as? PolygonHoleDto { super.writeByte(141) super.writeValue(value.toList()) - } else if let value = value as? PolylineDto { + } else if let value = value as? PolygonOptionsDto { super.writeByte(142) super.writeValue(value.toList()) - } else if let value = value as? PolylineOptionsDto { + } else if let value = value as? PolylineDto { super.writeByte(143) super.writeValue(value.toList()) - } else if let value = value as? StyleSpanDto { + } else if let value = value as? PolylineOptionsDto { super.writeByte(144) super.writeValue(value.toList()) - } else if let value = value as? StyleSpanStrokeStyleDto { + } else if let value = value as? StyleSpanDto { super.writeByte(145) super.writeValue(value.toList()) + } else if let value = value as? StyleSpanStrokeStyleDto { + super.writeByte(146) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -1721,27 +1517,27 @@ class NavigationViewApiCodec: FlutterStandardMessageCodec { protocol NavigationViewApi { func awaitMapReady(viewId: Int64, completion: @escaping (Result) -> Void) func isMyLocationEnabled(viewId: Int64) throws -> Bool - func enableMyLocation(viewId: Int64, enabled: Bool) throws + func setMyLocationEnabled(viewId: Int64, enabled: Bool) throws func getMyLocation(viewId: Int64) throws -> LatLngDto? func getMapType(viewId: Int64) throws -> MapTypeDto func setMapType(viewId: Int64, mapType: MapTypeDto) throws func setMapStyle(viewId: Int64, styleJson: String) throws func isNavigationTripProgressBarEnabled(viewId: Int64) throws -> Bool - func enableNavigationTripProgressBar(viewId: Int64, enabled: Bool) throws + func setNavigationTripProgressBarEnabled(viewId: Int64, enabled: Bool) throws func isNavigationHeaderEnabled(viewId: Int64) throws -> Bool - func enableNavigationHeader(viewId: Int64, enabled: Bool) throws + func setNavigationHeaderEnabled(viewId: Int64, enabled: Bool) throws func isNavigationFooterEnabled(viewId: Int64) throws -> Bool - func enableNavigationFooter(viewId: Int64, enabled: Bool) throws + func setNavigationFooterEnabled(viewId: Int64, enabled: Bool) throws func isRecenterButtonEnabled(viewId: Int64) throws -> Bool - func enableRecenterButton(viewId: Int64, enabled: Bool) throws + func setRecenterButtonEnabled(viewId: Int64, enabled: Bool) throws func isSpeedLimitIconEnabled(viewId: Int64) throws -> Bool - func enableSpeedLimitIcon(viewId: Int64, enabled: Bool) throws + func setSpeedLimitIconEnabled(viewId: Int64, enabled: Bool) throws func isSpeedometerEnabled(viewId: Int64) throws -> Bool - func enableSpeedometer(viewId: Int64, enabled: Bool) throws - func isIncidentCardsEnabled(viewId: Int64) throws -> Bool - func enableIncidentCards(viewId: Int64, enabled: Bool) throws + func setSpeedometerEnabled(viewId: Int64, enabled: Bool) throws + func isTrafficIncidentCardsEnabled(viewId: Int64) throws -> Bool + func setTrafficIncidentCardsEnabled(viewId: Int64, enabled: Bool) throws func isNavigationUIEnabled(viewId: Int64) throws -> Bool - func enableNavigationUI(viewId: Int64, enabled: Bool) throws + func setNavigationUIEnabled(viewId: Int64, enabled: Bool) throws func getCameraPosition(viewId: Int64) throws -> CameraPositionDto func getVisibleRegion(viewId: Int64) throws -> LatLngBoundsDto func followMyLocation(viewId: Int64, perspective: CameraPerspectiveDto, zoomLevel: Double?) throws @@ -1769,17 +1565,24 @@ protocol NavigationViewApi { func moveCameraByZoom(viewId: Int64, zoomBy: Double, focusDx: Double?, focusDy: Double?) throws func moveCameraToZoom(viewId: Int64, zoom: Double) throws func showRouteOverview(viewId: Int64) throws - func enableMyLocationButton(viewId: Int64, enabled: Bool) throws - func enableZoomGestures(viewId: Int64, enabled: Bool) throws - func enableZoomControls(viewId: Int64, enabled: Bool) throws - func enableCompass(viewId: Int64, enabled: Bool) throws - func enableRotateGestures(viewId: Int64, enabled: Bool) throws - func enableScrollGestures(viewId: Int64, enabled: Bool) throws - func enableScrollGesturesDuringRotateOrZoom(viewId: Int64, enabled: Bool) throws - func enableTiltGestures(viewId: Int64, enabled: Bool) throws - func enableMapToolbar(viewId: Int64, enabled: Bool) throws - func enableTraffic(viewId: Int64, enabled: Bool) throws + func getMinZoomPreference(viewId: Int64) throws -> Double + func getMaxZoomPreference(viewId: Int64) throws -> Double + func resetMinMaxZoomPreference(viewId: Int64) throws + func setMinZoomPreference(viewId: Int64, minZoomPreference: Double) throws + func setMaxZoomPreference(viewId: Int64, maxZoomPreference: Double) throws + func setMyLocationButtonEnabled(viewId: Int64, enabled: Bool) throws + func setConsumeMyLocationButtonClickEventsEnabled(viewId: Int64, enabled: Bool) throws + func setZoomGesturesEnabled(viewId: Int64, enabled: Bool) throws + func setZoomControlsEnabled(viewId: Int64, enabled: Bool) throws + func setCompassEnabled(viewId: Int64, enabled: Bool) throws + func setRotateGesturesEnabled(viewId: Int64, enabled: Bool) throws + func setScrollGesturesEnabled(viewId: Int64, enabled: Bool) throws + func setScrollGesturesDuringRotateOrZoomEnabled(viewId: Int64, enabled: Bool) throws + func setTiltGesturesEnabled(viewId: Int64, enabled: Bool) throws + func setMapToolbarEnabled(viewId: Int64, enabled: Bool) throws + func setTrafficEnabled(viewId: Int64, enabled: Bool) throws func isMyLocationButtonEnabled(viewId: Int64) throws -> Bool + func isConsumeMyLocationButtonClickEventsEnabled(viewId: Int64) throws -> Bool func isZoomGesturesEnabled(viewId: Int64) throws -> Bool func isZoomControlsEnabled(viewId: Int64) throws -> Bool func isCompassEnabled(viewId: Int64) throws -> Bool @@ -1810,6 +1613,7 @@ protocol NavigationViewApi { func updateCircles(viewId: Int64, circles: [CircleDto]) throws -> [CircleDto] func removeCircles(viewId: Int64, circles: [CircleDto]) throws func clearCircles(viewId: Int64) throws + func registerOnCameraChangedListener(viewId: Int64) throws } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -1858,25 +1662,25 @@ enum NavigationViewApiSetup { } else { isMyLocationEnabledChannel.setMessageHandler(nil) } - let enableMyLocationChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocation", + let setMyLocationEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableMyLocationChannel.setMessageHandler { message, reply in + setMyLocationEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableMyLocation(viewId: viewIdArg, enabled: enabledArg) + try api.setMyLocationEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableMyLocationChannel.setMessageHandler(nil) + setMyLocationEnabledChannel.setMessageHandler(nil) } let getMyLocationChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMyLocation", @@ -1975,25 +1779,25 @@ enum NavigationViewApiSetup { } else { isNavigationTripProgressBarEnabledChannel.setMessageHandler(nil) } - let enableNavigationTripProgressBarChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationTripProgressBar", + let setNavigationTripProgressBarEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationTripProgressBarEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableNavigationTripProgressBarChannel.setMessageHandler { message, reply in + setNavigationTripProgressBarEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableNavigationTripProgressBar(viewId: viewIdArg, enabled: enabledArg) + try api.setNavigationTripProgressBarEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableNavigationTripProgressBarChannel.setMessageHandler(nil) + setNavigationTripProgressBarEnabledChannel.setMessageHandler(nil) } let isNavigationHeaderEnabledChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isNavigationHeaderEnabled", @@ -2014,25 +1818,25 @@ enum NavigationViewApiSetup { } else { isNavigationHeaderEnabledChannel.setMessageHandler(nil) } - let enableNavigationHeaderChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationHeader", + let setNavigationHeaderEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationHeaderEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableNavigationHeaderChannel.setMessageHandler { message, reply in + setNavigationHeaderEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableNavigationHeader(viewId: viewIdArg, enabled: enabledArg) + try api.setNavigationHeaderEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableNavigationHeaderChannel.setMessageHandler(nil) + setNavigationHeaderEnabledChannel.setMessageHandler(nil) } let isNavigationFooterEnabledChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isNavigationFooterEnabled", @@ -2053,25 +1857,25 @@ enum NavigationViewApiSetup { } else { isNavigationFooterEnabledChannel.setMessageHandler(nil) } - let enableNavigationFooterChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationFooter", + let setNavigationFooterEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationFooterEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableNavigationFooterChannel.setMessageHandler { message, reply in + setNavigationFooterEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableNavigationFooter(viewId: viewIdArg, enabled: enabledArg) + try api.setNavigationFooterEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableNavigationFooterChannel.setMessageHandler(nil) + setNavigationFooterEnabledChannel.setMessageHandler(nil) } let isRecenterButtonEnabledChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isRecenterButtonEnabled", @@ -2092,25 +1896,25 @@ enum NavigationViewApiSetup { } else { isRecenterButtonEnabledChannel.setMessageHandler(nil) } - let enableRecenterButtonChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRecenterButton", + let setRecenterButtonEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRecenterButtonEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableRecenterButtonChannel.setMessageHandler { message, reply in + setRecenterButtonEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableRecenterButton(viewId: viewIdArg, enabled: enabledArg) + try api.setRecenterButtonEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableRecenterButtonChannel.setMessageHandler(nil) + setRecenterButtonEnabledChannel.setMessageHandler(nil) } let isSpeedLimitIconEnabledChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isSpeedLimitIconEnabled", @@ -2131,25 +1935,25 @@ enum NavigationViewApiSetup { } else { isSpeedLimitIconEnabledChannel.setMessageHandler(nil) } - let enableSpeedLimitIconChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedLimitIcon", + let setSpeedLimitIconEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedLimitIconEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableSpeedLimitIconChannel.setMessageHandler { message, reply in + setSpeedLimitIconEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableSpeedLimitIcon(viewId: viewIdArg, enabled: enabledArg) + try api.setSpeedLimitIconEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableSpeedLimitIconChannel.setMessageHandler(nil) + setSpeedLimitIconEnabledChannel.setMessageHandler(nil) } let isSpeedometerEnabledChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isSpeedometerEnabled", @@ -2170,64 +1974,64 @@ enum NavigationViewApiSetup { } else { isSpeedometerEnabledChannel.setMessageHandler(nil) } - let enableSpeedometerChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedometer", + let setSpeedometerEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedometerEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableSpeedometerChannel.setMessageHandler { message, reply in + setSpeedometerEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableSpeedometer(viewId: viewIdArg, enabled: enabledArg) + try api.setSpeedometerEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableSpeedometerChannel.setMessageHandler(nil) + setSpeedometerEnabledChannel.setMessageHandler(nil) } - let isIncidentCardsEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isIncidentCardsEnabled", + let isTrafficIncidentCardsEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isTrafficIncidentCardsEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - isIncidentCardsEnabledChannel.setMessageHandler { message, reply in + isTrafficIncidentCardsEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) do { - let result = try api.isIncidentCardsEnabled(viewId: viewIdArg) + let result = try api.isTrafficIncidentCardsEnabled(viewId: viewIdArg) reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - isIncidentCardsEnabledChannel.setMessageHandler(nil) + isTrafficIncidentCardsEnabledChannel.setMessageHandler(nil) } - let enableIncidentCardsChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableIncidentCards", + let setTrafficIncidentCardsEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficIncidentCardsEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableIncidentCardsChannel.setMessageHandler { message, reply in + setTrafficIncidentCardsEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableIncidentCards(viewId: viewIdArg, enabled: enabledArg) + try api.setTrafficIncidentCardsEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableIncidentCardsChannel.setMessageHandler(nil) + setTrafficIncidentCardsEnabledChannel.setMessageHandler(nil) } let isNavigationUIEnabledChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isNavigationUIEnabled", @@ -2248,25 +2052,25 @@ enum NavigationViewApiSetup { } else { isNavigationUIEnabledChannel.setMessageHandler(nil) } - let enableNavigationUIChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationUI", + let setNavigationUIEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationUIEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableNavigationUIChannel.setMessageHandler { message, reply in + setNavigationUIEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableNavigationUI(viewId: viewIdArg, enabled: enabledArg) + try api.setNavigationUIEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableNavigationUIChannel.setMessageHandler(nil) + setNavigationUIEnabledChannel.setMessageHandler(nil) } let getCameraPositionChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getCameraPosition", @@ -2708,218 +2512,338 @@ enum NavigationViewApiSetup { } else { showRouteOverviewChannel.setMessageHandler(nil) } - let enableMyLocationButtonChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocationButton", + let getMinZoomPreferenceChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMinZoomPreference", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableMyLocationButtonChannel.setMessageHandler { message, reply in + getMinZoomPreferenceChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let enabledArg = args[1] as! Bool do { - try api.enableMyLocationButton(viewId: viewIdArg, enabled: enabledArg) - reply(wrapResult(nil)) + let result = try api.getMinZoomPreference(viewId: viewIdArg) + reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - enableMyLocationButtonChannel.setMessageHandler(nil) + getMinZoomPreferenceChannel.setMessageHandler(nil) } - let enableZoomGesturesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomGestures", + let getMaxZoomPreferenceChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMaxZoomPreference", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableZoomGesturesChannel.setMessageHandler { message, reply in + getMaxZoomPreferenceChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let enabledArg = args[1] as! Bool do { - try api.enableZoomGestures(viewId: viewIdArg, enabled: enabledArg) - reply(wrapResult(nil)) + let result = try api.getMaxZoomPreference(viewId: viewIdArg) + reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - enableZoomGesturesChannel.setMessageHandler(nil) + getMaxZoomPreferenceChannel.setMessageHandler(nil) } - let enableZoomControlsChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomControls", + let resetMinMaxZoomPreferenceChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.resetMinMaxZoomPreference", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableZoomControlsChannel.setMessageHandler { message, reply in + resetMinMaxZoomPreferenceChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let enabledArg = args[1] as! Bool do { - try api.enableZoomControls(viewId: viewIdArg, enabled: enabledArg) + try api.resetMinMaxZoomPreference(viewId: viewIdArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableZoomControlsChannel.setMessageHandler(nil) + resetMinMaxZoomPreferenceChannel.setMessageHandler(nil) } - let enableCompassChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableCompass", + let setMinZoomPreferenceChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMinZoomPreference", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableCompassChannel.setMessageHandler { message, reply in + setMinZoomPreferenceChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let enabledArg = args[1] as! Bool + let minZoomPreferenceArg = args[1] as! Double do { - try api.enableCompass(viewId: viewIdArg, enabled: enabledArg) + try api.setMinZoomPreference(viewId: viewIdArg, minZoomPreference: minZoomPreferenceArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableCompassChannel.setMessageHandler(nil) + setMinZoomPreferenceChannel.setMessageHandler(nil) } - let enableRotateGesturesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRotateGestures", + let setMaxZoomPreferenceChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMaxZoomPreference", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableRotateGesturesChannel.setMessageHandler { message, reply in + setMaxZoomPreferenceChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let enabledArg = args[1] as! Bool + let maxZoomPreferenceArg = args[1] as! Double do { - try api.enableRotateGestures(viewId: viewIdArg, enabled: enabledArg) + try api.setMaxZoomPreference(viewId: viewIdArg, maxZoomPreference: maxZoomPreferenceArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableRotateGesturesChannel.setMessageHandler(nil) + setMaxZoomPreferenceChannel.setMessageHandler(nil) } - let enableScrollGesturesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGestures", + let setMyLocationButtonEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationButtonEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableScrollGesturesChannel.setMessageHandler { message, reply in + setMyLocationButtonEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableScrollGestures(viewId: viewIdArg, enabled: enabledArg) + try api.setMyLocationButtonEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableScrollGesturesChannel.setMessageHandler(nil) + setMyLocationButtonEnabledChannel.setMessageHandler(nil) } - let enableScrollGesturesDuringRotateOrZoomChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGesturesDuringRotateOrZoom", + let setConsumeMyLocationButtonClickEventsEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setConsumeMyLocationButtonClickEventsEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableScrollGesturesDuringRotateOrZoomChannel.setMessageHandler { message, reply in + setConsumeMyLocationButtonClickEventsEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableScrollGesturesDuringRotateOrZoom(viewId: viewIdArg, enabled: enabledArg) + try api.setConsumeMyLocationButtonClickEventsEnabled( + viewId: viewIdArg, + enabled: enabledArg + ) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableScrollGesturesDuringRotateOrZoomChannel.setMessageHandler(nil) + setConsumeMyLocationButtonClickEventsEnabledChannel.setMessageHandler(nil) } - let enableTiltGesturesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTiltGestures", + let setZoomGesturesEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomGesturesEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableTiltGesturesChannel.setMessageHandler { message, reply in + setZoomGesturesEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableTiltGestures(viewId: viewIdArg, enabled: enabledArg) + try api.setZoomGesturesEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableTiltGesturesChannel.setMessageHandler(nil) + setZoomGesturesEnabledChannel.setMessageHandler(nil) } - let enableMapToolbarChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMapToolbar", + let setZoomControlsEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomControlsEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableMapToolbarChannel.setMessageHandler { message, reply in + setZoomControlsEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableMapToolbar(viewId: viewIdArg, enabled: enabledArg) + try api.setZoomControlsEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableMapToolbarChannel.setMessageHandler(nil) + setZoomControlsEnabledChannel.setMessageHandler(nil) } - let enableTrafficChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTraffic", + let setCompassEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setCompassEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - enableTrafficChannel.setMessageHandler { message, reply in + setCompassEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let enabledArg = args[1] as! Bool do { - try api.enableTraffic(viewId: viewIdArg, enabled: enabledArg) + try api.setCompassEnabled(viewId: viewIdArg, enabled: enabledArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - enableTrafficChannel.setMessageHandler(nil) + setCompassEnabledChannel.setMessageHandler(nil) } - let isMyLocationButtonEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isMyLocationButtonEnabled", + let setRotateGesturesEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRotateGesturesEnabled", binaryMessenger: binaryMessenger, codec: codec ) if let api { - isMyLocationButtonEnabledChannel.setMessageHandler { message, reply in + setRotateGesturesEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let enabledArg = args[1] as! Bool do { - let result = try api.isMyLocationButtonEnabled(viewId: viewIdArg) - reply(wrapResult(result)) + try api.setRotateGesturesEnabled(viewId: viewIdArg, enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setRotateGesturesEnabledChannel.setMessageHandler(nil) + } + let setScrollGesturesEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesEnabled", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setScrollGesturesEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let enabledArg = args[1] as! Bool + do { + try api.setScrollGesturesEnabled(viewId: viewIdArg, enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setScrollGesturesEnabledChannel.setMessageHandler(nil) + } + let setScrollGesturesDuringRotateOrZoomEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesDuringRotateOrZoomEnabled", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setScrollGesturesDuringRotateOrZoomEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let enabledArg = args[1] as! Bool + do { + try api.setScrollGesturesDuringRotateOrZoomEnabled(viewId: viewIdArg, enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setScrollGesturesDuringRotateOrZoomEnabledChannel.setMessageHandler(nil) + } + let setTiltGesturesEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTiltGesturesEnabled", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setTiltGesturesEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let enabledArg = args[1] as! Bool + do { + try api.setTiltGesturesEnabled(viewId: viewIdArg, enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setTiltGesturesEnabledChannel.setMessageHandler(nil) + } + let setMapToolbarEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMapToolbarEnabled", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setMapToolbarEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let enabledArg = args[1] as! Bool + do { + try api.setMapToolbarEnabled(viewId: viewIdArg, enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setMapToolbarEnabledChannel.setMessageHandler(nil) + } + let setTrafficEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficEnabled", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setTrafficEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let enabledArg = args[1] as! Bool + do { + try api.setTrafficEnabled(viewId: viewIdArg, enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setTrafficEnabledChannel.setMessageHandler(nil) + } + let isMyLocationButtonEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isMyLocationButtonEnabled", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + isMyLocationButtonEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + do { + let result = try api.isMyLocationButtonEnabled(viewId: viewIdArg) + reply(wrapResult(result)) } catch { reply(wrapError(error)) } @@ -2927,6 +2851,25 @@ enum NavigationViewApiSetup { } else { isMyLocationButtonEnabledChannel.setMessageHandler(nil) } + let isConsumeMyLocationButtonClickEventsEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isConsumeMyLocationButtonClickEventsEnabled", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + isConsumeMyLocationButtonClickEventsEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + do { + let result = try api.isConsumeMyLocationButtonClickEventsEnabled(viewId: viewIdArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isConsumeMyLocationButtonClickEventsEnabledChannel.setMessageHandler(nil) + } let isZoomGesturesEnabledChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isZoomGesturesEnabled", binaryMessenger: binaryMessenger, @@ -3509,6 +3452,167 @@ enum NavigationViewApiSetup { } else { clearCirclesChannel.setMessageHandler(nil) } + let registerOnCameraChangedListenerChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.registerOnCameraChangedListener", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + registerOnCameraChangedListenerChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let viewIdArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + do { + try api.registerOnCameraChangedListener(viewId: viewIdArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + registerOnCameraChangedListenerChannel.setMessageHandler(nil) + } + } +} + +private class ImageRegistryApiCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 128: + return ImageDescriptorDto.fromList(readValue() as! [Any?]) + case 129: + return ImageDescriptorDto.fromList(readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class ImageRegistryApiCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? ImageDescriptorDto { + super.writeByte(128) + super.writeValue(value.toList()) + } else if let value = value as? ImageDescriptorDto { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class ImageRegistryApiCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + ImageRegistryApiCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + ImageRegistryApiCodecWriter(data: data) + } +} + +class ImageRegistryApiCodec: FlutterStandardMessageCodec { + static let shared = ImageRegistryApiCodec(readerWriter: ImageRegistryApiCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol ImageRegistryApi { + func registerBitmapImage(imageId: String, bytes: FlutterStandardTypedData, + imagePixelRatio: Double, width: Double?, + height: Double?) throws -> ImageDescriptorDto + func unregisterImage(imageDescriptor: ImageDescriptorDto) throws + func getRegisteredImages() throws -> [ImageDescriptorDto] + func clearRegisteredImages() throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +enum ImageRegistryApiSetup { + /// The codec used by ImageRegistryApi. + static var codec: FlutterStandardMessageCodec { ImageRegistryApiCodec.shared } + /// Sets up an instance of `ImageRegistryApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ImageRegistryApi?) { + let registerBitmapImageChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + registerBitmapImageChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let imageIdArg = args[0] as! String + let bytesArg = args[1] as! FlutterStandardTypedData + let imagePixelRatioArg = args[2] as! Double + let widthArg: Double? = nilOrValue(args[3]) + let heightArg: Double? = nilOrValue(args[4]) + do { + let result = try api.registerBitmapImage( + imageId: imageIdArg, + bytes: bytesArg, + imagePixelRatio: imagePixelRatioArg, + width: widthArg, + height: heightArg + ) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + registerBitmapImageChannel.setMessageHandler(nil) + } + let unregisterImageChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.unregisterImage", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + unregisterImageChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let imageDescriptorArg = args[0] as! ImageDescriptorDto + do { + try api.unregisterImage(imageDescriptor: imageDescriptorArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + unregisterImageChannel.setMessageHandler(nil) + } + let getRegisteredImagesChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.getRegisteredImages", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + getRegisteredImagesChannel.setMessageHandler { _, reply in + do { + let result = try api.getRegisteredImages() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getRegisteredImagesChannel.setMessageHandler(nil) + } + let clearRegisteredImagesChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.clearRegisteredImages", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + clearRegisteredImagesChannel.setMessageHandler { _, reply in + do { + try api.clearRegisteredImages() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + clearRegisteredImagesChannel.setMessageHandler(nil) + } } } @@ -3516,17 +3620,9 @@ private class NavigationViewEventApiCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { case 128: - return CircleClickedEventDto.fromList(readValue() as! [Any?]) + return CameraPositionDto.fromList(readValue() as! [Any?]) case 129: return LatLngDto.fromList(readValue() as! [Any?]) - case 130: - return MarkerDragEventDto.fromList(readValue() as! [Any?]) - case 131: - return MarkerEventDto.fromList(readValue() as! [Any?]) - case 132: - return PolygonClickedEventDto.fromList(readValue() as! [Any?]) - case 133: - return PolylineClickedEventDto.fromList(readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -3535,24 +3631,12 @@ private class NavigationViewEventApiCodecReader: FlutterStandardReader { private class NavigationViewEventApiCodecWriter: FlutterStandardWriter { override func writeValue(_ value: Any) { - if let value = value as? CircleClickedEventDto { + if let value = value as? CameraPositionDto { super.writeByte(128) super.writeValue(value.toList()) } else if let value = value as? LatLngDto { super.writeByte(129) super.writeValue(value.toList()) - } else if let value = value as? MarkerDragEventDto { - super.writeByte(130) - super.writeValue(value.toList()) - } else if let value = value as? MarkerEventDto { - super.writeByte(131) - super.writeValue(value.toList()) - } else if let value = value as? PolygonClickedEventDto { - super.writeByte(132) - super.writeValue(value.toList()) - } else if let value = value as? PolylineClickedEventDto { - super.writeByte(133) - super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -3582,15 +3666,28 @@ protocol NavigationViewEventApiProtocol { completion: @escaping (Result) -> Void) func onRecenterButtonClicked(viewId viewIdArg: Int64, completion: @escaping (Result) -> Void) - func onMarkerEvent(msg msgArg: MarkerEventDto, + func onMarkerEvent(viewId viewIdArg: Int64, markerId markerIdArg: String, + eventType eventTypeArg: MarkerEventTypeDto, completion: @escaping (Result) -> Void) - func onMarkerDragEvent(msg msgArg: MarkerDragEventDto, + func onMarkerDragEvent(viewId viewIdArg: Int64, markerId markerIdArg: String, + eventType eventTypeArg: MarkerDragEventTypeDto, + position positionArg: LatLngDto, completion: @escaping (Result) -> Void) - func onPolygonClicked(msg msgArg: PolygonClickedEventDto, + func onPolygonClicked(viewId viewIdArg: Int64, polygonId polygonIdArg: String, completion: @escaping (Result) -> Void) - func onPolylineClicked(msg msgArg: PolylineClickedEventDto, + func onPolylineClicked(viewId viewIdArg: Int64, polylineId polylineIdArg: String, completion: @escaping (Result) -> Void) - func onCircleClicked(msg msgArg: CircleClickedEventDto, + func onCircleClicked(viewId viewIdArg: Int64, circleId circleIdArg: String, + completion: @escaping (Result) -> Void) + func onNavigationUIEnabledChanged(viewId viewIdArg: Int64, + navigationUIEnabled navigationUIEnabledArg: Bool, + completion: @escaping (Result) -> Void) + func onMyLocationClicked(viewId viewIdArg: Int64, + completion: @escaping (Result) -> Void) + func onMyLocationButtonClicked(viewId viewIdArg: Int64, + completion: @escaping (Result) -> Void) + func onCameraChanged(viewId viewIdArg: Int64, eventType eventTypeArg: CameraEventTypeDto, + position positionArg: CameraPositionDto, completion: @escaping (Result) -> Void) } @@ -3679,7 +3776,8 @@ class NavigationViewEventApi: NavigationViewEventApiProtocol { } } - func onMarkerEvent(msg msgArg: MarkerEventDto, + func onMarkerEvent(viewId viewIdArg: Int64, markerId markerIdArg: String, + eventType eventTypeArg: MarkerEventTypeDto, completion: @escaping (Result) -> Void) { let channelName = "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerEvent" @@ -3688,7 +3786,7 @@ class NavigationViewEventApi: NavigationViewEventApiProtocol { binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([viewIdArg, markerIdArg, eventTypeArg.rawValue] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -3704,7 +3802,9 @@ class NavigationViewEventApi: NavigationViewEventApiProtocol { } } - func onMarkerDragEvent(msg msgArg: MarkerDragEventDto, + func onMarkerDragEvent(viewId viewIdArg: Int64, markerId markerIdArg: String, + eventType eventTypeArg: MarkerDragEventTypeDto, + position positionArg: LatLngDto, completion: @escaping (Result) -> Void) { let channelName = "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent" @@ -3713,7 +3813,34 @@ class NavigationViewEventApi: NavigationViewEventApiProtocol { binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel + .sendMessage([viewIdArg, markerIdArg, eventTypeArg.rawValue, + positionArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + + func onPolygonClicked(viewId viewIdArg: Int64, polygonId polygonIdArg: String, + completion: @escaping (Result) -> Void) { + let channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolygonClicked" + let channel = FlutterBasicMessageChannel( + name: channelName, + binaryMessenger: binaryMessenger, + codec: codec + ) + channel.sendMessage([viewIdArg, polygonIdArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -3729,16 +3856,16 @@ class NavigationViewEventApi: NavigationViewEventApiProtocol { } } - func onPolygonClicked(msg msgArg: PolygonClickedEventDto, - completion: @escaping (Result) -> Void) { + func onPolylineClicked(viewId viewIdArg: Int64, polylineId polylineIdArg: String, + completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolygonClicked" + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolylineClicked" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([viewIdArg, polylineIdArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -3754,16 +3881,16 @@ class NavigationViewEventApi: NavigationViewEventApiProtocol { } } - func onPolylineClicked(msg msgArg: PolylineClickedEventDto, - completion: @escaping (Result) -> Void) { + func onCircleClicked(viewId viewIdArg: Int64, circleId circleIdArg: String, + completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolylineClicked" + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCircleClicked" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([viewIdArg, circleIdArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -3779,16 +3906,93 @@ class NavigationViewEventApi: NavigationViewEventApiProtocol { } } - func onCircleClicked(msg msgArg: CircleClickedEventDto, + func onNavigationUIEnabledChanged(viewId viewIdArg: Int64, + navigationUIEnabled navigationUIEnabledArg: Bool, + completion: @escaping (Result) -> Void) { + let channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onNavigationUIEnabledChanged" + let channel = FlutterBasicMessageChannel( + name: channelName, + binaryMessenger: binaryMessenger, + codec: codec + ) + channel.sendMessage([viewIdArg, navigationUIEnabledArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + + func onMyLocationClicked(viewId viewIdArg: Int64, + completion: @escaping (Result) -> Void) { + let channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationClicked" + let channel = FlutterBasicMessageChannel( + name: channelName, + binaryMessenger: binaryMessenger, + codec: codec + ) + channel.sendMessage([viewIdArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + + func onMyLocationButtonClicked(viewId viewIdArg: Int64, + completion: @escaping (Result) -> Void) { + let channelName = + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationButtonClicked" + let channel = FlutterBasicMessageChannel( + name: channelName, + binaryMessenger: binaryMessenger, + codec: codec + ) + channel.sendMessage([viewIdArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + + func onCameraChanged(viewId viewIdArg: Int64, eventType eventTypeArg: CameraEventTypeDto, + position positionArg: CameraPositionDto, completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCircleClicked" + "dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCameraChanged" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([viewIdArg, eventTypeArg.rawValue, positionArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -3833,10 +4037,12 @@ private class NavigationSessionApiCodecReader: FlutterStandardReader { case 139: return RouteSegmentTrafficDataRoadStretchRenderingDataDto.fromList(readValue() as! [Any?]) case 140: - return RoutingOptionsDto.fromList(readValue() as! [Any?]) + return RouteTokenOptionsDto.fromList(readValue() as! [Any?]) case 141: - return SimulationOptionsDto.fromList(readValue() as! [Any?]) + return RoutingOptionsDto.fromList(readValue() as! [Any?]) case 142: + return SimulationOptionsDto.fromList(readValue() as! [Any?]) + case 143: return SpeedAlertOptionsDto.fromList(readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -3882,15 +4088,18 @@ private class NavigationSessionApiCodecWriter: FlutterStandardWriter { } else if let value = value as? RouteSegmentTrafficDataRoadStretchRenderingDataDto { super.writeByte(139) super.writeValue(value.toList()) - } else if let value = value as? RoutingOptionsDto { + } else if let value = value as? RouteTokenOptionsDto { super.writeByte(140) super.writeValue(value.toList()) - } else if let value = value as? SimulationOptionsDto { + } else if let value = value as? RoutingOptionsDto { super.writeByte(141) super.writeValue(value.toList()) - } else if let value = value as? SpeedAlertOptionsDto { + } else if let value = value as? SimulationOptionsDto { super.writeByte(142) super.writeValue(value.toList()) + } else if let value = value as? SpeedAlertOptionsDto { + super.writeByte(143) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -3915,7 +4124,8 @@ class NavigationSessionApiCodec: FlutterStandardMessageCodec { /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol NavigationSessionApi { /// General. - func createNavigationSession(completion: @escaping (Result) -> Void) + func createNavigationSession(abnormalTerminationReportingEnabled: Bool, + completion: @escaping (Result) -> Void) func isInitialized() throws -> Bool func cleanup() throws func showTermsAndConditionsDialog(title: String, companyName: String, @@ -3923,11 +4133,12 @@ protocol NavigationSessionApi { completion: @escaping (Result) -> Void) func areTermsAccepted() throws -> Bool func resetTermsAccepted() throws + func getNavSDKVersion() throws -> String /// Navigation. func isGuidanceRunning() throws -> Bool func startGuidance() throws func stopGuidance() throws - func setDestinations(msg: DestinationsDto, + func setDestinations(destinations: DestinationsDto, completion: @escaping (Result) -> Void) func clearDestinations() throws func continueToNextDestination() throws -> NavigationWaypointDto? @@ -3984,15 +4195,20 @@ enum NavigationSessionApiSetup { codec: codec ) if let api { - createNavigationSessionChannel.setMessageHandler { _, reply in - api.createNavigationSession { result in - switch result { - case .success: - reply(wrapResult(nil)) - case let .failure(error): - reply(wrapError(error)) + createNavigationSessionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let abnormalTerminationReportingEnabledArg = args[0] as! Bool + api + .createNavigationSession( + abnormalTerminationReportingEnabled: abnormalTerminationReportingEnabledArg + ) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case let .failure(error): + reply(wrapError(error)) + } } - } } } else { createNavigationSessionChannel.setMessageHandler(nil) @@ -4092,6 +4308,23 @@ enum NavigationSessionApiSetup { } else { resetTermsAcceptedChannel.setMessageHandler(nil) } + let getNavSDKVersionChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.getNavSDKVersion", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + getNavSDKVersionChannel.setMessageHandler { _, reply in + do { + let result = try api.getNavSDKVersion() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getNavSDKVersionChannel.setMessageHandler(nil) + } /// Navigation. let isGuidanceRunningChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.isGuidanceRunning", @@ -4152,8 +4385,8 @@ enum NavigationSessionApiSetup { if let api { setDestinationsChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let msgArg = args[0] as! DestinationsDto - api.setDestinations(msg: msgArg) { result in + let destinationsArg = args[0] as! DestinationsDto + api.setDestinations(destinations: destinationsArg) { result in switch result { case let .success(res): reply(wrapResult(res.rawValue)) @@ -4576,27 +4809,9 @@ private class NavigationSessionEventApiCodecReader: FlutterStandardReader { case 128: return LatLngDto.fromList(readValue() as! [Any?]) case 129: - return LatLngDto.fromList(readValue() as! [Any?]) - case 130: - return NavigationSessionEventDto.fromList(readValue() as! [Any?]) - case 131: return NavigationWaypointDto.fromList(readValue() as! [Any?]) - case 132: - return OnArrivalEventDto.fromList(readValue() as! [Any?]) - case 133: - return RemainingTimeOrDistanceChangedEventDto.fromList(readValue() as! [Any?]) - case 134: - return ReroutingEventDto.fromList(readValue() as! [Any?]) - case 135: - return RoadSnappedLocationUpdatedEventDto.fromList(readValue() as! [Any?]) - case 136: - return RoadSnappedRawLocationUpdatedEventDto.fromList(readValue() as! [Any?]) - case 137: - return RouteChangedEventDto.fromList(readValue() as! [Any?]) - case 138: + case 130: return SpeedingUpdatedEventDto.fromList(readValue() as! [Any?]) - case 139: - return TrafficUpdatedEventDto.fromList(readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -4608,38 +4823,11 @@ private class NavigationSessionEventApiCodecWriter: FlutterStandardWriter { if let value = value as? LatLngDto { super.writeByte(128) super.writeValue(value.toList()) - } else if let value = value as? LatLngDto { - super.writeByte(129) - super.writeValue(value.toList()) - } else if let value = value as? NavigationSessionEventDto { - super.writeByte(130) - super.writeValue(value.toList()) } else if let value = value as? NavigationWaypointDto { - super.writeByte(131) - super.writeValue(value.toList()) - } else if let value = value as? OnArrivalEventDto { - super.writeByte(132) - super.writeValue(value.toList()) - } else if let value = value as? RemainingTimeOrDistanceChangedEventDto { - super.writeByte(133) - super.writeValue(value.toList()) - } else if let value = value as? ReroutingEventDto { - super.writeByte(134) - super.writeValue(value.toList()) - } else if let value = value as? RoadSnappedLocationUpdatedEventDto { - super.writeByte(135) - super.writeValue(value.toList()) - } else if let value = value as? RoadSnappedRawLocationUpdatedEventDto { - super.writeByte(136) - super.writeValue(value.toList()) - } else if let value = value as? RouteChangedEventDto { - super.writeByte(137) + super.writeByte(129) super.writeValue(value.toList()) } else if let value = value as? SpeedingUpdatedEventDto { - super.writeByte(138) - super.writeValue(value.toList()) - } else if let value = value as? TrafficUpdatedEventDto { - super.writeByte(139) + super.writeByte(130) super.writeValue(value.toList()) } else { super.writeValue(value) @@ -4664,26 +4852,25 @@ class NavigationSessionEventApiCodec: FlutterStandardMessageCodec { /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol NavigationSessionEventApiProtocol { - func onNavigationSessionEvent(msg msgArg: NavigationSessionEventDto, - completion: @escaping (Result) -> Void) func onSpeedingUpdated(msg msgArg: SpeedingUpdatedEventDto, completion: @escaping (Result) -> Void) - func onRoadSnappedLocationUpdated(msg msgArg: RoadSnappedLocationUpdatedEventDto, + func onRoadSnappedLocationUpdated(location locationArg: LatLngDto, completion: @escaping (Result) -> Void) - func onRoadSnappedRawLocationUpdated(msg msgArg: RoadSnappedRawLocationUpdatedEventDto, + func onRoadSnappedRawLocationUpdated(location locationArg: LatLngDto, completion: @escaping (Result) -> Void) - func onArrival(msg msgArg: OnArrivalEventDto, + func onArrival(waypoint waypointArg: NavigationWaypointDto, completion: @escaping (Result) -> Void) - func onRouteChanged(msg msgArg: RouteChangedEventDto, - completion: @escaping (Result) -> Void) - func onRemainingTimeOrDistanceChanged(msg msgArg: RemainingTimeOrDistanceChangedEventDto, + func onRouteChanged(completion: @escaping (Result) -> Void) + func onRemainingTimeOrDistanceChanged(remainingTime remainingTimeArg: Double, + remainingDistance remainingDistanceArg: Double, completion: @escaping (Result) -> Void) - /// Android only event. - func onTrafficUpdated(msg msgArg: TrafficUpdatedEventDto, - completion: @escaping (Result) -> Void) - /// Android only event. - func onRerouting(msg msgArg: ReroutingEventDto, - completion: @escaping (Result) -> Void) + /// Android-only event. + func onTrafficUpdated(completion: @escaping (Result) -> Void) + /// Android-only event. + func onRerouting(completion: @escaping (Result) -> Void) + /// Android-only event. + func onGpsAvailabilityUpdate(available availableArg: Bool, + completion: @escaping (Result) -> Void) } class NavigationSessionEventApi: NavigationSessionEventApiProtocol { @@ -4696,10 +4883,10 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { NavigationSessionEventApiCodec.shared } - func onNavigationSessionEvent(msg msgArg: NavigationSessionEventDto, - completion: @escaping (Result) -> Void) { + func onSpeedingUpdated(msg msgArg: SpeedingUpdatedEventDto, + completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onNavigationSessionEvent" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, @@ -4721,16 +4908,16 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - func onSpeedingUpdated(msg msgArg: SpeedingUpdatedEventDto, - completion: @escaping (Result) -> Void) { + func onRoadSnappedLocationUpdated(location locationArg: LatLngDto, + completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([locationArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -4746,16 +4933,16 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - func onRoadSnappedLocationUpdated(msg msgArg: RoadSnappedLocationUpdatedEventDto, - completion: @escaping (Result) -> Void) { + func onRoadSnappedRawLocationUpdated(location locationArg: LatLngDto, + completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([locationArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -4771,16 +4958,16 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - func onRoadSnappedRawLocationUpdated(msg msgArg: RoadSnappedRawLocationUpdatedEventDto, - completion: @escaping (Result) -> Void) { + func onArrival(waypoint waypointArg: NavigationWaypointDto, + completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([waypointArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -4796,16 +4983,15 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - func onArrival(msg msgArg: OnArrivalEventDto, - completion: @escaping (Result) -> Void) { + func onRouteChanged(completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage(nil) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -4821,16 +5007,18 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - func onRouteChanged(msg msgArg: RouteChangedEventDto, - completion: @escaping (Result) -> Void) { + func onRemainingTimeOrDistanceChanged(remainingTime remainingTimeArg: Double, + remainingDistance remainingDistanceArg: Double, + completion: @escaping (Result) + -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([remainingTimeArg, remainingDistanceArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -4846,17 +5034,16 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - func onRemainingTimeOrDistanceChanged(msg msgArg: RemainingTimeOrDistanceChangedEventDto, - completion: @escaping (Result) - -> Void) { + /// Android-only event. + func onTrafficUpdated(completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage(nil) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -4872,17 +5059,16 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - /// Android only event. - func onTrafficUpdated(msg msgArg: TrafficUpdatedEventDto, - completion: @escaping (Result) -> Void) { + /// Android-only event. + func onRerouting(completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage(nil) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return @@ -4898,17 +5084,17 @@ class NavigationSessionEventApi: NavigationSessionEventApiProtocol { } } - /// Android only event. - func onRerouting(msg msgArg: ReroutingEventDto, - completion: @escaping (Result) -> Void) { + /// Android-only event. + func onGpsAvailabilityUpdate(available availableArg: Bool, + completion: @escaping (Result) -> Void) { let channelName = - "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting" + "dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onGpsAvailabilityUpdate" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec ) - channel.sendMessage([msgArg] as [Any?]) { response in + channel.sendMessage([availableArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return diff --git a/lib/google_maps_navigation.dart b/lib/google_maps_navigation.dart index 9043508..c2ec7e3 100644 --- a/lib/google_maps_navigation.dart +++ b/lib/google_maps_navigation.dart @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +export 'src/google_maps_image_registry.dart'; export 'src/google_maps_navigation.dart'; export 'src/google_maps_navigation_android.dart'; export 'src/google_maps_navigation_ios.dart'; -export 'src/method_channel/messages.g.dart'; export 'src/navigator/google_maps_navigator.dart'; export 'src/types/types.dart'; diff --git a/lib/src/google_maps_image_registry.dart b/lib/src/google_maps_image_registry.dart new file mode 100644 index 0000000..79d6e47 --- /dev/null +++ b/lib/src/google_maps_image_registry.dart @@ -0,0 +1,69 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import 'dart:typed_data'; + +import 'google_maps_navigation_platform_interface.dart'; +import 'types/types.dart'; + +/// Register bitmap image to image registry. +/// Returns [ImageDescriptor] that can be used to reference the bitmap when creating +/// [MarkerOptions]. +/// [bitmap] is the bytes of bitmap to be registered, in PNG format. +/// Set [imagePixelRatio] if bitmap is larger tha it's intended display size. +/// For example, if image width is 64 pixels and it need's to be displayed in 32 +/// logical pixel size, set [imagePixelRatio] to 2. +/// Optionally specify wanted logical pixel size with [width] or [height]. +/// If only [width] or [height] is specified the other dimension is scaled +/// according to the aspect ratio of the bitmap. +/// +/// Throws [ImageDecodingFailedException] if bitmap decoding fails. +/// {@category Image Registry} +Future registerBitmapImage( + {required ByteData bitmap, + double imagePixelRatio = 1.0, + double? width, + double? height}) { + return GoogleMapsNavigationPlatform.instance.registerBitmapImage( + bitmap: bitmap.buffer.asUint8List(), + imagePixelRatio: imagePixelRatio, + width: width, + height: height); +} + +/// Delete previously registered bitmap from image registry. +/// {@category Image Registry} +Future unregisterImage(ImageDescriptor imageDescriptor) { + return GoogleMapsNavigationPlatform.instance + .unregisterImage(imageDescriptor: imageDescriptor); +} + +/// Get all registered bitmaps from image registry. +/// {@category Image Registry} +Future> getRegisteredImages() { + return GoogleMapsNavigationPlatform.instance.getRegisteredImages(); +} + +/// Remove all registered bitmaps from image registry. +/// {@category Image Registry} +Future clearRegisteredImages() { + return GoogleMapsNavigationPlatform.instance.clearRegisteredImages(); +} + +/// [registerBitmapImage] failed to decode bitmap from byte array. +/// {@category Image Registry} +class ImageDecodingFailedException implements Exception { + /// Default constructor for [ImageDecodingFailedException]. + const ImageDecodingFailedException(); +} diff --git a/lib/src/google_maps_navigation.dart b/lib/src/google_maps_navigation.dart index 7186f99..f0db633 100644 --- a/lib/src/google_maps_navigation.dart +++ b/lib/src/google_maps_navigation.dart @@ -20,18 +20,12 @@ import 'package:flutter/material.dart'; import '../google_maps_navigation.dart'; import 'google_maps_navigation_platform_interface.dart'; -import 'types/util/marker_conversion.dart'; -/// Created callback. +/// The view creation callback. typedef OnCreatedCallback = void Function( GoogleNavigationViewController controller, ); -/// Navigation session events callback. -typedef OnNavigationSessionEventCallback = void Function( - NavigationSessionEvent onNavigationSessionEvent, -); - /// Called during speeding event. typedef OnSpeedingUpdatedEventCallback = void Function( SpeedingUpdatedEvent onSpeedingUpdatedEvent, @@ -48,12 +42,21 @@ typedef OnRoadSnappedLocationUpdatedEventCallback = void Function( RoadSnappedLocationUpdatedEvent onRoadSnappedLocationUpdatedEvent, ); +/// Called during road snapped raw location event (Android only). +typedef OnRoadSnappedRawLocationUpdatedEventCallback = void Function( + RoadSnappedRawLocationUpdatedEvent onRoadSnappedRawLocationUpdatedEvent, +); + /// Called during arriving to destination event. typedef OnArrivalEventCallback = void Function(OnArrivalEvent onArrivalEvent); /// Called during rerouting event. (Android only) typedef OnReroutingEventCallback = void Function(); +/// Called during GPS availability event. (Android only). +typedef OnGpsAvailabilityEventCallback = void Function( + GpsAvailabilityUpdatedEvent gpsAvailabilityUpdatedEvent); + /// Called during traffic updated event. (Android only) typedef OnTrafficUpdatedEventCallback = void Function(); @@ -61,7 +64,7 @@ typedef OnTrafficUpdatedEventCallback = void Function(); typedef OnRouteChangedEventCallback = void Function(); /// Called during recenter button click event. -typedef OnRecenterButtonClickedEventCallback = void Function( +typedef OnRecenterButtonClicked = void Function( NavigationViewRecenterButtonClickedEvent); /// Called during remaining time or distance changed event. @@ -104,6 +107,59 @@ typedef OnPolylineClicked = void Function(String polylineId); /// Called during circle clicked event. typedef OnCircleClicked = void Function(String circleId); +/// Called when the [GoogleNavigationViewController.isNavigationUIEnabled] status changes. +typedef OnNavigationUIEnabledChanged = void Function(bool navigationUIEnabled); + +/// Called during my location clicked event. +typedef OnMyLocationClicked = void Function(MyLocationClickedEvent); + +/// Called during my location button clicked event. +typedef OnMyLocationButtonClicked = void Function(MyLocationButtonClickedEvent); + +/// Called when the camera starts moving after it has been idle or when the +/// reason for the camera motion has changed. +/// +/// [gesture] is true when the camera motion has been initiated in response +/// to the user gestures on the map. For example: pan, tilt, pinch to zoom, or +/// rotate. +/// [gesture] is false when the camera motion has been initiated in response +/// to user actions. For example: zoom buttons, my location button, or marker +/// clicks. +/// [position] is the camera position where the motion started. +typedef OnCameraMoveStarted = void Function( + CameraPosition position, bool gesture); + +/// Called repeatedly as the camera continues to move after the +/// [OnCameraMoveStarted] call. The method may be called as often as once every +/// frame. +/// +/// [position] is the current camera position. +typedef OnCameraMove = void Function(CameraPosition position); + +/// Called when the camera movement has ended, there are no pending animations +/// and the user has stopped interacting with the map. +/// +/// [position] is the camera position where the motion ended. +typedef OnCameraIdle = void Function(CameraPosition position); + +/// Called when the camera starts following current location, typically will +/// get called in response to [GoogleNavigationViewController.followMyLocation]. +/// Only applicable on Android. +/// +/// [position] is the current camera position. +typedef OnCameraStartedFollowingLocation = void Function( + CameraPosition position); + +/// Called when the camera stops following current location. A camera already +/// following location will exit the follow mode if the camera is moved via +/// user gesture or an API call, e.g. [GoogleNavigationViewController.moveCamera] +/// or [GoogleNavigationViewController.animateCamera]. +/// Only applicable on Android. +/// +/// [position] is the current camera position. +typedef OnCameraStoppedFollowingLocation = void Function( + CameraPosition position); + /// The main map view widget for Google Maps Navigation. /// {@category Navigation View} class GoogleMapsNavigationView extends StatefulWidget { @@ -124,39 +180,47 @@ class GoogleMapsNavigationView extends StatefulWidget { /// // Other initial map settings... /// ) /// ``` - const GoogleMapsNavigationView({ - super.key, - required this.onViewCreated, - this.initialCameraPosition = const CameraPosition(), - this.initialMapType = MapType.normal, - this.initialCompassEnabled = true, - this.initialRotateGesturesEnabled = true, - this.initialScrollGesturesEnabled = true, - this.initialTiltGesturesEnabled = true, - this.initialZoomGesturesEnabled = true, - this.initialScrollGesturesEnabledDuringRotateOrZoom = true, - this.initialMapToolbarEnabled = true, - this.initialMinZoomPreference, - this.initialMaxZoomPreference, - this.initialZoomControlsEnabled = true, - this.initialCameraTargetBounds, - this.initialNavigationUiEnabled = false, - this.layoutDirection, - this.gestureRecognizers = const >{}, - this.onRecenterButtonClicked, - this.onMarkerClicked, - this.onMarkerDrag, - this.onMarkerDragStart, - this.onMarkerDragEnd, - this.onMarkerInfoWindowClicked, - this.onMarkerInfoWindowClosed, - this.onMarkerInfoWindowLongClicked, - this.onMapClicked, - this.onMapLongClicked, - this.onPolygonClicked, - this.onPolylineClicked, - this.onCircleClicked, - }); + const GoogleMapsNavigationView( + {super.key, + required this.onViewCreated, + this.initialCameraPosition = const CameraPosition(), + this.initialMapType = MapType.normal, + this.initialCompassEnabled = true, + this.initialRotateGesturesEnabled = true, + this.initialScrollGesturesEnabled = true, + this.initialTiltGesturesEnabled = true, + this.initialZoomGesturesEnabled = true, + this.initialScrollGesturesEnabledDuringRotateOrZoom = true, + this.initialMapToolbarEnabled = true, + this.initialMinZoomPreference, + this.initialMaxZoomPreference, + this.initialZoomControlsEnabled = true, + this.initialCameraTargetBounds, + this.initialNavigationUIEnabledPreference = + NavigationUIEnabledPreference.automatic, + this.layoutDirection, + this.gestureRecognizers = const >{}, + this.onRecenterButtonClicked, + this.onMarkerClicked, + this.onMarkerDrag, + this.onMarkerDragStart, + this.onMarkerDragEnd, + this.onMarkerInfoWindowClicked, + this.onMarkerInfoWindowClosed, + this.onMarkerInfoWindowLongClicked, + this.onMapClicked, + this.onMapLongClicked, + this.onPolygonClicked, + this.onPolylineClicked, + this.onCircleClicked, + this.onNavigationUIEnabledChanged, + this.onMyLocationClicked, + this.onMyLocationButtonClicked, + this.onCameraMoveStarted, + this.onCameraMove, + this.onCameraIdle, + this.onCameraStartedFollowingLocation, + this.onCameraStoppedFollowingLocation}); /// On view created callback. final OnCreatedCallback onViewCreated; @@ -204,7 +268,7 @@ class GoogleMapsNavigationView extends StatefulWidget { /// By default, the zoom gestures enabled. final bool initialZoomGesturesEnabled; - /// /// Specifies whether scroll gestures during rotate or zoom should be enabled. + /// Specifies whether scroll gestures during rotate or zoom should be enabled. /// /// If enabled, users can swipe to pan the camera. If disabled, swiping has no effect. /// This setting doesn't restrict programmatic movement and animation of the camera. @@ -212,12 +276,12 @@ class GoogleMapsNavigationView extends StatefulWidget { /// By default, the zoom gestures enabled. final bool initialScrollGesturesEnabledDuringRotateOrZoom; - /// Specifies whether the mapToolbar should be enabled. Only applicable on Android. + /// Specifies whether the map toolbar should be enabled. Only applicable on Android. /// - /// If enabled, and the Map Toolbar can be shown in the current context, + /// If enabled, and the map toolbar can be shown in the current context, /// users will see a bar with various context-dependent actions. /// - /// By default, the Map Toolbar is enabled. + /// By default, the map toolbar is enabled. final bool initialMapToolbarEnabled; /// Specifies a preferred lower bound for camera zoom. @@ -243,8 +307,18 @@ class GoogleMapsNavigationView extends StatefulWidget { /// Determines the initial visibility of the navigation UI on map initialization. /// - /// False by default. - final bool initialNavigationUiEnabled; + /// By default set to [NavigationUIEnabledPreference.automatic], + /// meaning the navigation UI gets enabled if the navigation + /// session has already been successfully started. + /// + /// If set to [NavigationUIEnabledPreference.disabled] the navigation view + /// initially displays a classic map view. + /// + /// Note on Android enabling the navigation UI for the view requires that the + /// navigation session has already been successfully started with + /// [GoogleMapsNavigator.initializeNavigationSession]. On iOS accepting + /// the terms and conditions is enough. + final NavigationUIEnabledPreference initialNavigationUIEnabledPreference; /// Which gestures should be forwarded to the PlatformView. /// @@ -255,7 +329,7 @@ class GoogleMapsNavigationView extends StatefulWidget { final Set> gestureRecognizers; /// On recenter button clicked event callback. - final OnRecenterButtonClickedEventCallback? onRecenterButtonClicked; + final OnRecenterButtonClicked? onRecenterButtonClicked; /// On marker clicked callback. final OnMarkerClicked? onMarkerClicked; @@ -293,12 +367,37 @@ class GoogleMapsNavigationView extends StatefulWidget { /// On circle clicked callback. final OnCircleClicked? onCircleClicked; + /// On navigation UI enabled changed callback. + final OnNavigationUIEnabledChanged? onNavigationUIEnabledChanged; + + /// On my location clicked callback. + final OnMyLocationClicked? onMyLocationClicked; + + /// On my location button clicked callback. + final OnMyLocationButtonClicked? onMyLocationButtonClicked; + + /// On camera move started callback. + final OnCameraMoveStarted? onCameraMoveStarted; + + /// On camera move callback. + final OnCameraMove? onCameraMove; + + /// On camera idle callback. + final OnCameraIdle? onCameraIdle; + + /// On camera started following location callback (Android-only). + final OnCameraStartedFollowingLocation? onCameraStartedFollowingLocation; + + /// On camera stopped following location callback (Android-only). + final OnCameraStoppedFollowingLocation? onCameraStoppedFollowingLocation; + /// Creates a [State] for this [GoogleMapsNavigationView]. @override State createState() => GoogleMapsNavigationViewState(); } /// Google Maps Navigation. +/// {@category Navigation View} class GoogleMapsNavigationViewState extends State { @override Widget build(BuildContext context) { @@ -325,7 +424,8 @@ class GoogleMapsNavigationViewState extends State { cameraTargetBounds: widget.initialCameraTargetBounds, ), navigationViewOptions: NavigationViewOptions( - navigationUIEnabled: widget.initialNavigationUiEnabled)), + navigationUIEnabledPreference: + widget.initialNavigationUIEnabledPreference)), onMapReady: _onPlatformViewCreated); } @@ -340,7 +440,7 @@ class GoogleMapsNavigationViewState extends State { /// Settings for the user interface of the map. /// {@category Navigation View} class NavigationViewUISettings { - /// NavigationViewUISettings constructor. + /// [NavigationViewUISettings] constructor. NavigationViewUISettings(this._viewId); final int _viewId; @@ -349,17 +449,31 @@ class NavigationViewUISettings { /// /// By default, the my location button is visible /// when the my location indicator is shown. - Future enableMyLocationButton({required bool enabled}) { + Future setMyLocationButtonEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableMyLocationButton(viewId: _viewId, enabled: enabled); + .setMyLocationButtonEnabled(viewId: _viewId, enabled: enabled); + } + + /// Sets whether to consume my location button click events. + /// + /// If [enabled] is set to true, the default behaviour does not occur. + /// If [enabled] is set to false, the default behaviour occurs. The default + /// behavior is for the camera move such that it is centered on the user location. + /// + /// Note: By default, the button click events are not consumed, and the map + /// follows its native default behavior. This method can be used to override this behavior. + Future setConsumeMyLocationButtonClickEventsEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance + .setConsumeMyLocationButtonClickEventsEnabled( + viewId: _viewId, enabled: enabled); } /// Sets the preference for whether the user is allowed to zoom the map using a gesture. /// /// Initial value can be set with [GoogleMapsNavigationView.initialZoomGesturesEnabled]. - Future enableZoomGestures({required bool enabled}) { + Future setZoomGesturesEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableZoomGestures(viewId: _viewId, enabled: enabled); + .setZoomGesturesEnabled(viewId: _viewId, enabled: enabled); } /// Enables or disables the zoom controls. @@ -367,9 +481,9 @@ class NavigationViewUISettings { /// Initial value can be set with [GoogleMapsNavigationView.initialZoomControlsEnabled]. /// /// The zoom controls are only available on Android. Throws [UnsupportedError] on iOS. - Future enableZoomControls({required bool enabled}) { + Future setZoomControlsEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableZoomControls(viewId: _viewId, enabled: enabled); + .setZoomControlsEnabled(viewId: _viewId, enabled: enabled); } /// Enables or disables the compass. @@ -378,51 +492,51 @@ class NavigationViewUISettings { /// from the default position, where the north points up. /// /// Initial value can be set with [GoogleMapsNavigationView.initialCompassEnabled]. - Future enableCompass({required bool enabled}) { + Future setCompassEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableCompass(viewId: _viewId, enabled: enabled); + .setCompassEnabled(viewId: _viewId, enabled: enabled); } /// Sets the preference for whether the user is allowed to rotate the map using a gesture. /// /// Initial value can be set with [GoogleMapsNavigationView.initialRotateGesturesEnabled]. - Future enableRotateGestures({required bool enabled}) { + Future setRotateGesturesEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableRotateGestures(viewId: _viewId, enabled: enabled); + .setRotateGesturesEnabled(viewId: _viewId, enabled: enabled); } /// Sets the preference for whether the user is allowed to scroll the map using a gesture. /// /// Initial value can be set with [GoogleMapsNavigationView.initialScrollGesturesEnabled]. - Future enableScrollGestures({required bool enabled}) { + Future setScrollGesturesEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableScrollGestures(viewId: _viewId, enabled: enabled); + .setScrollGesturesEnabled(viewId: _viewId, enabled: enabled); } /// Sets the preference for whether the user is allowed to scroll the map /// at the same time when zooming or rotating the map with a gesture. /// /// Initial value can be set with [GoogleMapsNavigationView.initialScrollGesturesEnabledDuringRotateOrZoom]. - Future enableScrollGesturesDuringRotateOrZoom({required bool enabled}) { + Future setScrollGesturesDuringRotateOrZoomEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableScrollGesturesDuringRotateOrZoom( + .setScrollGesturesDuringRotateOrZoomEnabled( viewId: _viewId, enabled: enabled); } /// Sets the preference for whether the user is allowed to tilt the map with a gesture. /// /// Initial value can be set with [GoogleMapsNavigationView.initialTiltGesturesEnabled]. - Future enableTiltGestures({required bool enabled}) { + Future setTiltGesturesEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableTiltGestures(viewId: _viewId, enabled: enabled); + .setTiltGesturesEnabled(viewId: _viewId, enabled: enabled); } /// Turns the traffic layer on or off. /// /// By default, the traffic layer is off. - Future enableTraffic({required bool enabled}) { + Future setTrafficEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableTraffic(viewId: _viewId, enabled: enabled); + .setTrafficEnabled(viewId: _viewId, enabled: enabled); } /// Sets the preference for whether the map toolbar should be enabled or disabled. @@ -430,24 +544,30 @@ class NavigationViewUISettings { /// Initial value can be set with [GoogleMapsNavigationView.initialMapToolbarEnabled]. /// /// The map toolbar is only available on Android. Throws [UnsupportedError] on iOS. - Future enableMapToolbar({required bool enabled}) { + Future setMapToolbarEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableMapToolbar(viewId: _viewId, enabled: enabled); + .setMapToolbarEnabled(viewId: _viewId, enabled: enabled); } - /// Gets whether the my location button is enabled or disabled. + /// Checks if the my location button is enabled. Future isMyLocationButtonEnabled() async { return GoogleMapsNavigationPlatform.instance .isMyLocationButtonEnabled(viewId: _viewId); } - /// Gets whether zoom gestures are enabled/disabled. + /// Checks if the my location button consumes click events. + Future isConsumeMyLocationButtonClickEventsEnabled() async { + return GoogleMapsNavigationPlatform.instance + .isConsumeMyLocationButtonClickEventsEnabled(viewId: _viewId); + } + + /// Checks if the zoom gestures are enabled. Future isZoomGesturesEnabled() async { return GoogleMapsNavigationPlatform.instance .isZoomGesturesEnabled(viewId: _viewId); } - /// Gets whether zoom controls are enabled/disabled. + /// Checks if the zoom controls are enabled. /// /// The zoom controls are only available on Android. Throws [UnsupportedError] on iOS. Future isZoomControlsEnabled() async { @@ -455,37 +575,38 @@ class NavigationViewUISettings { .isZoomControlsEnabled(viewId: _viewId); } - /// Gets whether the compass is enabled/disabled. + /// Checks if the compass is enabled. Future isCompassEnabled() async { return GoogleMapsNavigationPlatform.instance .isCompassEnabled(viewId: _viewId); } - /// Gets whether rotate gestures are enabled/disabled. + /// Checks if the rotate gestures are enabled. Future isRotateGesturesEnabled() async { return GoogleMapsNavigationPlatform.instance .isRotateGesturesEnabled(viewId: _viewId); } - /// Gets whether scroll gestures are enabled/disabled. + /// Checks if the scroll gestures are enabled. Future isScrollGesturesEnabled() async { return GoogleMapsNavigationPlatform.instance .isScrollGesturesEnabled(viewId: _viewId); } - /// Gets whether scroll gestures are enabled/disabled during rotation and zoom gestures. + /// Checks if the scroll gestures are enabled during the rotation and + /// zoom gestures. Future isScrollGesturesEnabledDuringRotateOrZoom() async { return GoogleMapsNavigationPlatform.instance .isScrollGesturesEnabledDuringRotateOrZoom(viewId: _viewId); } - /// Gets whether tilt gestures are enabled/disabled. + /// Checks if the scroll gestures are enabled. Future isTiltGesturesEnabled() async { return GoogleMapsNavigationPlatform.instance .isTiltGesturesEnabled(viewId: _viewId); } - /// Gets whether the Map Toolbar is enabled/disabled. + /// Checks if the map toolbar is enabled. /// /// The map toolbar is only available on Android. Throws [UnsupportedError] on iOS. Future isMapToolbarEnabled() async { @@ -493,7 +614,7 @@ class NavigationViewUISettings { .isMapToolbarEnabled(viewId: _viewId); } - /// Checks whether the map is drawing traffic data. + /// Checks if the map is displaying traffic data. Future isTrafficEnabled() async { return GoogleMapsNavigationPlatform.instance .isTrafficEnabled(viewId: _viewId); @@ -506,7 +627,7 @@ class GoogleNavigationViewController { /// Basic constructor. /// /// Don't create this directly, but access through - /// GoogleMapsNavigation.onViewCreated() callback. + /// [GoogleMapsNavigationView.onViewCreated] callback. GoogleNavigationViewController(this._viewId, [this._viewState]) : settings = NavigationViewUISettings(_viewId) { _initListeners(); @@ -519,24 +640,28 @@ class GoogleNavigationViewController { /// Settings for the user interface of the map. final NavigationViewUISettings settings; - /// Getter for viewId. + /// Getter for view ID. int getViewId() { return _viewId; } /// Initializes the event channel listeners for the navigation view instance. void _initListeners() { - setOnMapClickedListeners(); - setOnRecenterButtonClickedListener(); - setOnMarkerClickedListeners(); - setOnMarkerDragListeners(); - setOnPolygonClickedListener(); - setOnPolylineClickedListener(); - setOnCircleClickedListener(); + _setOnMapClickedListeners(); + _setOnRecenterButtonClickedListener(); + _setOnMarkerClickedListeners(); + _setOnMarkerDragListeners(); + _setOnPolygonClickedListener(); + _setOnPolylineClickedListener(); + _setOnCircleClickedListener(); + _setOnNavigationUIEnabledChangedListener(); + _setOnMyLocationClickedListener(); + _setOnMyLocationButtonClickedListener(); + _setOnCameraChangedListener(); } /// Sets the event channel listener for the map click event listeners. - void setOnMapClickedListeners() { + void _setOnMapClickedListeners() { if (_viewState != null) { if (_viewState?.widget.onMapClicked != null) { GoogleMapsNavigationPlatform.instance @@ -556,7 +681,7 @@ class GoogleNavigationViewController { } /// Sets the event channel listener for the on recenter button clicked event. - void setOnRecenterButtonClickedListener() { + void _setOnRecenterButtonClickedListener() { if (_viewState != null && _viewState?.widget.onRecenterButtonClicked != null) { GoogleMapsNavigationPlatform.instance @@ -566,70 +691,129 @@ class GoogleNavigationViewController { } /// Sets the event channel listener for the marker clicked events. - void setOnMarkerClickedListeners() { + void _setOnMarkerClickedListeners() { GoogleMapsNavigationPlatform.instance .getMarkerEventStream(viewId: _viewId) - .listen((MarkerEventDto event) { + .listen((MarkerEvent event) { switch (event.eventType) { - case MarkerEventTypeDto.clicked: + case MarkerEventType.clicked: _viewState?.widget.onMarkerClicked?.call(event.markerId); - case MarkerEventTypeDto.infoWindowClicked: + case MarkerEventType.infoWindowClicked: _viewState?.widget.onMarkerInfoWindowClicked?.call(event.markerId); - case MarkerEventTypeDto.infoWindowClosed: + case MarkerEventType.infoWindowClosed: _viewState?.widget.onMarkerInfoWindowClosed?.call(event.markerId); - case MarkerEventTypeDto.infoWindowLongClicked: + case MarkerEventType.infoWindowLongClicked: _viewState?.widget.onMarkerInfoWindowLongClicked ?.call(event.markerId); } }); } + /// Sets the event channel listener for the on my location clicked event. + void _setOnMyLocationClickedListener() { + if (_viewState != null && _viewState?.widget.onMyLocationClicked != null) { + GoogleMapsNavigationPlatform.instance + .getMyLocationClickedEventStream(viewId: _viewId) + .listen(_viewState?.widget.onMyLocationClicked); + } + } + + /// Sets the event channel listener for the on my location button clicked event. + void _setOnMyLocationButtonClickedListener() { + if (_viewState != null && + _viewState?.widget.onMyLocationButtonClicked != null) { + GoogleMapsNavigationPlatform.instance + .getMyLocationButtonClickedEventStream(viewId: _viewId) + .listen(_viewState?.widget.onMyLocationButtonClicked); + } + } + + /// Sets the event channel listener for camera changed events. + void _setOnCameraChangedListener() { + // Register listeners if any of the callbacks are not null. + if (_viewState?.widget.onCameraMoveStarted != null || + _viewState?.widget.onCameraMove != null || + _viewState?.widget.onCameraIdle != null) { + GoogleMapsNavigationPlatform.instance + .registerOnCameraChangedListener(viewId: _viewId); + } + GoogleMapsNavigationPlatform.instance + .getCameraChangedEventStream(viewId: _viewId) + .listen((CameraChangedEvent event) { + switch (event.eventType) { + case CameraEventType.moveStartedByApi: + _viewState?.widget.onCameraMoveStarted?.call(event.position, false); + case CameraEventType.moveStartedByGesture: + _viewState?.widget.onCameraMoveStarted?.call(event.position, true); + case CameraEventType.onCameraMove: + _viewState?.widget.onCameraMove?.call(event.position); + case CameraEventType.onCameraIdle: + _viewState?.widget.onCameraIdle?.call(event.position); + case CameraEventType.onCameraStartedFollowingLocation: + _viewState?.widget.onCameraStartedFollowingLocation + ?.call(event.position); + case CameraEventType.onCameraStoppedFollowingLocation: + _viewState?.widget.onCameraStoppedFollowingLocation + ?.call(event.position); + } + }); + } + /// Sets the event channel listener for the marker drag event. - void setOnMarkerDragListeners() { + void _setOnMarkerDragListeners() { GoogleMapsNavigationPlatform.instance .getMarkerDragEventStream(viewId: _viewId) - .listen((MarkerDragEventDto event) { + .listen((MarkerDragEvent event) { switch (event.eventType) { - case MarkerDragEventTypeDto.drag: - _viewState?.widget.onMarkerDrag - ?.call(event.markerId, event.position.toLatLng()); - case MarkerDragEventTypeDto.dragEnd: + case MarkerDragEventType.drag: + _viewState?.widget.onMarkerDrag?.call(event.markerId, event.position); + case MarkerDragEventType.dragEnd: _viewState?.widget.onMarkerDragEnd - ?.call(event.markerId, event.position.toLatLng()); - case MarkerDragEventTypeDto.dragStart: + ?.call(event.markerId, event.position); + case MarkerDragEventType.dragStart: _viewState?.widget.onMarkerDragStart - ?.call(event.markerId, event.position.toLatLng()); + ?.call(event.markerId, event.position); } }); } /// Sets the event channel listener for the polygon clicked event. - void setOnPolygonClickedListener() { + void _setOnPolygonClickedListener() { GoogleMapsNavigationPlatform.instance .getPolygonClickedEventStream(viewId: _viewId) - .listen((PolygonClickedEventDto event) { + .listen((PolygonClickedEvent event) { _viewState?.widget.onPolygonClicked?.call(event.polygonId); }); } /// Sets the event channel listener for the polyline clicked event. - void setOnPolylineClickedListener() { + void _setOnPolylineClickedListener() { GoogleMapsNavigationPlatform.instance - .getPolylineDtoClickedEventStream(viewId: _viewId) - .listen((PolylineClickedEventDto event) { + .getPolylineClickedEventStream(viewId: _viewId) + .listen((PolylineClickedEvent event) { _viewState?.widget.onPolylineClicked?.call(event.polylineId); }); } /// Sets the event channel listener for the circle clicked event. - void setOnCircleClickedListener() { + void _setOnCircleClickedListener() { GoogleMapsNavigationPlatform.instance - .getCircleDtoClickedEventStream(viewId: _viewId) - .listen((CircleClickedEventDto event) { + .getCircleClickedEventStream(viewId: _viewId) + .listen((CircleClickedEvent event) { _viewState?.widget.onCircleClicked?.call(event.circleId); }); } + /// Sets the event channel listener for the navigation UI enabled changed event. + void _setOnNavigationUIEnabledChangedListener() { + GoogleMapsNavigationPlatform.instance + .getNavigationUIEnabledChangedEventStream(viewId: _viewId) + .listen((NavigationUIEnabledChangedEvent event) { + _viewState?.widget.onNavigationUIEnabledChanged + ?.call(event.navigationUIEnabled); + }); + } + /// Change status of my location enabled. /// /// By default, the my location layer is disabled, but gets @@ -637,17 +821,27 @@ class GoogleNavigationViewController { /// /// On iOS this property doesn't control the my location indication during /// the navigation. - Future enableMyLocation({required bool enabled}) { + Future setMyLocationEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableMyLocation(viewId: _viewId, enabled: enabled); + .setMyLocationEnabled(viewId: _viewId, enabled: enabled); } - /// Get the map type. + /// This method returns the current map type of the Google Maps view instance. Future getMapType() { return GoogleMapsNavigationPlatform.instance.getMapType(viewId: _viewId); } - /// Change the map type. + /// Changes the type of the map being displayed on the Google Maps view. + /// + /// The [mapType] parameter specifies the new map type to be set. + /// It should be one of the values defined in the [MapType] enum, + /// such as [MapType.normal], [MapType.satellite], [MapType.terrain], + /// or [MapType.hybrid]. + /// + /// Example usage: + /// ```dart + /// _navigationViewController.changeMapType(MapType.satellite); + /// ``` Future setMapType({required MapType mapType}) async { return GoogleMapsNavigationPlatform.instance .setMapType(viewId: _viewId, mapType: mapType); @@ -682,8 +876,16 @@ class GoogleNavigationViewController { /// Use [perspective] to specify the orientation of the camera /// and optional [zoomLevel] to control the map zoom. /// - /// Automatically started in perspective [CameraPerspective.tilted] when - /// the navigation guidance begins. + /// Automatically started in the perspective [CameraPerspective.tilted] when + /// the navigation is initialized with [GoogleMapsNavigator.initializeNavigationSession] + /// or when navigation UI gets re-enabled with [setNavigationUIEnabled]. + /// + /// In Android, you can use [GoogleMapsNavigationView.onCameraStartedFollowingLocation] + /// and [GoogleMapsNavigationView.onCameraStoppedFollowingLocation] callbacks + /// to detect when the follow location mode is enabled or disabled. + /// + /// Note there are small differences on how Android and iOS handle the camera + /// during the follow my location mode (tilt, zoom, transitions, etc.). /// /// See also [GoogleMapsNavigator.startGuidance], [showRouteOverview] and [animateCamera]. Future followMyLocation(CameraPerspective perspective, @@ -692,12 +894,12 @@ class GoogleNavigationViewController { viewId: _viewId, perspective: perspective, zoomLevel: zoomLevel); } - /// Gets users current location. + /// Gets user's current location. Future getMyLocation() async { return GoogleMapsNavigationPlatform.instance.getMyLocation(viewId: _viewId); } - /// Gets current visible map region / camera bounds. + /// Gets the current visible map region or camera bounds. Future getVisibleRegion() async { return GoogleMapsNavigationPlatform.instance .getVisibleRegion(viewId: _viewId); @@ -741,7 +943,7 @@ class GoogleNavigationViewController { /// Moves the camera from the current position to the position /// defined in the [cameraUpdate]. /// - /// See CameraUpdate for more information + /// See [CameraUpdate] for more information /// on how to create different camera movements. Future moveCamera(CameraUpdate cameraUpdate) { return GoogleMapsNavigationPlatform.instance @@ -754,12 +956,12 @@ class GoogleNavigationViewController { .isNavigationTripProgressBarEnabled(viewId: _viewId); } - /// Enable the navigation trip progress bar. + /// Enable or disable the navigation trip progress bar. /// /// By default, the navigation trip progress bar is disabled. - Future enableNavigationTripProgressBar({required bool enabled}) { + Future setNavigationTripProgressBarEnabled(bool enabled) { return GoogleMapsNavigationPlatform.instance - .enableNavigationTripProgressBar( + .setNavigationTripProgressBarEnabled( viewId: _viewId, enabled: enabled, ); @@ -771,11 +973,11 @@ class GoogleNavigationViewController { .isNavigationHeaderEnabled(viewId: _viewId); } - /// Enable the navigation header. + /// Enable or disable the navigation header. /// /// By default, the navigation header is enabled. - Future enableNavigationHeader({required bool enabled}) { - return GoogleMapsNavigationPlatform.instance.enableNavigationHeader( + Future setNavigationHeaderEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.setNavigationHeaderEnabled( viewId: _viewId, enabled: enabled, ); @@ -787,14 +989,14 @@ class GoogleNavigationViewController { .isNavigationFooterEnabled(viewId: _viewId); } - /// Enable the navigation footer. + /// Enable or disable the navigation footer. /// /// By default, the navigation footer is enabled. /// /// Also known as ETA card, for example in Android /// calls [setEtaCardEnabled().](https://developers.google.com/maps/documentation/navigation/android-sdk/v1/reference/com/google/android/libraries/navigation/NavigationView#setEtaCardEnabled(boolean)) - Future enableNavigationFooter({required bool enabled}) { - return GoogleMapsNavigationPlatform.instance.enableNavigationFooter( + Future setNavigationFooterEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.setNavigationFooterEnabled( viewId: _viewId, enabled: enabled, ); @@ -806,11 +1008,11 @@ class GoogleNavigationViewController { .isRecenterButtonEnabled(viewId: _viewId); } - /// Enable the recenter button. + /// Enable or disable the recenter button. /// /// By default, the recenter button is enabled. - Future enableRecenterButton({required bool enabled}) { - return GoogleMapsNavigationPlatform.instance.enableRecenterButton( + Future setRecenterButtonEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.setRecenterButtonEnabled( viewId: _viewId, enabled: enabled, ); @@ -825,10 +1027,10 @@ class GoogleNavigationViewController { /// Allow showing the speed limit indicator. /// /// By default, the speed limit is not displayed. - Future enableSpeedLimitIcon({required bool enable}) { - return GoogleMapsNavigationPlatform.instance.enableSpeedLimitIcon( + Future setSpeedLimitIconEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.setSpeedLimitIconEnabled( viewId: _viewId, - enable: enable, + enabled: enabled, ); } @@ -841,26 +1043,26 @@ class GoogleNavigationViewController { /// Allow showing the speedometer. /// /// By default, the speedometer is not displayed. - Future enableSpeedometer({required bool enable}) { - return GoogleMapsNavigationPlatform.instance.enableSpeedometer( + Future setSpeedometerEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.setSpeedometerEnabled( viewId: _viewId, - enable: enable, + enabled: enabled, ); } /// Are the incident cards displayed. - Future isIncidentCardsEnabled() { + Future isTrafficIncidentCardsEnabled() { return GoogleMapsNavigationPlatform.instance - .isIncidentCardsEnabled(viewId: _viewId); + .isTrafficIncidentCardsEnabled(viewId: _viewId); } - /// Enable showing of the incident cards. + /// Enable or disable showing of the incident cards. /// /// By default, the incident cards are shown. - Future enableIncidentCards({required bool enable}) { - return GoogleMapsNavigationPlatform.instance.enableIncidentCards( + Future setTrafficIncidentCardsEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.setTrafficIncidentCardsEnabled( viewId: _viewId, - enable: enable, + enabled: enabled, ); } @@ -877,9 +1079,12 @@ class GoogleNavigationViewController { /// Disabling hides routes on iOS, but on Android the routes stay visible. /// /// By default, the navigation UI is enabled when the session has been - /// initialized with GoogleMapsNavigotor.initializeNavigationSession(). - Future enableNavigationUI({required bool enabled}) { - return GoogleMapsNavigationPlatform.instance.enableNavigationUI( + /// initialized with [GoogleMapsNavigator.initializeNavigationSession]. + /// + /// Fails on Android if the navigation session has not been initialized, + /// and on iOS if the terms and conditions have not been accepted. + Future setNavigationUIEnabled(bool enabled) { + return GoogleMapsNavigationPlatform.instance.setNavigationUIEnabled( viewId: _viewId, enabled: enabled, ); @@ -894,132 +1099,202 @@ class GoogleNavigationViewController { ); } - /// Get all markers from map view. + /// Returns the minimum zoom level preference from the map view. + /// If minimum zoom preference is not set previously, returns minimum possible + /// zoom level for the current map type. + Future getMinZoomPreference() { + return GoogleMapsNavigationPlatform.instance + .getMinZoomPreference(viewId: _viewId); + } + + /// Returns the maximum zoom level preference from the map view. + /// If maximum zoom preference is not set previously, returns maximum possible + /// zoom level for the current map type. + Future getMaxZoomPreference() { + return GoogleMapsNavigationPlatform.instance + .getMaxZoomPreference(viewId: _viewId); + } + + /// Removes any previously specified upper and lower zoom bounds. + Future resetMinMaxZoomPreference() { + return GoogleMapsNavigationPlatform.instance + .resetMinMaxZoomPreference(viewId: _viewId); + } + + /// Sets a preferred lower bound for the camera zoom. + /// + /// When the minimum zoom changes, the SDK adjusts all later camera updates + /// to respect that minimum if possible. Note that there are technical + /// considerations that may prevent the SDK from allowing users to zoom too low. + /// + /// Throws [MinZoomRangeException] if [minZoomPreference] is + /// greater than maximum zoom lavel. + Future setMinZoomPreference(double minZoomPreference) { + return GoogleMapsNavigationPlatform.instance.setMinZoomPreference( + viewId: _viewId, minZoomPreference: minZoomPreference); + } + + /// Sets a preferred upper bound for the camera zoom. + /// + /// When the maximum zoom changes, the SDK adjusts all later camera updates + /// to respect that maximum if possible. Note that there are technical + /// considerations that may prevent the SDK from allowing users to zoom too + /// deep into the map. For example, satellite or terrain may have a lower + /// maximum zoom than the base map tiles. + /// + /// Throws [MaxZoomRangeException] if [maxZoomPreference] is + /// less than minimum zoom lavel. + Future setMaxZoomPreference(double maxZoomPreference) { + return GoogleMapsNavigationPlatform.instance.setMaxZoomPreference( + viewId: _viewId, maxZoomPreference: maxZoomPreference); + } + + /// Retrieves all markers that have been added to the map view. Future> getMarkers() { return GoogleMapsNavigationPlatform.instance.getMarkers(viewId: _viewId); } - /// Add markers to the map. + /// Add markers to the map view. Future> addMarkers(List markerOptions) { return GoogleMapsNavigationPlatform.instance .addMarkers(viewId: _viewId, markerOptions: markerOptions); } - /// Update markers to the map. + /// Update markers to the map view. /// - /// If the [markers] cannot be not be found throws [MarkerNotFoundException]. + /// Throws [MarkerNotFoundException] if the [markers] list contains one or + /// more markers that have not been added to the map view via [addMarkers] or + /// contains markers that have already been removed from the map view. Future> updateMarkers(List markers) async { return GoogleMapsNavigationPlatform.instance .updateMarkers(viewId: _viewId, markers: markers); } - /// Remove markers from the map. + /// Remove markers from the map view. /// - /// If the [markers] cannot be not be found throws [MarkerNotFoundException]. + /// Throws [MarkerNotFoundException] if the [markers] list contains one or + /// more markers that have not been added to the map view via [addMarkers] or + /// contains markers that have already been removed from the map view. Future removeMarkers(List markers) async { return GoogleMapsNavigationPlatform.instance .removeMarkers(viewId: _viewId, markers: markers); } - /// Remove all markers from the map. + /// Remove all markers from the map view. Future clearMarkers() { return GoogleMapsNavigationPlatform.instance.clearMarkers(viewId: _viewId); } - /// Get all polygons from map view. + /// Retrieves all polygons that have been added to the map view. Future> getPolygons() { return GoogleMapsNavigationPlatform.instance.getPolygons(viewId: _viewId); } - /// Add polygons to map view. + /// Add polygons to the map view. Future> addPolygons(List polygonOptions) { return GoogleMapsNavigationPlatform.instance .addPolygons(viewId: _viewId, polygonOptions: polygonOptions); } - /// Update polygons to map view. + /// Update polygons to the map view. /// - /// If [polygons] cannot be not be found throws [PolygonNotFoundException]. + /// Throws [PolygonNotFoundException] if the [polygons] list contains + /// polygon that has not beed added to the map view via [addPolygons] or + /// contains polygon that has already been removed from the map view. Future> updatePolygons(List polygons) async { return GoogleMapsNavigationPlatform.instance .updatePolygons(viewId: _viewId, polygons: polygons); } - /// Remove polygons from map. + /// Remove polygons from the map view. /// - /// If [polygons] cannot be not be found throws [PolygonNotFoundException]. + /// Throws [PolygonNotFoundException] if the [polygons] list contains + /// polygon that has not beed added to the map view via [addPolygons] or + /// contains polygon that has already been removed from the map view. Future removePolygons(List polygons) async { return GoogleMapsNavigationPlatform.instance .removePolygons(viewId: _viewId, polygons: polygons); } - /// Remove all polygons from map. + /// Remove all polygons from the map view. Future clearPolygons() { return GoogleMapsNavigationPlatform.instance.clearPolygons(viewId: _viewId); } - /// Get all polylines from map view. + /// Retrieves all polylines that have been added to the map view. Future> getPolylines() { return GoogleMapsNavigationPlatform.instance.getPolylines(viewId: _viewId); } - /// Add polylines to map view. + /// Add polylines to the map view. Future> addPolylines(List polylineOptions) { return GoogleMapsNavigationPlatform.instance .addPolylines(viewId: _viewId, polylineOptions: polylineOptions); } - /// Update polylines to map view. + /// Update polylines to the map view. /// - /// If [polylines] cannot be not be found throws [PolylineNotFoundException]. + /// Throws [PolylineNotFoundException] if the [polylines] list contains + /// polyline that has not beed added to the map view via [addPolylines] or + /// contains polyline that has already been removed from the map view. Future> updatePolylines(List polylines) async { return GoogleMapsNavigationPlatform.instance .updatePolylines(viewId: _viewId, polylines: polylines); } - /// Remove polylines from map. + /// Remove polylines from the map view. /// - /// If [polylines] cannot be not be found throws [PolylineNotFoundException]. + /// Throws [PolylineNotFoundException] if the [polylines] list contains + /// polyline that has not beed added to the map view via [addPolylines] or + /// contains polyline that has already been removed from the map view. Future removePolylines(List polylines) async { return GoogleMapsNavigationPlatform.instance .removePolylines(viewId: _viewId, polylines: polylines); } - /// Remove all polylines from map. + /// Remove all polylines from the map view. Future clearPolylines() { return GoogleMapsNavigationPlatform.instance .clearPolylines(viewId: _viewId); } - /// Get all circles from map view. + /// Gets all circles from the map view. Future> getCircles() { return GoogleMapsNavigationPlatform.instance.getCircles(viewId: _viewId); } - /// Add circles to map view. + /// Add circles to the map view. Future> addCircles(List options) { return GoogleMapsNavigationPlatform.instance .addCircles(viewId: _viewId, options: options); } - /// Update circles to map view. + /// Update circles to the map view. + /// + /// Throws [CircleNotFoundException] if the [circles] list contains one or + /// more circles that have not been added to the map view via [addCircles] or + /// contains circles that have already been removed from the map view. Future> updateCircles(List circles) async { return GoogleMapsNavigationPlatform.instance .updateCircles(viewId: _viewId, circles: circles); } - /// Remove circles from map. + /// Remove circles from the map view. + /// + /// Throws [CircleNotFoundException] if the [circles] list contains one or + /// more circles that have not been added to the map view via [addCircles] or + /// contains circles that have already been removed from the map view. Future removeCircles(List circles) async { return GoogleMapsNavigationPlatform.instance .removeCircles(viewId: _viewId, circles: circles); } - /// Remove all circles from map. + /// Remove all circles from the map view. Future clearCircles() { return GoogleMapsNavigationPlatform.instance.clearCircles(viewId: _viewId); } - /// Removes all markers, polylines, polygons, overlays, etc from the map. + /// Remove all markers, polylines, polygons, overlays, etc from the map view. Future clear() { return GoogleMapsNavigationPlatform.instance.clear(viewId: _viewId); } @@ -1028,6 +1303,7 @@ class GoogleNavigationViewController { /// [GoogleNavigationViewController.updateMarkers] or /// [GoogleNavigationViewController.removeMarkers] failed /// to find the marker given to the method. +/// {@category Navigation View} class MarkerNotFoundException implements Exception { /// Default constructor for [MarkerNotFoundException]. const MarkerNotFoundException(); @@ -1036,6 +1312,7 @@ class MarkerNotFoundException implements Exception { /// [GoogleNavigationViewController.updatePolygons] or /// [GoogleNavigationViewController.removePolygons] failed /// to find the polygon given to the method. +/// {@category Navigation View} class PolygonNotFoundException implements Exception { /// Default constructor for [PolygonNotFoundException]. const PolygonNotFoundException(); @@ -1044,6 +1321,7 @@ class PolygonNotFoundException implements Exception { /// [GoogleNavigationViewController.updatePolylines] or /// [GoogleNavigationViewController.removePolylines] failed /// to find the polyline given to the method. +/// {@category Navigation View} class PolylineNotFoundException implements Exception { /// Default constructor for [PolylineNotFoundException]. const PolylineNotFoundException(); @@ -1052,13 +1330,39 @@ class PolylineNotFoundException implements Exception { /// [GoogleNavigationViewController.updateCircles] or /// [GoogleNavigationViewController.removeCircles] failed /// to find the circle given to the method. +/// {@category Navigation View} class CircleNotFoundException implements Exception { /// Default constructor for [CircleNotFoundException]. const CircleNotFoundException(); } /// [GoogleNavigationViewController.setMapStyle] failed to set the map style. +/// {@category Navigation View} class MapStyleException implements Exception { /// Default constructor for [MapStyleException]. const MapStyleException(); } + +/// [GoogleNavigationViewController.setMaxZoomPreference] failed to set zoom level. +/// {@category Navigation View} +class MaxZoomRangeException implements Exception { + /// Default constructor for [MaxZoomRangeException]. + const MaxZoomRangeException(); + + @override + String toString() { + return 'MaxZoomRangeException: Cannot set max zoom to less than min zoom'; + } +} + +/// [GoogleNavigationViewController.setMinZoomPreference] failed to set zoom level. +/// {@category Navigation View} +class MinZoomRangeException implements Exception { + /// Default constructor for [MinZoomRangeException]. + const MinZoomRangeException(); + + @override + String toString() { + return 'MinZoomRangeException: Cannot set min zoom to greater than max zoom'; + } +} diff --git a/lib/src/google_maps_navigation_android.dart b/lib/src/google_maps_navigation_android.dart index bc08cc7..696ab76 100644 --- a/lib/src/google_maps_navigation_android.dart +++ b/lib/src/google_maps_navigation_android.dart @@ -20,13 +20,15 @@ import '../google_maps_navigation.dart'; import 'google_maps_navigation_platform_interface.dart'; import 'inspector/inspector_android.dart'; import 'inspector/inspector_platform.dart'; -import 'method_channel/common_session_api.dart'; -import 'method_channel/common_view_api.dart'; +import 'method_channel/method_channel.dart'; /// Google Maps Navigation Platform Android specific functionalities. /// @nodoc class GoogleMapsNavigationAndroid extends GoogleMapsNavigationPlatform - with CommonNavigationSessionAPI, CommonNavigationViewAPI { + with + CommonNavigationSessionAPI, + CommonNavigationViewAPI, + CommonImageRegistryAPI { /// Registers the Android implementation of GoogleMapsNavigationPlatform. static void registerWith() { GoogleMapsNavigationPlatform.instance = GoogleMapsNavigationAndroid(); diff --git a/lib/src/google_maps_navigation_ios.dart b/lib/src/google_maps_navigation_ios.dart index f20657e..6c59320 100644 --- a/lib/src/google_maps_navigation_ios.dart +++ b/lib/src/google_maps_navigation_ios.dart @@ -19,13 +19,15 @@ import '../google_maps_navigation.dart'; import 'google_maps_navigation_platform_interface.dart'; import 'inspector/inspector_ios.dart'; import 'inspector/inspector_platform.dart'; -import 'method_channel/common_session_api.dart'; -import 'method_channel/common_view_api.dart'; +import 'method_channel/method_channel.dart'; /// Google Maps Navigation Platform iOS specific functionalities. /// @nodoc class GoogleMapsNavigationIOS extends GoogleMapsNavigationPlatform - with CommonNavigationSessionAPI, CommonNavigationViewAPI { + with + CommonNavigationSessionAPI, + CommonNavigationViewAPI, + CommonImageRegistryAPI { /// Registers the iOS implementation of GoogleMapsNavigationPlatform. static void registerWith() { GoogleMapsNavigationPlatform.instance = GoogleMapsNavigationIOS(); diff --git a/lib/src/google_maps_navigation_platform_interface.dart b/lib/src/google_maps_navigation_platform_interface.dart index 62b576b..d6d6fe2 100644 --- a/lib/src/google_maps_navigation_platform_interface.dart +++ b/lib/src/google_maps_navigation_platform_interface.dart @@ -13,6 +13,7 @@ // limitations under the License. import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -21,14 +22,17 @@ import '../google_maps_navigation.dart'; /// Callback signature for when a map view is ready. /// -/// `id` is the platform view's unique identifier. +/// `viewId` is the platform view's unique identifier. /// @nodoc typedef MapReadyCallback = void Function(int viewId); /// Google Maps Navigation Platform Interface for iOS and Android implementations. /// @nodoc abstract class GoogleMapsNavigationPlatform extends PlatformInterface - with NavigationSessionAPIInterface, NavigationViewAPIInterface { + with + NavigationSessionAPIInterface, + NavigationViewAPIInterface, + ImageRegistryAPIInterface { /// Constructs a GoogleMapsNavigationPlatform. GoogleMapsNavigationPlatform() : super(token: _token); @@ -69,7 +73,8 @@ abstract class GoogleMapsNavigationPlatform extends PlatformInterface /// API interface for actions of the navigation session. abstract mixin class NavigationSessionAPIInterface { /// Creates navigation session in the native platform and returns navigation session controller. - Future createNavigationSession(); + Future createNavigationSession( + bool abnormalTerminationReportingEnabled); /// Check whether navigator has been initialized. Future isInitialized(); @@ -87,6 +92,9 @@ abstract mixin class NavigationSessionAPIInterface { /// Resets terms of service acceptance state. Future resetTermsAccepted(); + /// Gets the native navigation SDK version as string. + Future getNavSDKVersion(); + /// Has guidance been started. Future isGuidanceRunning(); @@ -177,18 +185,20 @@ abstract mixin class NavigationSessionAPIInterface { getNavigationRoadSnappedLocationEventStream(); /// Get navigation road snapped raw location event stream from the navigation session. + /// Android only. Stream getNavigationRoadSnappedRawLocationEventStream(); - /// Get navigation session event stream from the navigation session. - Stream getNavigationSessionEventStream(); - /// Get navigation on arrival event stream from the navigation session. Stream getNavigationOnArrivalEventStream(); /// Get navigation on rerouting event stream from the navigation session. Stream getNavigationOnReroutingEventStream(); + /// Get navigation on GPS availability update event stream from the navigation session. + Stream + getNavigationOnGpsAvailabilityUpdateEventStream(); + /// Get navigation traffic updated event stream from the navigation session. Stream getNavigationTrafficUpdatedEventStream(); @@ -214,7 +224,8 @@ abstract mixin class NavigationViewAPIInterface { Future isMyLocationEnabled({required int viewId}); /// Enabled location in the navigation view. - Future enableMyLocation({required int viewId, required bool enabled}); + Future setMyLocationEnabled( + {required int viewId, required bool enabled}); /// Get the map type. Future getMapType({required int viewId}); @@ -226,42 +237,54 @@ abstract mixin class NavigationViewAPIInterface { Future setMapStyle(int viewId, String? styleJson); /// Enables or disables the my-location button. - Future enableMyLocationButton( + Future setMyLocationButtonEnabled( + {required int viewId, required bool enabled}); + + /// Enables or disables if the my location button consumes click events. + Future setConsumeMyLocationButtonClickEventsEnabled( {required int viewId, required bool enabled}); /// Enables or disables the zoom gestures. - Future enableZoomGestures({required int viewId, required bool enabled}); + Future setZoomGesturesEnabled( + {required int viewId, required bool enabled}); /// Enables or disables the zoom controls. - Future enableZoomControls({required int viewId, required bool enabled}); + Future setZoomControlsEnabled( + {required int viewId, required bool enabled}); /// Enables or disables the compass. - Future enableCompass({required int viewId, required bool enabled}); + Future setCompassEnabled({required int viewId, required bool enabled}); /// Sets the preference for whether rotate gestures should be enabled or disabled. - Future enableRotateGestures( + Future setRotateGesturesEnabled( {required int viewId, required bool enabled}); /// Sets the preference for whether scroll gestures should be enabled or disabled. - Future enableScrollGestures( + Future setScrollGesturesEnabled( {required int viewId, required bool enabled}); /// Sets the preference for whether scroll gestures can take place at the same time as a zoom or rotate gesture. - Future enableScrollGesturesDuringRotateOrZoom( + Future setScrollGesturesDuringRotateOrZoomEnabled( {required int viewId, required bool enabled}); /// Sets the preference for whether tilt gestures should be enabled or disabled. - Future enableTiltGestures({required int viewId, required bool enabled}); + Future setTiltGesturesEnabled( + {required int viewId, required bool enabled}); /// Sets the preference for whether the Map Toolbar should be enabled or disabled. - Future enableMapToolbar({required int viewId, required bool enabled}); + Future setMapToolbarEnabled( + {required int viewId, required bool enabled}); /// Turns the traffic layer on or off. - Future enableTraffic({required int viewId, required bool enabled}); + Future setTrafficEnabled({required int viewId, required bool enabled}); /// Get the preference for whether the my location button should be enabled or disabled. Future isMyLocationButtonEnabled({required int viewId}); + /// Get the preference for whether the my location button consumes click events. + Future isConsumeMyLocationButtonClickEventsEnabled( + {required int viewId}); + /// Gets the preference for whether zoom gestures should be enabled or disabled. Future isZoomGesturesEnabled({required int viewId}); @@ -319,58 +342,78 @@ abstract mixin class NavigationViewAPIInterface { Future isNavigationTripProgressBarEnabled({required int viewId}); /// Enable navigation trip progress bar. - Future enableNavigationTripProgressBar( + Future setNavigationTripProgressBarEnabled( {required int viewId, required bool enabled}); /// Is the navigation header enabled. Future isNavigationHeaderEnabled({required int viewId}); /// Enable navigation header. - Future enableNavigationHeader( + Future setNavigationHeaderEnabled( {required int viewId, required bool enabled}); /// Is the navigation footer enabled. Future isNavigationFooterEnabled({required int viewId}); /// Enable the navigation footer. - Future enableNavigationFooter( + Future setNavigationFooterEnabled( {required int viewId, required bool enabled}); /// Is the recenter button enabled. Future isRecenterButtonEnabled({required int viewId}); /// Enable the recenter button. - Future enableRecenterButton( + Future setRecenterButtonEnabled( {required int viewId, required bool enabled}); /// Is the speed limit displayed. Future isSpeedLimitIconEnabled({required int viewId}); /// Should display speed limit. - Future enableSpeedLimitIcon( - {required int viewId, required bool enable}); + Future setSpeedLimitIconEnabled( + {required int viewId, required bool enabled}); /// Is speedometer displayed. Future isSpeedometerEnabled({required int viewId}); /// Should display speedometer. - Future enableSpeedometer({required int viewId, required bool enable}); + Future setSpeedometerEnabled( + {required int viewId, required bool enabled}); /// Is incident cards displayed. - Future isIncidentCardsEnabled({required int viewId}); + Future isTrafficIncidentCardsEnabled({required int viewId}); /// Should display incident cards. - Future enableIncidentCards({required int viewId, required bool enable}); + Future setTrafficIncidentCardsEnabled( + {required int viewId, required bool enabled}); /// Is navigation UI enabled. Future isNavigationUIEnabled({required int viewId}); /// Enable navigation UI. - Future enableNavigationUI({required int viewId, required bool enabled}); + Future setNavigationUIEnabled( + {required int viewId, required bool enabled}); /// Show route overview. Future showRouteOverview({required int viewId}); + /// Returns the minimum zoom level. + Future getMinZoomPreference({required int viewId}); + + /// Returns the maximum zoom level for the current camera position. + Future getMaxZoomPreference({required int viewId}); + + /// Removes any previously specified upper and lower zoom bounds. + Future resetMinMaxZoomPreference({required int viewId}); + + /// Sets a preferred lower bound for the camera zoom. + Future setMinZoomPreference( + {required int viewId, required double minZoomPreference}); + + /// Sets a preferred upper bound for the camera zoom. + Future setMaxZoomPreference( + {required int viewId, required double maxZoomPreference}); + /// Get map clicked event stream from the navigation view. Stream getMapClickEventStream({required int viewId}); @@ -388,7 +431,7 @@ abstract mixin class NavigationViewAPIInterface { Future> addMarkers( {required int viewId, required List markerOptions}); - /// Update markers to map view. + /// Update markers on the map view. Future> updateMarkers( {required int viewId, required List markers}); @@ -409,7 +452,7 @@ abstract mixin class NavigationViewAPIInterface { Future> addPolygons( {required int viewId, required List polygonOptions}); - /// Update polygons to map view. + /// Update polygons on the map view. Future> updatePolygons( {required int viewId, required List polygons}); @@ -427,7 +470,7 @@ abstract mixin class NavigationViewAPIInterface { Future> addPolylines( {required int viewId, required List polylineOptions}); - /// Update polylines to map view. + /// Update polylines on the map view. Future> updatePolylines( {required int viewId, required List polylines}); @@ -445,7 +488,7 @@ abstract mixin class NavigationViewAPIInterface { Future> addCircles( {required int viewId, required List options}); - /// Update circles to map view. + /// Update circles on the map view. Future> updateCircles( {required int viewId, required List circles}); @@ -456,24 +499,41 @@ abstract mixin class NavigationViewAPIInterface { /// Remove all circles from map view. Future clearCircles({required int viewId}); + /// Register camera changed listeners. + Future registerOnCameraChangedListener({required int viewId}); + /// Get navigation view marker event stream from the navigation view. - Stream getMarkerEventStream({required int viewId}); + Stream getMarkerEventStream({required int viewId}); /// Get navigation view marker drag event stream from the navigation view. - Stream getMarkerDragEventStream({required int viewId}); + Stream getMarkerDragEventStream({required int viewId}); /// Get navigation view polygon clicked event stream from the navigation view. - Stream getPolygonClickedEventStream( + Stream getPolygonClickedEventStream( {required int viewId}); /// Get navigation view polyline clicked event stream from the navigation view. - Stream getPolylineDtoClickedEventStream( + Stream getPolylineClickedEventStream( {required int viewId}); /// Get navigation view circle clicked event stream from the navigation view. - Stream getCircleDtoClickedEventStream( + Stream getCircleClickedEventStream({required int viewId}); + + /// Get navigation UI changed event stream from the navigation view. + Stream + getNavigationUIEnabledChangedEventStream({required int viewId}); + + /// Get navigation view my location clicked event stream from the navigation view. + Stream getMyLocationClickedEventStream( + {required int viewId}); + + /// Get navigation view my location button clicked event stream from the navigation view. + Stream getMyLocationButtonClickedEventStream( {required int viewId}); + /// Get navigation view camera changed event stream from the navigation view. + Stream getCameraChangedEventStream({required int viewId}); + /// Populates [GoogleNavigationInspectorPlatform.instance] to allow /// inspecting the platform map state. @visibleForTesting @@ -482,3 +542,23 @@ abstract mixin class NavigationViewAPIInterface { 'enableDebugInspection() has not been implemented.'); } } + +/// API interface for actions of the image registry. +/// @nodoc +abstract mixin class ImageRegistryAPIInterface { + /// Register bitmap to image registry. + Future registerBitmapImage( + {required Uint8List bitmap, + required double imagePixelRatio, + double? width, + double? height}); + + /// Delete bitmap from image registry. + Future unregisterImage({required ImageDescriptor imageDescriptor}); + + /// Get all registered bitmaps from image registry. + Future> getRegisteredImages(); + + /// Remove all registered bitmaps from image registry. + Future clearRegisteredImages(); +} diff --git a/lib/src/inspector/inspector_platform.dart b/lib/src/inspector/inspector_platform.dart index e63e684..bd54b32 100644 --- a/lib/src/inspector/inspector_platform.dart +++ b/lib/src/inspector/inspector_platform.dart @@ -13,7 +13,8 @@ // limitations under the License. import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import '../../google_maps_navigation.dart'; + +import '../method_channel/method_channel.dart'; /// The interface that platform-specific implementations of /// `google_maps_navigation` can extend to support state inspection in tests. diff --git a/lib/src/method_channel/common_image_api.dart b/lib/src/method_channel/common_image_api.dart new file mode 100644 index 0000000..acc5358 --- /dev/null +++ b/lib/src/method_channel/common_image_api.dart @@ -0,0 +1,74 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import 'package:flutter/services.dart'; + +import '../../google_maps_navigation.dart'; +import '../google_maps_navigation_platform_interface.dart'; +import 'method_channel.dart'; + +/// @nodoc +/// CommonImageRegistryAPI handles image registry API +/// actions that are common to both iOS and Android. +mixin CommonImageRegistryAPI on ImageRegistryAPIInterface { + final ImageRegistryApi _imageApi = ImageRegistryApi(); + + /// Keep track of image count, used to generate image ID's. + int _imageCounter = 0; + String _createImageId() { + final String imageId = 'Image_$_imageCounter'; + _imageCounter += 1; + return imageId; + } + + @override + Future registerBitmapImage( + {required Uint8List bitmap, + required double imagePixelRatio, + double? width, + double? height}) async { + final String newImageId = _createImageId(); + try { + final ImageDescriptorDto addedImage = await _imageApi.registerBitmapImage( + newImageId, bitmap, imagePixelRatio, width, height); + return addedImage.toImageDescriptor(); + } on PlatformException catch (error) { + if (error.code == 'imageDecodingFailed') { + throw const ImageDecodingFailedException(); + } else { + rethrow; + } + } + } + + @override + Future unregisterImage({required ImageDescriptor imageDescriptor}) { + return _imageApi.unregisterImage(imageDescriptor.toDto()); + } + + @override + Future> getRegisteredImages() async { + final List registeredImages = + await _imageApi.getRegisteredImages(); + return registeredImages + .whereType() + .map((ImageDescriptorDto e) => e.toImageDescriptor()) + .toList(); + } + + @override + Future clearRegisteredImages() { + return _imageApi.clearRegisteredImages(); + } +} diff --git a/lib/src/method_channel/common_session_api.dart b/lib/src/method_channel/common_session_api.dart index c6ecc19..81a97c4 100644 --- a/lib/src/method_channel/common_session_api.dart +++ b/lib/src/method_channel/common_session_api.dart @@ -19,6 +19,8 @@ import 'package:stream_transform/stream_transform.dart'; import '../../google_maps_navigation.dart'; import '../google_maps_navigation_platform_interface.dart'; +import 'convert/navigation_waypoint.dart'; +import 'method_channel.dart'; /// @nodoc /// CommonNavigationSessionAPI handles navigation session API @@ -48,12 +50,14 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { /// Creates navigation session in the native platform and returns navigation session controller. @override - Future createNavigationSession() async { + Future createNavigationSession( + bool abnormalTerminationReportingEnabled) async { // Setup session API streams. ensureSessionAPISetUp(); try { // Create native navigation session manager. - await _sessionApi.createNavigationSession(); + await _sessionApi + .createNavigationSession(abnormalTerminationReportingEnabled); } on PlatformException catch (e) { switch (e.code) { case 'notAuthorized': @@ -78,7 +82,16 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { @override Future cleanup() async { - await _sessionApi.cleanup(); + try { + return await _sessionApi.cleanup(); + } on PlatformException catch (e) { + switch (e.code) { + case 'sessionNotInitialized': + throw const SessionNotInitializedException(); + default: + rethrow; + } + } } /// Show terms and conditions dialog. @@ -124,6 +137,20 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { } } + @override + Future getNavSDKVersion() { + try { + return _sessionApi.getNavSDKVersion(); + } on PlatformException catch (e) { + switch (e.code) { + case 'sessionNotInitialized': + throw const SessionNotInitializedException(); + default: + rethrow; + } + } + } + @override Future isGuidanceRunning() async { try { @@ -173,13 +200,15 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { Future setDestinations(Destinations msg) async { try { final RouteStatusDto status = - await _sessionApi.setDestinations(navigationDestinationToDto(msg)); + await _sessionApi.setDestinations(msg.toDto()); - return navigationRouteStatusFromDto(status); + return status.toNavigationRouteStatus(); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': throw const SessionNotInitializedException(); + case 'routeTokenMalformed': + throw const RouteTokenMalformedException(); default: rethrow; } @@ -210,7 +239,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { if (waypointDto == null) { return null; } - return navigationWaypointFromDto(waypointDto); + return waypointDto.toNavigationWaypoint(); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -227,7 +256,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { try { final NavigationTimeAndDistanceDto timeAndDistance = await _sessionApi.getCurrentTimeAndDistance(); - return navigationTimeAndDistanceFromDto(timeAndDistance); + return timeAndDistance.toNavigationTimeAndDistance(); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -243,8 +272,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { Future setAudioGuidance( NavigationAudioGuidanceSettings settings) async { try { - return await _sessionApi - .setAudioGuidance(navigationAudioGuidanceSettingsToDto(settings)); + return await _sessionApi.setAudioGuidance(settings.toDto()); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -259,7 +287,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { @override Future setUserLocation(LatLng location) async { try { - return await _sessionApi.setUserLocation(latLngToDto(location)); + return await _sessionApi.setUserLocation(location.toDto()); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -325,10 +353,10 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { final RouteStatusDto routeStatus = await _sessionApi.simulateLocationsAlongNewRoute(waypoints.map( (NavigationWaypoint e) { - return navigationWaypointToDto(e); + return e.toDto(); }, ).toList()); - return navigationRouteStatusFromDto(routeStatus); + return routeStatus.toNavigationRouteStatus(); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -351,13 +379,13 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( waypoints.map( (NavigationWaypoint e) { - return navigationWaypointToDto(e); + return e.toDto(); }, ).toList(), - routingOptionsToDto(routingOptions), + routingOptions.toDto(), simulationOptionsToDto(simulationOptions), ); - return navigationRouteStatusFromDto(routeStatus); + return routeStatus.toNavigationRouteStatus(); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -379,12 +407,12 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { await _sessionApi.simulateLocationsAlongNewRouteWithRoutingOptions( waypoints.map( (NavigationWaypoint e) { - return navigationWaypointToDto(e); + return e.toDto(); }, ).toList(), - routingOptionsToDto(routingOptions), + routingOptions.toDto(), ); - return navigationRouteStatusFromDto(routeStatus); + return routeStatus.toNavigationRouteStatus(); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -479,7 +507,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { return routeSegments .where((RouteSegmentDto? p) => p != null) .cast() - .map((RouteSegmentDto s) => routeSegmentFromDto(s)) + .map((RouteSegmentDto s) => s.toRouteSegment()) .toList(); } on PlatformException catch (e) { switch (e.code) { @@ -519,9 +547,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { try { final RouteSegmentDto? currentRouteSegment = await _sessionApi.getCurrentRouteSegment(); - return currentRouteSegment != null - ? routeSegmentFromDto(currentRouteSegment) - : null; + return currentRouteSegment?.toRouteSegment(); } on PlatformException catch (e) { switch (e.code) { case 'sessionNotInitialized': @@ -537,8 +563,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { Stream getNavigationSpeedingEventStream() { return _sessionEventStreamController.stream .whereType() - .map((SpeedingUpdatedEventDto event) => - speedingUpdatedEventFromDto(event)); + .map((SpeedingUpdatedEventDto event) => event.toSpeedingUpdatedEvent()); } /// Get event stream for road snapped location updates. @@ -546,9 +571,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { Stream getNavigationRoadSnappedLocationEventStream() { return _sessionEventStreamController.stream - .whereType() - .map((RoadSnappedLocationUpdatedEventDto event) => - roadSnappedLocationUpdatedEventFromDto(event)); + .whereType(); } /// Get event stream for road snapped location updates. @@ -556,51 +579,45 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { Stream getNavigationRoadSnappedRawLocationEventStream() { return _sessionEventStreamController.stream - .whereType() - .map((RoadSnappedRawLocationUpdatedEventDto event) => - roadSnappedRawLocationUpdatedEventFromDto(event)); - } - - /// Get event stream for navigation session events. - @override - Stream getNavigationSessionEventStream() { - return _sessionEventStreamController.stream - .whereType() - .map((NavigationSessionEventDto event) => - navigationSessionEventFromDto(event)); + .whereType(); } /// Get navigation on arrival event stream from the navigation session. @override Stream getNavigationOnArrivalEventStream() { - return _sessionEventStreamController.stream - .whereType() - .map((OnArrivalEventDto event) => OnArrivalEvent( - waypoint: navigationWaypointFromDto(event.waypoint))); + return _sessionEventStreamController.stream.whereType(); } /// Get navigation on rerouting event stream from the navigation session. @override Stream getNavigationOnReroutingEventStream() { return _sessionEventStreamController.stream - .whereType() - .map((ReroutingEventDto event) => ()); + .whereType<_ReroutingEvent>() + .map((_ReroutingEvent event) => ()); + } + + /// Get navigation on GPS availability update event stream from the navigation session. + @override + Stream + getNavigationOnGpsAvailabilityUpdateEventStream() { + return _sessionEventStreamController.stream + .whereType(); } /// Get navigation traffic updated event stream from the navigation session. @override Stream getNavigationTrafficUpdatedEventStream() { return _sessionEventStreamController.stream - .whereType() - .map((TrafficUpdatedEventDto event) => ()); + .whereType<_TrafficUpdatedEvent>() + .map((_TrafficUpdatedEvent event) => ()); } /// Get navigation on route changed event stream from the navigation session. @override Stream getNavigationOnRouteChangedEventStream() { return _sessionEventStreamController.stream - .whereType() - .map((RouteChangedEventDto event) => ()); + .whereType<_RouteChangedEvent>() + .map((_RouteChangedEvent event) => ()); } /// Get navigation remaining time or distance event stream from the navigation session. @@ -608,9 +625,7 @@ mixin CommonNavigationSessionAPI implements NavigationSessionAPIInterface { Stream getNavigationRemainingTimeOrDistanceChangedEventStream() { return _sessionEventStreamController.stream - .whereType() - .map((RemainingTimeOrDistanceChangedEventDto event) => - remainingTimeOrDistanceChangedEventFromDto(event)); + .whereType(); } @override @@ -632,49 +647,63 @@ class NavigationSessionEventApiImpl implements NavigationSessionEventApi { final StreamController sessionEventStreamController; @override - void onNavigationSessionEvent(NavigationSessionEventDto event) { + void onSpeedingUpdated(SpeedingUpdatedEventDto event) { sessionEventStreamController.add(event); } @override - void onSpeedingUpdated(SpeedingUpdatedEventDto event) { - sessionEventStreamController.add(event); + void onArrival(NavigationWaypointDto waypoint) { + sessionEventStreamController + .add(OnArrivalEvent(waypoint: waypoint.toNavigationWaypoint())); } @override - void onRoadSnappedLocationUpdated(RoadSnappedLocationUpdatedEventDto event) { - sessionEventStreamController.add(event); + void onRerouting() { + sessionEventStreamController.add(_ReroutingEvent()); } @override - void onArrival(OnArrivalEventDto event) { - sessionEventStreamController.add(event); + void onGpsAvailabilityUpdate(bool available) { + sessionEventStreamController + .add(GpsAvailabilityUpdatedEvent(available: available)); } @override - void onRouteChanged(RouteChangedEventDto event) { - sessionEventStreamController.add(event); + void onRouteChanged() { + sessionEventStreamController.add(_RouteChangedEvent()); } @override - void onRerouting(ReroutingEventDto event) { - sessionEventStreamController.add(event); + void onTrafficUpdated() { + sessionEventStreamController.add(_TrafficUpdatedEvent()); } @override - void onTrafficUpdated(TrafficUpdatedEventDto event) { - sessionEventStreamController.add(event); + void onRoadSnappedLocationUpdated(LatLngDto location) { + sessionEventStreamController + .add(RoadSnappedLocationUpdatedEvent(location: location.toLatLng())); } + // Android only. @override - void onRoadSnappedRawLocationUpdated( - RoadSnappedRawLocationUpdatedEventDto event) { - sessionEventStreamController.add(event); + void onRoadSnappedRawLocationUpdated(LatLngDto location) { + sessionEventStreamController + .add(RoadSnappedRawLocationUpdatedEvent(location: location.toLatLng())); } @override void onRemainingTimeOrDistanceChanged( - RemainingTimeOrDistanceChangedEventDto event) { - sessionEventStreamController.add(event); + double remainingTime, double remainingDistance) { + sessionEventStreamController.add(RemainingTimeOrDistanceChangedEvent( + remainingTime: remainingTime, remainingDistance: remainingDistance)); } } + +/// Event wrapper for a route update events. +class _RouteChangedEvent {} + +/// Event wrapper for a rerouting events. +class _ReroutingEvent {} + +/// Event wrapper for a traffic updated events. +class _TrafficUpdatedEvent {} diff --git a/lib/src/method_channel/common_view_api.dart b/lib/src/method_channel/common_view_api.dart index 1d3022f..32af91c 100644 --- a/lib/src/method_channel/common_view_api.dart +++ b/lib/src/method_channel/common_view_api.dart @@ -19,10 +19,7 @@ import 'package:flutter/services.dart'; import '../../google_maps_navigation.dart'; import '../google_maps_navigation_platform_interface.dart'; -import '../types/util/circle_conversion.dart'; -import '../types/util/marker_conversion.dart'; -import '../types/util/polygon_conversion.dart'; -import '../types/util/polyline_conversion.dart'; +import 'method_channel.dart'; /// @nodoc /// Class that handles navigation view communications. @@ -32,7 +29,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { final StreamController<_ViewIdEventWrapper> _viewEventStreamController = StreamController<_ViewIdEventWrapper>.broadcast(); - /// Keep track of marker count, used to generate marker id's + /// Keep track of marker count, used to generate marker ID's. int _markerCounter = 0; String _createMarkerId() { final String markerId = 'Marker_$_markerCounter'; @@ -40,7 +37,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { return markerId; } - /// Keep track of polygon count, used to generate polygon id's + /// Keep track of polygon count, used to generate polygon ID's. int _polygonCounter = 0; String _createPolygonId() { final String polygonId = 'Polygon_$_polygonCounter'; @@ -48,7 +45,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { return polygonId; } - /// Keep track of polyline count, used to generate polyline id's + /// Keep track of polyline count, used to generate polyline ID's. int _polylineCounter = 0; String _createPolylineId() { final String polylineId = 'Polyline_$_polylineCounter'; @@ -56,7 +53,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { return polylineId; } - /// Keep track of circle count, used to generate circle id's + /// Keep track of circle count, used to generate circle ID's. int _circleCounter = 0; String _createCircleId() { final String circleId = 'Circle_$_circleCounter'; @@ -92,7 +89,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { ); final MapOptionsDto mapOptionsMessage = MapOptionsDto( cameraPosition: initialCameraPosition, - mapType: mapTypeToDto(mapOptions.mapType), + mapType: mapOptions.mapType.toDto(), compassEnabled: mapOptions.compassEnabled, rotateGesturesEnabled: mapOptions.rotateGesturesEnabled, scrollGesturesEnabled: mapOptions.scrollGesturesEnabled, @@ -104,16 +101,11 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { minZoomPreference: mapOptions.minZoomPreference, maxZoomPreference: mapOptions.maxZoomPreference, zoomControlsEnabled: mapOptions.zoomControlsEnabled, - cameraTargetBounds: mapOptions.cameraTargetBounds != null - ? latLngBoundsToDto(mapOptions.cameraTargetBounds!) - : null); + cameraTargetBounds: mapOptions.cameraTargetBounds?.toDto()); // Navigation options - final NavigationViewOptions navigationViewOptions = - initializationSettings.navigationViewOptions; final NavigationViewOptionsDto navigationOptionsMessage = - NavigationViewOptionsDto( - navigationUIEnabled: navigationViewOptions.navigationUIEnabled); + initializationSettings.navigationViewOptions.toDto(); // Build NavigationViewCreationMessage return NavigationViewCreationOptionsDto( @@ -132,20 +124,21 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableMyLocation({required int viewId, required bool enabled}) { - return _viewApi.enableMyLocation(viewId, enabled); + Future setMyLocationEnabled( + {required int viewId, required bool enabled}) { + return _viewApi.setMyLocationEnabled(viewId, enabled); } @override Future getMapType({required int viewId}) async { final MapTypeDto mapType = await _viewApi.getMapType(viewId); - return mapTypeFromDto(mapType); + return mapType.toMapType(); } @override Future setMapType( {required int viewId, required MapType mapType}) async { - return _viewApi.setMapType(viewId, mapTypeToDto(mapType)); + return _viewApi.setMapType(viewId, mapType.toDto()); } @override @@ -164,22 +157,29 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableMyLocationButton( + Future setMyLocationButtonEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableMyLocationButton(viewId, enabled); + return _viewApi.setMyLocationButtonEnabled(viewId, enabled); + } + + @override + Future setConsumeMyLocationButtonClickEventsEnabled( + {required int viewId, required bool enabled}) async { + return _viewApi.setConsumeMyLocationButtonClickEventsEnabled( + viewId, enabled); } @override - Future enableZoomGestures( + Future setZoomGesturesEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableZoomGestures(viewId, enabled); + return _viewApi.setZoomGesturesEnabled(viewId, enabled); } @override - Future enableZoomControls( + Future setZoomControlsEnabled( {required int viewId, required bool enabled}) async { try { - return await _viewApi.enableZoomControls(viewId, enabled); + return await _viewApi.setZoomControlsEnabled(viewId, enabled); } on PlatformException catch (error) { if (error.code == 'notSupported') { throw UnsupportedError('Zoom controls are not supported on iOS.'); @@ -190,39 +190,39 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableCompass({required int viewId, required bool enabled}) { - return _viewApi.enableCompass(viewId, enabled); + Future setCompassEnabled({required int viewId, required bool enabled}) { + return _viewApi.setCompassEnabled(viewId, enabled); } @override - Future enableRotateGestures( + Future setRotateGesturesEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableRotateGestures(viewId, enabled); + return _viewApi.setRotateGesturesEnabled(viewId, enabled); } @override - Future enableScrollGestures( + Future setScrollGesturesEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableScrollGestures(viewId, enabled); + return _viewApi.setScrollGesturesEnabled(viewId, enabled); } @override - Future enableScrollGesturesDuringRotateOrZoom( + Future setScrollGesturesDuringRotateOrZoomEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableScrollGesturesDuringRotateOrZoom(viewId, enabled); + return _viewApi.setScrollGesturesDuringRotateOrZoomEnabled(viewId, enabled); } @override - Future enableTiltGestures( + Future setTiltGesturesEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableTiltGestures(viewId, enabled); + return _viewApi.setTiltGesturesEnabled(viewId, enabled); } @override - Future enableMapToolbar( + Future setMapToolbarEnabled( {required int viewId, required bool enabled}) async { try { - return await _viewApi.enableMapToolbar(viewId, enabled); + return await _viewApi.setMapToolbarEnabled(viewId, enabled); } on PlatformException catch (error) { if (error.code == 'notSupported') { throw UnsupportedError('Map toolbar is not supported on iOS.'); @@ -233,8 +233,8 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableTraffic({required int viewId, required bool enabled}) { - return _viewApi.enableTraffic(viewId, enabled); + Future setTrafficEnabled({required int viewId, required bool enabled}) { + return _viewApi.setTrafficEnabled(viewId, enabled); } @override @@ -242,6 +242,12 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { return _viewApi.isMyLocationButtonEnabled(viewId); } + @override + Future isConsumeMyLocationButtonClickEventsEnabled( + {required int viewId}) { + return _viewApi.isConsumeMyLocationButtonClickEventsEnabled(viewId); + } + @override Future isNavigationTripProgressBarEnabled({required int viewId}) { return _viewApi.isNavigationTripProgressBarEnabled(viewId); @@ -315,7 +321,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { if (myLocation == null) { return null; } - return latLngFromDto(myLocation); + return myLocation.toLatLng(); } @override @@ -323,7 +329,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { final CameraPositionDto position = await _viewApi.getCameraPosition( viewId, ); - return cameraPositionfromDto(position); + return position.toCameraPosition(); } @override @@ -332,8 +338,9 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { viewId, ); return LatLngBounds( - southwest: latLngFromDto(bounds.southwest), - northeast: latLngFromDto(bounds.northeast)); + southwest: bounds.southwest.toLatLng(), + northeast: bounds.northeast.toLatLng(), + ); } @override @@ -346,7 +353,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { case CameraUpdateType.cameraPosition: unawaited(_viewApi .animateCameraToCameraPosition(viewId, - cameraPositionToDto(cameraUpdate.cameraPosition!), duration) + cameraUpdate.cameraPosition!.toCameraPosition(), duration) .then((bool success) => onFinished != null && Platform.isAndroid ? onFinished(success) : null)); @@ -359,11 +366,8 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { : null)); case CameraUpdateType.latLngBounds: unawaited(_viewApi - .animateCameraToLatLngBounds( - viewId, - latLngBoundsToDto(cameraUpdate.bounds!), - cameraUpdate.padding!, - duration) + .animateCameraToLatLngBounds(viewId, cameraUpdate.bounds!.toDto(), + cameraUpdate.padding!, duration) .then((bool success) => onFinished != null && Platform.isAndroid ? onFinished(success) : null)); @@ -404,14 +408,14 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { case CameraUpdateType.cameraPosition: assert(cameraUpdate.cameraPosition != null, 'Camera position is null'); return _viewApi.moveCameraToCameraPosition( - viewId, cameraPositionToDto(cameraUpdate.cameraPosition!)); + viewId, cameraUpdate.cameraPosition!.toCameraPosition()); case CameraUpdateType.latLng: return _viewApi.moveCameraToLatLng( viewId, cameraUpdate.latLng!.toDto()); case CameraUpdateType.latLngBounds: assert(cameraUpdate.padding != null, 'Camera position is null'); - return _viewApi.moveCameraToLatLngBounds(viewId, - latLngBoundsToDto(cameraUpdate.bounds!), cameraUpdate.padding!); + return _viewApi.moveCameraToLatLngBounds( + viewId, cameraUpdate.bounds!.toDto(), cameraUpdate.padding!); case CameraUpdateType.latLngZoom: return _viewApi.moveCameraToLatLngZoom( viewId, cameraUpdate.latLng!.toDto(), cameraUpdate.zoom!); @@ -431,14 +435,13 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { {required int viewId, required CameraPerspective perspective, required double? zoomLevel}) { - return _viewApi.followMyLocation( - viewId, cameraPerspectiveTypeToDto(perspective), zoomLevel); + return _viewApi.followMyLocation(viewId, perspective.toDto(), zoomLevel); } @override - Future enableNavigationTripProgressBar( + Future setNavigationTripProgressBarEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableNavigationTripProgressBar(viewId, enabled); + return _viewApi.setNavigationTripProgressBarEnabled(viewId, enabled); } @override @@ -447,9 +450,9 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableNavigationHeader( + Future setNavigationHeaderEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableNavigationHeader(viewId, enabled); + return _viewApi.setNavigationHeaderEnabled(viewId, enabled); } @override @@ -458,9 +461,9 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableNavigationFooter( + Future setNavigationFooterEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableNavigationFooter(viewId, enabled); + return _viewApi.setNavigationFooterEnabled(viewId, enabled); } @override @@ -469,9 +472,9 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableRecenterButton( + Future setRecenterButtonEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableRecenterButton(viewId, enabled); + return _viewApi.setRecenterButtonEnabled(viewId, enabled); } @override @@ -480,9 +483,9 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableSpeedLimitIcon( - {required int viewId, required bool enable}) { - return _viewApi.enableSpeedLimitIcon(viewId, enable); + Future setSpeedLimitIconEnabled( + {required int viewId, required bool enabled}) { + return _viewApi.setSpeedLimitIconEnabled(viewId, enabled); } @override @@ -491,19 +494,20 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableSpeedometer({required int viewId, required bool enable}) { - return _viewApi.enableSpeedometer(viewId, enable); + Future setSpeedometerEnabled( + {required int viewId, required bool enabled}) { + return _viewApi.setSpeedometerEnabled(viewId, enabled); } @override - Future isIncidentCardsEnabled({required int viewId}) { - return _viewApi.isIncidentCardsEnabled(viewId); + Future isTrafficIncidentCardsEnabled({required int viewId}) { + return _viewApi.isTrafficIncidentCardsEnabled(viewId); } @override - Future enableIncidentCards( - {required int viewId, required bool enable}) { - return _viewApi.enableIncidentCards(viewId, enable); + Future setTrafficIncidentCardsEnabled( + {required int viewId, required bool enabled}) { + return _viewApi.setTrafficIncidentCardsEnabled(viewId, enabled); } @override @@ -512,9 +516,9 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Future enableNavigationUI( + Future setNavigationUIEnabled( {required int viewId, required bool enabled}) { - return _viewApi.enableNavigationUI(viewId, enabled); + return _viewApi.setNavigationUIEnabled(viewId, enabled); } @override @@ -522,6 +526,49 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { return _viewApi.showRouteOverview(viewId); } + @override + Future getMinZoomPreference({required int viewId}) { + return _viewApi.getMinZoomPreference(viewId); + } + + @override + Future getMaxZoomPreference({required int viewId}) { + return _viewApi.getMaxZoomPreference(viewId); + } + + @override + Future resetMinMaxZoomPreference({required int viewId}) { + return _viewApi.resetMinMaxZoomPreference(viewId); + } + + @override + Future setMinZoomPreference( + {required int viewId, required double minZoomPreference}) async { + try { + return await _viewApi.setMinZoomPreference(viewId, minZoomPreference); + } on PlatformException catch (error) { + if (error.code == 'minZoomGreaterThanMaxZoom') { + throw const MinZoomRangeException(); + } else { + rethrow; + } + } + } + + @override + Future setMaxZoomPreference( + {required int viewId, required double maxZoomPreference}) async { + try { + return await _viewApi.setMaxZoomPreference(viewId, maxZoomPreference); + } on PlatformException catch (error) { + if (error.code == 'maxZoomLessThanMinZoom') { + throw const MaxZoomRangeException(); + } else { + rethrow; + } + } + } + Stream _unwrapEventStream({required int viewId}) { // If event that does not return _viewEventStreamController.stream @@ -553,7 +600,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { final List options = markerOptions.map((MarkerOptions opt) => opt.toDto()).toList(); - // Create marker objects with new id's + // Create marker objects with new ID's final List markersToAdd = options .map((MarkerOptionsDto options) => MarkerDto(markerId: _createMarkerId(), options: options)) @@ -638,7 +685,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { final List options = polygonOptions.map((PolygonOptions opt) => opt.toDto()).toList(); - // Create polygon objects with new id's + // Create polygon objects with new ID's final List polygonsToAdd = options .map((PolygonOptionsDto options) => PolygonDto(polygonId: _createPolygonId(), options: options)) @@ -715,11 +762,10 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { {required int viewId, required List polylineOptions}) async { // Convert options to pigeon format - final List options = polylineOptions - .map((PolylineOptions opt) => opt.toPolylineOptionsDto()) - .toList(); + final List options = + polylineOptions.map((PolylineOptions opt) => opt.toDto()).toList(); - // Create polyline objects with new id's + // Create polyline objects with new ID's final List polylinesToAdd = options .map((PolylineOptionsDto options) => PolylineDto(polylineId: _createPolylineId(), options: options)) @@ -800,7 +846,7 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { final List optionsDto = options.map((CircleOptions opt) => opt.toDto()).toList(); - // Create circle objects with new id's + // Create circle objects with new ID's final List circlesToAdd = optionsDto .map((CircleOptionsDto options) => CircleDto(circleId: _createCircleId(), options: options)) @@ -863,6 +909,11 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { return _viewApi.clearCircles(viewId); } + @override + Future registerOnCameraChangedListener({required int viewId}) { + return _viewApi.registerOnCameraChangedListener(viewId); + } + @override Stream getMapClickEventStream({required int viewId}) { return _unwrapEventStream(viewId: viewId); @@ -874,31 +925,55 @@ mixin CommonNavigationViewAPI on NavigationViewAPIInterface { } @override - Stream getMarkerEventStream({required int viewId}) { - return _unwrapEventStream(viewId: viewId); + Stream getMarkerEventStream({required int viewId}) { + return _unwrapEventStream(viewId: viewId); } @override - Stream getMarkerDragEventStream({required int viewId}) { - return _unwrapEventStream(viewId: viewId); + Stream getMarkerDragEventStream({required int viewId}) { + return _unwrapEventStream(viewId: viewId); } @override - Stream getPolygonClickedEventStream( + Stream getPolygonClickedEventStream( {required int viewId}) { - return _unwrapEventStream(viewId: viewId); + return _unwrapEventStream(viewId: viewId); } @override - Stream getPolylineDtoClickedEventStream( + Stream getPolylineClickedEventStream( {required int viewId}) { - return _unwrapEventStream(viewId: viewId); + return _unwrapEventStream(viewId: viewId); } @override - Stream getCircleDtoClickedEventStream( + Stream getCircleClickedEventStream( {required int viewId}) { - return _unwrapEventStream(viewId: viewId); + return _unwrapEventStream(viewId: viewId); + } + + @override + Stream + getNavigationUIEnabledChangedEventStream({required int viewId}) { + return _unwrapEventStream(viewId: viewId); + } + + @override + Stream getMyLocationClickedEventStream( + {required int viewId}) { + return _unwrapEventStream(viewId: viewId); + } + + @override + Stream getMyLocationButtonClickedEventStream( + {required int viewId}) { + return _unwrapEventStream(viewId: viewId); + } + + @override + Stream getCameraChangedEventStream( + {required int viewId}) { + return _unwrapEventStream(viewId: viewId); } } @@ -918,40 +993,76 @@ class NavigationViewEventApiImpl implements NavigationViewEventApi { } @override - void onMarkerEvent(MarkerEventDto event) { - _viewEventStreamController.add(_ViewIdEventWrapper(event.viewId, event)); + void onMapClickEvent(int viewId, LatLngDto latLng) { + _viewEventStreamController + .add(_ViewIdEventWrapper(viewId, MapClickEvent(latLng.toLatLng()))); } @override - void onMarkerDragEvent(MarkerDragEventDto event) { - _viewEventStreamController.add(_ViewIdEventWrapper(event.viewId, event)); + void onMapLongClickEvent(int viewId, LatLngDto latLng) { + _viewEventStreamController + .add(_ViewIdEventWrapper(viewId, MapLongClickEvent(latLng.toLatLng()))); } @override - void onPolygonClicked(PolygonClickedEventDto event) { - _viewEventStreamController.add(_ViewIdEventWrapper(event.viewId, event)); + void onMarkerDragEvent(int viewId, String markerId, + MarkerDragEventTypeDto eventType, LatLngDto position) { + // _viewEventStreamController.add(_ViewIdEventWrapper(event.viewId, event)); } @override - void onMapClickEvent(int viewId, LatLngDto latLng) { - _viewEventStreamController - .add(_ViewIdEventWrapper(viewId, MapClickEvent(latLngFromDto(latLng)))); + void onMarkerEvent( + int viewId, String markerId, MarkerEventTypeDto eventType) { + _viewEventStreamController.add(_ViewIdEventWrapper( + viewId, + MarkerEvent( + markerId: markerId, eventType: eventType.toMarkerEventType()))); } @override - void onMapLongClickEvent(int viewId, LatLngDto latLng) { + void onPolygonClicked(int viewId, String polygonId) { _viewEventStreamController.add( - _ViewIdEventWrapper(viewId, MapLongClickEvent(latLngFromDto(latLng)))); + _ViewIdEventWrapper(viewId, PolygonClickedEvent(polygonId: polygonId))); } @override - void onPolylineClicked(PolylineClickedEventDto msg) { - _viewEventStreamController.add(_ViewIdEventWrapper(msg.viewId, msg)); + void onPolylineClicked(int viewId, String polylineId) { + _viewEventStreamController.add(_ViewIdEventWrapper( + viewId, PolylineClickedEvent(polylineId: polylineId))); } @override - void onCircleClicked(CircleClickedEventDto msg) { - _viewEventStreamController.add(_ViewIdEventWrapper(msg.viewId, msg)); + void onCircleClicked(int viewId, String circleId) { + _viewEventStreamController.add( + _ViewIdEventWrapper(viewId, CircleClickedEvent(circleId: circleId))); + } + + @override + void onNavigationUIEnabledChanged(int viewId, bool enabled) { + _viewEventStreamController.add( + _ViewIdEventWrapper(viewId, NavigationUIEnabledChangedEvent(enabled))); + } + + @override + void onMyLocationClicked(int viewId) { + _viewEventStreamController + .add(_ViewIdEventWrapper(viewId, MyLocationClickedEvent())); + } + + @override + void onMyLocationButtonClicked(int viewId) { + _viewEventStreamController + .add(_ViewIdEventWrapper(viewId, MyLocationButtonClickedEvent())); + } + + @override + void onCameraChanged( + int viewId, CameraEventTypeDto eventType, CameraPositionDto position) { + _viewEventStreamController.add(_ViewIdEventWrapper( + viewId, + CameraChangedEvent( + eventType: eventType.toCameraEventType(), + position: position.toCameraPosition()))); } } diff --git a/lib/src/method_channel/convert/camera.dart b/lib/src/method_channel/convert/camera.dart new file mode 100644 index 0000000..87cb794 --- /dev/null +++ b/lib/src/method_channel/convert/camera.dart @@ -0,0 +1,70 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../../google_maps_navigation.dart'; +import '../method_channel.dart'; + +/// [CameraPositionDto] convert extension. +/// @nodoc +extension ConvertCameraPositionDto on CameraPositionDto { + /// Convert [CameraPositionDto] to [CameraPosition]. + CameraPosition toCameraPosition() => CameraPosition( + bearing: bearing, target: target.toLatLng(), tilt: tilt, zoom: zoom); +} + +/// [CameraPosition] convert extension. +/// @nodoc +extension ConvertCameraPosition on CameraPosition { + /// Convert [CameraPosition] to [CameraPositionDto]. + CameraPositionDto toCameraPosition() => CameraPositionDto( + bearing: bearing, target: target.toDto(), tilt: tilt, zoom: zoom); +} + +/// [CameraPerspective] convert extension. +/// @nodoc +extension ConvertCameraPerspective on CameraPerspective { + /// Convert [CameraPerspective] to [CameraPerspectiveDto]. + CameraPerspectiveDto toDto() { + switch (this) { + case CameraPerspective.tilted: + return CameraPerspectiveDto.tilted; + case CameraPerspective.topDownHeadingUp: + return CameraPerspectiveDto.topDownHeadingUp; + case CameraPerspective.topDownNorthUp: + return CameraPerspectiveDto.topDownNorthUp; + } + } +} + +/// [CameraEventTypeDto] convert extension. +/// @nodoc +extension ConvertCameraEventTypeDto on CameraEventTypeDto { + /// Convert [CameraEventTypeDto] to [CameraEventType]. + CameraEventType toCameraEventType() { + switch (this) { + case CameraEventTypeDto.moveStartedByApi: + return CameraEventType.moveStartedByApi; + case CameraEventTypeDto.moveStartedByGesture: + return CameraEventType.moveStartedByGesture; + case CameraEventTypeDto.onCameraMove: + return CameraEventType.onCameraMove; + case CameraEventTypeDto.onCameraIdle: + return CameraEventType.onCameraIdle; + case CameraEventTypeDto.onCameraStartedFollowingLocation: + return CameraEventType.onCameraStartedFollowingLocation; + case CameraEventTypeDto.onCameraStoppedFollowingLocation: + return CameraEventType.onCameraStoppedFollowingLocation; + } + } +} diff --git a/lib/src/types/util/circle_conversion.dart b/lib/src/method_channel/convert/circle.dart similarity index 91% rename from lib/src/types/util/circle_conversion.dart rename to lib/src/method_channel/convert/circle.dart index cb79c86..9155ed0 100644 --- a/lib/src/types/util/circle_conversion.dart +++ b/lib/src/method_channel/convert/circle.dart @@ -11,40 +11,13 @@ // 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. - -import 'package:flutter/material.dart'; +import 'dart:ui'; import '../../../google_maps_navigation.dart'; -import 'marker_conversion.dart'; -import 'polyline_conversion.dart'; - -/// [Circle] convert extension. -extension ConvertCircle on Circle { - /// Convert [Circle] to [CircleDto]. - CircleDto toDto() { - return CircleDto(circleId: circleId, options: options.toDto()); - } -} - -/// [CircleOptions] convert extension. -extension ConvertCircleOptions on CircleOptions { - /// Convert [CircleOptions] to [CircleOptionsDto]. - CircleOptionsDto toDto() { - return CircleOptionsDto( - position: position.toDto(), - radius: radius, - strokePattern: - strokePattern.map((PatternItem e) => e.toPatternItemDto()).toList(), - clickable: clickable, - fillColor: fillColor.value, - strokeColor: strokeColor.value, - strokeWidth: strokeWidth, - visible: visible, - zIndex: zIndex); - } -} +import '../method_channel.dart'; /// [CircleDto] convert extension. +/// @nodoc extension ConvertCircleDto on CircleDto { /// Convert [CircleDto] to [Circle]. Circle toCircle() { @@ -52,7 +25,17 @@ extension ConvertCircleDto on CircleDto { } } +/// [Circle] convert extension. +/// @nodoc +extension ConvertCircle on Circle { + /// Convert [Circle] to [CircleDto]. + CircleDto toDto() { + return CircleDto(circleId: circleId, options: options.toDto()); + } +} + /// [CircleOptionsDto] convert extension. +/// @nodoc extension ConvertCircleOptionsDto on CircleOptionsDto { /// Convert [CircleOptionsDto] to [CircleOptions]. CircleOptions toCircleOptions() { @@ -71,3 +54,22 @@ extension ConvertCircleOptionsDto on CircleOptionsDto { zIndex: zIndex); } } + +/// [CircleOptions] convert extension. +/// @nodoc +extension ConvertCircleOptions on CircleOptions { + /// Convert [CircleOptions] to [CircleOptionsDto]. + CircleOptionsDto toDto() { + return CircleOptionsDto( + position: position.toDto(), + radius: radius, + strokePattern: + strokePattern.map((PatternItem pi) => pi.toDto()).toList(), + clickable: clickable, + fillColor: fillColor.value, + strokeColor: strokeColor.value, + strokeWidth: strokeWidth, + visible: visible, + zIndex: zIndex); + } +} diff --git a/lib/src/method_channel/convert/convert.dart b/lib/src/method_channel/convert/convert.dart new file mode 100644 index 0000000..e964dab --- /dev/null +++ b/lib/src/method_channel/convert/convert.dart @@ -0,0 +1,28 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +export 'camera.dart'; +export 'circle.dart'; +export 'destinations.dart'; +export 'latlng.dart'; +export 'latlng_bounds.dart'; +export 'map_type.dart'; +export 'marker.dart'; +export 'navigation.dart'; +export 'navigation_display_options.dart'; +export 'navigation_routing_options.dart'; +export 'pattern.dart'; +export 'polygon.dart'; +export 'polyline.dart'; +export 'simulation.dart'; diff --git a/lib/src/method_channel/convert/destinations.dart b/lib/src/method_channel/convert/destinations.dart new file mode 100644 index 0000000..881dea7 --- /dev/null +++ b/lib/src/method_channel/convert/destinations.dart @@ -0,0 +1,33 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; +import 'navigation_route_token_options.dart'; +import 'navigation_waypoint.dart'; + +/// [Destinations] convert extension. +/// @nodoc +extension ConvertDestinations on Destinations { + /// Converts [Destinations] to [DestinationsDto] + DestinationsDto toDto() => DestinationsDto( + waypoints: waypoints.map( + (NavigationWaypoint e) { + return e.toDto(); + }, + ).toList(), + displayOptions: displayOptions.toDto(), + routingOptions: routingOptions?.toDto(), + routeTokenOptions: routeTokenOptions?.toDto()); +} diff --git a/lib/src/method_channel/convert/latlng.dart b/lib/src/method_channel/convert/latlng.dart new file mode 100644 index 0000000..f9c4921 --- /dev/null +++ b/lib/src/method_channel/convert/latlng.dart @@ -0,0 +1,34 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; + +/// [LatLng] convert extension. +/// @nodoc +extension ConvertLatLng on LatLng { + /// Converts [LatLng] to [LatLngDto] + LatLngDto toDto() { + return LatLngDto(latitude: latitude, longitude: longitude); + } +} + +/// [LatLngDto] convert extension. +/// @nodoc +extension ConvertLatLngDto on LatLngDto { + /// Converts [LatLngDto] to [LatLng] + LatLng toLatLng() { + return LatLng(latitude: latitude, longitude: longitude); + } +} diff --git a/lib/src/method_channel/convert/latlng_bounds.dart b/lib/src/method_channel/convert/latlng_bounds.dart new file mode 100644 index 0000000..d5a7991 --- /dev/null +++ b/lib/src/method_channel/convert/latlng_bounds.dart @@ -0,0 +1,26 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; + +/// [LatLngBounds] convert extension. +/// @nodoc +extension ConvertLatLngBounds on LatLngBounds { + /// Convert [LatLngBounds] to [LatLngBoundsDto]. + LatLngBoundsDto toDto() => LatLngBoundsDto( + northeast: northeast.toDto(), + southwest: southwest.toDto(), + ); +} diff --git a/lib/src/method_channel/convert/map_type.dart b/lib/src/method_channel/convert/map_type.dart new file mode 100644 index 0000000..e54f433 --- /dev/null +++ b/lib/src/method_channel/convert/map_type.dart @@ -0,0 +1,56 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; + +/// [MapType] convert extension. +/// @nodoc +extension ConvertMapType on MapType { + /// Converts [MapType] to [MapTypeDto] + MapTypeDto toDto() { + switch (this) { + case MapType.none: + return MapTypeDto.none; + case MapType.hybrid: + return MapTypeDto.hybrid; + case MapType.normal: + return MapTypeDto.normal; + case MapType.satellite: + return MapTypeDto.satellite; + case MapType.terrain: + return MapTypeDto.terrain; + } + } +} + +/// [MapTypeDto] convert extension. +/// @nodoc +extension ConvertMapTypeDto on MapTypeDto { + /// Converts [MapTypeDto] to [MapType] + MapType toMapType() { + switch (this) { + case MapTypeDto.none: + return MapType.none; + case MapTypeDto.hybrid: + return MapType.hybrid; + case MapTypeDto.normal: + return MapType.normal; + case MapTypeDto.satellite: + return MapType.satellite; + case MapTypeDto.terrain: + return MapType.terrain; + } + } +} diff --git a/lib/src/types/util/marker_conversion.dart b/lib/src/method_channel/convert/marker.dart similarity index 61% rename from lib/src/types/util/marker_conversion.dart rename to lib/src/method_channel/convert/marker.dart index 6170160..f8bfc1c 100644 --- a/lib/src/types/util/marker_conversion.dart +++ b/lib/src/method_channel/convert/marker.dart @@ -13,8 +13,10 @@ // limitations under the License. import '../../../google_maps_navigation.dart'; +import '../method_channel.dart'; -/// [Marker] convert extension +/// [Marker] convert extension. +/// @nodoc extension ConvertMarker on Marker { /// Converts [Marker] to [MarkerDto] MarkerDto toDto() { @@ -22,21 +24,17 @@ extension ConvertMarker on Marker { } } -/// [NavigationViewMarker] convert extension +/// [MarkerDto] convert extension. +/// @nodoc extension ConvertMarkerDto on MarkerDto { /// Converts [MarkerDto] to [Marker] Marker toMarker() { return Marker(markerId: markerId, options: options.toMarkerOptions()); } - - /// Converts [Marker] to [MarkerDto] - static MarkerDto fromMarker(Marker marker) { - return MarkerDto( - markerId: marker.markerId, options: marker.options.toDto()); - } } -/// [MarkerOptions] convert extension +/// [MarkerOptions] convert extension. +/// @nodoc extension ConvertMarkerOptions on MarkerOptions { /// Converts [MarkerOptions] to [MarkerOptionsDto] MarkerOptionsDto toDto() { @@ -45,6 +43,7 @@ extension ConvertMarkerOptions on MarkerOptions { anchor: anchor.toDto(), draggable: draggable, flat: flat, + icon: icon.toDto(), consumeTapEvents: consumeTapEvents, position: position.toDto(), rotation: rotation, @@ -54,7 +53,8 @@ extension ConvertMarkerOptions on MarkerOptions { } } -/// [MarkerOptionsDto] convert extension +/// [MarkerOptionsDto] convert extension. +/// @nodoc extension ConvertMarkerOptionsDto on MarkerOptionsDto { /// Converts [MarkerOptionsDto] to [MarkerOptions] MarkerOptions toMarkerOptions() { @@ -63,6 +63,7 @@ extension ConvertMarkerOptionsDto on MarkerOptionsDto { anchor: anchor.toMarkerAnchor(), draggable: draggable, flat: flat, + icon: icon.toImageDescriptor(), consumeTapEvents: consumeTapEvents, position: position.toLatLng(), rotation: rotation, @@ -72,7 +73,8 @@ extension ConvertMarkerOptionsDto on MarkerOptionsDto { } } -/// [MarkerAnchor] convert extension +/// [MarkerAnchor] convert extension. +/// @nodoc extension ConvertMarkerAnchor on MarkerAnchor { /// Converts [MarkerAnchor] to [MarkerAnchorDto] MarkerAnchorDto toDto() { @@ -80,7 +82,8 @@ extension ConvertMarkerAnchor on MarkerAnchor { } } -/// [MarkerAnchorDto] convert extension +/// [MarkerAnchorDto] convert extension. +/// @nodoc extension ConvertMarkerAnchorDto on MarkerAnchorDto { /// Converts [MarkerAnchorDto] to [MarkerAnchor] MarkerAnchor toMarkerAnchor() { @@ -89,6 +92,7 @@ extension ConvertMarkerAnchorDto on MarkerAnchorDto { } /// [InfoWindow] convert extension. +/// @nodoc extension ConvertInfoWindow on InfoWindow { /// Converts [InfoWindow] to [InfoWindowDto]. InfoWindowDto toDto() { @@ -98,6 +102,7 @@ extension ConvertInfoWindow on InfoWindow { } /// [InfoWindowDto] convert extension. +/// @nodoc extension ConvertInfoWindowDto on InfoWindowDto { /// Converts [InfoWindowDto] to [InfoWindow]. InfoWindow toInfoWindow() { @@ -106,18 +111,46 @@ extension ConvertInfoWindowDto on InfoWindowDto { } } -/// [LatLng] convert extension -extension ConvertLatLng on LatLng { - /// Converts [LatLng] to [LatLngDto] - LatLngDto toDto() { - return LatLngDto(latitude: latitude, longitude: longitude); +/// [MarkerEventTypeDto] convert extension. +/// @nodoc +extension ConvertMarkerEventType on MarkerEventTypeDto { + /// Converts [MarkerEventTypeDto] to [MarkerEventType] + MarkerEventType toMarkerEventType() { + switch (this) { + case MarkerEventTypeDto.clicked: + return MarkerEventType.clicked; + case MarkerEventTypeDto.infoWindowClicked: + return MarkerEventType.infoWindowClicked; + case MarkerEventTypeDto.infoWindowClosed: + return MarkerEventType.infoWindowClosed; + case MarkerEventTypeDto.infoWindowLongClicked: + return MarkerEventType.infoWindowLongClicked; + } + } +} + +/// [ImageDescriptor] convert extension. +/// @nodoc +extension ConvertImageDescriptor on ImageDescriptor { + /// Converts [ImageDescriptor] to [ImageDescriptorDto]. + ImageDescriptorDto toDto() { + return ImageDescriptorDto( + registeredImageId: registeredImageId, + imagePixelRatio: imagePixelRatio, + width: width, + height: height); } } -/// [LatLngDto] convert extension -extension ConvertLatLngDto on LatLngDto { - /// Converts [LatLngDto] to [LatLng] - LatLng toLatLng() { - return LatLng(latitude: latitude, longitude: longitude); +/// [ImageDescriptorDto] convert extension. +/// @nodoc +extension ConvertImageDescriptorDto on ImageDescriptorDto { + /// Converts [ImageDescriptorDto] to [ImageDescriptor]. + ImageDescriptor toImageDescriptor() { + return ImageDescriptor( + registeredImageId: registeredImageId, + imagePixelRatio: imagePixelRatio, + width: width, + height: height); } } diff --git a/lib/src/method_channel/convert/navigation.dart b/lib/src/method_channel/convert/navigation.dart new file mode 100644 index 0000000..b347ea9 --- /dev/null +++ b/lib/src/method_channel/convert/navigation.dart @@ -0,0 +1,208 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../../google_maps_navigation.dart'; +import '../method_channel.dart'; +import 'navigation_waypoint.dart'; + +/// [SpeedAlertSeverityDto] convert extension. +/// @nodoc +extension ConvertSpeedAlertSeverityDto on SpeedAlertSeverityDto { + /// Converts [SpeedAlertSeverityDto] to [SpeedAlertSeverity] + SpeedAlertSeverity toSpeedAlertSeverity() { + switch (this) { + case SpeedAlertSeverityDto.unknown: + return SpeedAlertSeverity.unknown; + case SpeedAlertSeverityDto.notSpeeding: + return SpeedAlertSeverity.notSpeeding; + case SpeedAlertSeverityDto.minor: + return SpeedAlertSeverity.minor; + case SpeedAlertSeverityDto.major: + return SpeedAlertSeverity.major; + } + } +} + +/// [SpeedingUpdatedEventDto] convert extension. +/// @nodoc +extension ConvertSpeedingUpdatedEventDto on SpeedingUpdatedEventDto { + /// Converts [SpeedingUpdatedEventDto] to [SpeedingUpdatedEvent] + SpeedingUpdatedEvent toSpeedingUpdatedEvent() => SpeedingUpdatedEvent( + percentageAboveLimit: percentageAboveLimit, + severity: severity.toSpeedAlertSeverity(), + ); +} + +/// [RouteStatusDto] convert extension. +/// @nodoc +extension ConvertRouteStatusDto on RouteStatusDto { + /// Converts [RouteStatusDto] to [NavigationRouteStatus] + NavigationRouteStatus toNavigationRouteStatus() { + switch (this) { + case RouteStatusDto.internalError: + return NavigationRouteStatus.internalError; + case RouteStatusDto.statusOk: + return NavigationRouteStatus.statusOk; + case RouteStatusDto.routeNotFound: + return NavigationRouteStatus.routeNotFound; + case RouteStatusDto.networkError: + return NavigationRouteStatus.networkError; + case RouteStatusDto.quotaExceeded: + return NavigationRouteStatus.quotaExceeded; + case RouteStatusDto.apiKeyNotAuthorized: + return NavigationRouteStatus.apiKeyNotAuthorized; + case RouteStatusDto.statusCanceled: + return NavigationRouteStatus.statusCanceled; + case RouteStatusDto.duplicateWaypointsError: + return NavigationRouteStatus.duplicateWaypointsError; + case RouteStatusDto.noWaypointsError: + return NavigationRouteStatus.noWaypointsError; + case RouteStatusDto.locationUnavailable: + return NavigationRouteStatus.locationUnavailable; + case RouteStatusDto.waypointError: + return NavigationRouteStatus.waypointError; + case RouteStatusDto.travelModeUnsupported: + return NavigationRouteStatus.travelModeUnsupported; + case RouteStatusDto.unknown: + return NavigationRouteStatus.unknown; + case RouteStatusDto.locationUnknown: + return NavigationRouteStatus.locationUnknown; + case RouteStatusDto.quotaCheckFailed: + return NavigationRouteStatus.quotaCheckFailed; + } + } +} + +/// [NavigationTimeAndDistanceDto] convert extension. +/// @nodoc +extension ConvertNavigationTimeAndDistanceDto on NavigationTimeAndDistanceDto { + /// Converts [NavigationTimeAndDistanceDto] to [NavigationTimeAndDistance] + NavigationTimeAndDistance toNavigationTimeAndDistance() => + NavigationTimeAndDistance(time: time, distance: distance); +} + +/// [NavigationAudioGuidanceSettings] convert extension. +/// @nodoc +extension ConvertNavigationAudioGuidanceSettings + on NavigationAudioGuidanceSettings { + /// Converts [NavigationAudioGuidanceSettings] to [NavigationAudioGuidanceSettingsDto] + NavigationAudioGuidanceSettingsDto toDto() { + late AudioGuidanceTypeDto? targetGuidanceType; + switch (guidanceType) { + case NavigationAudioGuidanceType.silent: + targetGuidanceType = AudioGuidanceTypeDto.silent; + case NavigationAudioGuidanceType.alertsAndGuidance: + targetGuidanceType = AudioGuidanceTypeDto.alertsAndGuidance; + case NavigationAudioGuidanceType.alertsOnly: + targetGuidanceType = AudioGuidanceTypeDto.alertsOnly; + case null: + targetGuidanceType = null; + } + + return NavigationAudioGuidanceSettingsDto( + isBluetoothAudioEnabled: isBluetoothAudioEnabled, + isVibrationEnabled: isVibrationEnabled, + guidanceType: targetGuidanceType, + ); + } +} + +/// [RouteSegmentTrafficDataRoadStretchRenderingDataDto] convert extension. +/// @nodoc +extension ConvertRouteSegmentTrafficDataRoadStretchRenderingDataDto + on RouteSegmentTrafficDataRoadStretchRenderingDataDto { + /// Converts [RouteSegmentTrafficDataRoadStretchRenderingDataDto] to [RouteSegmentTrafficDataRoadStretchRenderingData] + RouteSegmentTrafficDataRoadStretchRenderingData + toRouteSegmentTrafficDataRoadStretchRenderingData() => + RouteSegmentTrafficDataRoadStretchRenderingData( + style: () { + switch (style) { + case RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto + .slowerTraffic: + return RouteSegmentTrafficDataRoadStretchRenderingDataStyle + .slowerTraffic; + case RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto + .trafficJam: + return RouteSegmentTrafficDataRoadStretchRenderingDataStyle + .trafficJam; + case RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto + .unknown: + return RouteSegmentTrafficDataRoadStretchRenderingDataStyle + .unknown; + } + }(), + lengthMeters: lengthMeters, + offsetMeters: offsetMeters, + ); +} + +/// [RouteSegmentTrafficDataDto] convert extension. +/// @nodoc +extension ConvertRouteSegmentTrafficDataDto on RouteSegmentTrafficDataDto { + /// Converts [RouteSegmentTrafficDataDto] to [RouteSegmentTrafficData] + RouteSegmentTrafficData toRouteSegmentTrafficData() => + RouteSegmentTrafficData( + status: () { + switch (status) { + case RouteSegmentTrafficDataStatusDto.ok: + return RouteSegmentTrafficDataStatus.ok; + case RouteSegmentTrafficDataStatusDto.unavailable: + return RouteSegmentTrafficDataStatus.unavailable; + } + }(), + roadStretchRenderingDataList: roadStretchRenderingDataList + .where((RouteSegmentTrafficDataRoadStretchRenderingDataDto? d) => + d != null) + .cast() + .map((RouteSegmentTrafficDataRoadStretchRenderingDataDto d) => + d.toRouteSegmentTrafficDataRoadStretchRenderingData()) + .toList(), + ); +} + +/// [RouteSegmentDto] convert extension. +/// @nodoc +extension ConvertRouteSegmentDto on RouteSegmentDto { + /// Converts [RouteSegmentDto] to [RouteSegment] + RouteSegment toRouteSegment() => RouteSegment( + destinationLatLng: LatLng( + latitude: destinationLatLng.latitude, + longitude: destinationLatLng.longitude), + destinationWaypoint: destinationWaypoint?.toNavigationWaypoint(), + latLngs: latLngs + ?.where((LatLngDto? p) => p != null) + .cast() + .map((LatLngDto p) => + LatLng(latitude: p.latitude, longitude: p.longitude)) + .toList(), + trafficData: trafficData?.toRouteSegmentTrafficData(), + ); +} + +/// [NavigationViewOptions] convert extension. +/// @nodoc +extension ConvertNavigationViewOptions on NavigationViewOptions { + /// Converts [NavigationViewOptions] to [NavigationViewOptionsDto] + NavigationViewOptionsDto toDto() { + late NavigationUIEnabledPreferenceDto preference; + switch (navigationUIEnabledPreference) { + case NavigationUIEnabledPreference.automatic: + preference = NavigationUIEnabledPreferenceDto.automatic; + case NavigationUIEnabledPreference.disabled: + preference = NavigationUIEnabledPreferenceDto.disabled; + } + + return NavigationViewOptionsDto(navigationUIEnabledPreference: preference); + } +} diff --git a/lib/src/method_channel/convert/navigation_display_options.dart b/lib/src/method_channel/convert/navigation_display_options.dart new file mode 100644 index 0000000..10e9b64 --- /dev/null +++ b/lib/src/method_channel/convert/navigation_display_options.dart @@ -0,0 +1,27 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; + +/// [NavigationDisplayOptions] convert extension. +/// @nodoc +extension ConvertNavigationDisplayOptions on NavigationDisplayOptions { + /// Converts [NavigationWaypoint] to [NavigationDisplayOptionsDto] + NavigationDisplayOptionsDto toDto() => NavigationDisplayOptionsDto( + showDestinationMarkers: showDestinationMarkers, + showStopSigns: showStopSigns, + showTrafficLights: showTrafficLights, + ); +} diff --git a/lib/src/method_channel/convert/navigation_route_token_options.dart b/lib/src/method_channel/convert/navigation_route_token_options.dart new file mode 100644 index 0000000..70cc19a --- /dev/null +++ b/lib/src/method_channel/convert/navigation_route_token_options.dart @@ -0,0 +1,26 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; + +/// [NavigationDisplayOptions] convert extension. +/// @nodoc +extension ConvertRouteTokenOptions on RouteTokenOptions { + /// Converts [RouteTokenOptions] to [RouteTokenOptionsDto] + RouteTokenOptionsDto toDto() => RouteTokenOptionsDto( + routeToken: routeToken, + travelMode: travelMode?.toDto(), + ); +} diff --git a/lib/src/method_channel/convert/navigation_routing_options.dart b/lib/src/method_channel/convert/navigation_routing_options.dart new file mode 100644 index 0000000..42a00a2 --- /dev/null +++ b/lib/src/method_channel/convert/navigation_routing_options.dart @@ -0,0 +1,85 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; + +/// [RoutingOptions] convert extension. +/// @nodoc +extension ConvertRoutingOptions on RoutingOptions { + /// Converts [RoutingOptions] to [RoutingOptionsDto] + RoutingOptionsDto toDto() => RoutingOptionsDto( + alternateRoutesStrategy: alternateRoutesStrategy?.toDto(), + routingStrategy: routingStrategy?.toDto(), + targetDistanceMeters: targetDistanceMeters, + travelMode: travelMode?.toDto(), + avoidFerries: avoidFerries, + avoidHighways: avoidHighways, + avoidTolls: avoidTolls, + locationTimeoutMs: locationTimeoutMs, + ); +} + +/// [NavigationAlternateRoutesStrategy] convert extension. +/// @nodoc +extension ConvertNavigationAlternateRoutesStrategy + on NavigationAlternateRoutesStrategy { + /// Converts [NavigationAlternateRoutesStrategy] to [AlternateRoutesStrategyDto] + AlternateRoutesStrategyDto toDto() { + switch (this) { + case NavigationAlternateRoutesStrategy.all: + return AlternateRoutesStrategyDto.all; + case NavigationAlternateRoutesStrategy.none: + return AlternateRoutesStrategyDto.none; + case NavigationAlternateRoutesStrategy.one: + return AlternateRoutesStrategyDto.one; + } + } +} + +/// [NavigationRoutingStrategy] convert extension. +/// @nodoc +extension ConvertNavigationRoutingStrategy on NavigationRoutingStrategy { + /// Converts [NavigationRoutingStrategy] to [RoutingStrategyDto] + RoutingStrategyDto toDto() { + switch (this) { + case NavigationRoutingStrategy.defaultBest: + return RoutingStrategyDto.defaultBest; + case NavigationRoutingStrategy.deltaToTargetDistance: + return RoutingStrategyDto.deltaToTargetDistance; + case NavigationRoutingStrategy.shorter: + return RoutingStrategyDto.shorter; + } + } +} + +/// [NavigationTravelMode] convert extension. +/// @nodoc +extension ConvertNavigationTravelMode on NavigationTravelMode { + /// Converts [NavigationTravelMode] to [TravelModeDto] + TravelModeDto toDto() { + switch (this) { + case NavigationTravelMode.driving: + return TravelModeDto.driving; + case NavigationTravelMode.cycling: + return TravelModeDto.cycling; + case NavigationTravelMode.walking: + return TravelModeDto.walking; + case NavigationTravelMode.twoWheeler: + return TravelModeDto.twoWheeler; + case NavigationTravelMode.taxi: + return TravelModeDto.taxi; + } + } +} diff --git a/lib/src/method_channel/convert/navigation_waypoint.dart b/lib/src/method_channel/convert/navigation_waypoint.dart new file mode 100644 index 0000000..74e7492 --- /dev/null +++ b/lib/src/method_channel/convert/navigation_waypoint.dart @@ -0,0 +1,42 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../types/types.dart'; +import '../method_channel.dart'; + +/// [NavigationWaypointDto] convert extension. +/// @nodoc +extension ConvertNavigationWaypointDto on NavigationWaypointDto { + /// Converts [NavigationWaypointDto] to [NavigationWaypoint] + NavigationWaypoint toNavigationWaypoint() => NavigationWaypoint( + title: title, + target: target?.toLatLng(), + placeID: placeID, + preferSameSideOfRoad: preferSameSideOfRoad, + preferredSegmentHeading: preferredSegmentHeading, + ); +} + +/// [NavigationWaypoint] convert extension. +/// @nodoc +extension ConvertNavigationWaypoint on NavigationWaypoint { + /// Converts [NavigationWaypoint] to [NavigationWaypointDto] + NavigationWaypointDto toDto() => NavigationWaypointDto( + title: title, + target: target?.toDto(), + placeID: placeID, + preferSameSideOfRoad: preferSameSideOfRoad, + preferredSegmentHeading: preferredSegmentHeading, + ); +} diff --git a/lib/src/method_channel/convert/pattern.dart b/lib/src/method_channel/convert/pattern.dart new file mode 100644 index 0000000..127a70f --- /dev/null +++ b/lib/src/method_channel/convert/pattern.dart @@ -0,0 +1,56 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../../google_maps_navigation.dart'; +import '../method_channel.dart'; + +/// [PatternItemDto] convert extension. +/// @nodoc +extension ConvertPatternItemDto on PatternItemDto { + /// Convert [PatternItemDto] to [PatternItem]. + PatternItem toPatternItem() { + switch (type) { + case PatternTypeDto.dash: + return DashPattern(length: length!); + case PatternTypeDto.dot: + return const DotPattern(); + case PatternTypeDto.gap: + return GapPattern(length: length!); + } + } +} + +/// [PatternItem] convert extension. +/// @nodoc +extension ConvertPatternItem on PatternItem { + /// Convert [PatternItem] to [PatternItemDto]. + PatternItemDto toDto() { + switch (type) { + case PatternType.dash: + return PatternItemDto( + type: PatternTypeDto.dash, + length: (this as DashPattern).length, + ); + case PatternType.dot: + return PatternItemDto( + type: PatternTypeDto.dot, + ); + case PatternType.gap: + return PatternItemDto( + type: PatternTypeDto.gap, + length: (this as GapPattern).length, + ); + } + } +} diff --git a/lib/src/types/util/polygon_conversion.dart b/lib/src/method_channel/convert/polygon.dart similarity index 96% rename from lib/src/types/util/polygon_conversion.dart rename to lib/src/method_channel/convert/polygon.dart index e675ca1..f618d1a 100644 --- a/lib/src/types/util/polygon_conversion.dart +++ b/lib/src/method_channel/convert/polygon.dart @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:flutter/material.dart'; +import 'dart:ui'; import '../../../google_maps_navigation.dart'; -import 'marker_conversion.dart'; +import '../method_channel.dart'; /// [Polygon] convert extension. +/// @nodoc extension ConvertPolygon on Polygon { /// Convert [Polygon] to [PolygonDto]. PolygonDto toDto() { @@ -26,6 +27,7 @@ extension ConvertPolygon on Polygon { } /// [PolygonOptions] convert extension. +/// @nodoc extension ConvertPolygonOptions on PolygonOptions { /// Convert [PolygonOptions] to [PolygonOptionsDto]. PolygonOptionsDto toDto() { @@ -46,6 +48,7 @@ extension ConvertPolygonOptions on PolygonOptions { } /// [PolygonDto] convert extension. +/// @nodoc extension ConvertPolygonDto on PolygonDto { /// Convert [PolygonDto] to [Polygon]. Polygon toPolygon() { @@ -54,6 +57,7 @@ extension ConvertPolygonDto on PolygonDto { } /// [PolygonOptionsDto] convert extension. +/// @nodoc extension ConvertPolygonOptionsDto on PolygonOptionsDto { /// Convert [PolygonOptionsDto] to [PolygonOptions]. PolygonOptions toPolygonOptions() { diff --git a/lib/src/types/util/polyline_conversion.dart b/lib/src/method_channel/convert/polyline.dart similarity index 83% rename from lib/src/types/util/polyline_conversion.dart rename to lib/src/method_channel/convert/polyline.dart index dbf5088..b286fae 100644 --- a/lib/src/types/util/polyline_conversion.dart +++ b/lib/src/method_channel/convert/polyline.dart @@ -12,24 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:flutter/material.dart'; +import 'dart:ui'; import '../../../google_maps_navigation.dart'; -import 'marker_conversion.dart'; +import '../method_channel.dart'; /// [Polyline] convert extension. +/// @nodoc extension ConvertPolyline on Polyline { /// Convert [Polyline] to [NavigationViewPolyline]. PolylineDto toNavigationViewPolyline() { - return PolylineDto( - polylineId: polylineId, options: options.toPolylineOptionsDto()); + return PolylineDto(polylineId: polylineId, options: options.toDto()); } } /// [PolylineOptions] convert extension. +/// @nodoc extension ConvertPolylineOptions on PolylineOptions { /// Convert [PolylineOptions] to [PolylineOptionsDto]. - PolylineOptionsDto toPolylineOptionsDto() { + PolylineOptionsDto toDto() { return PolylineOptionsDto( points: points?.map((LatLng point) => point.toDto()).toList(), clickable: clickable, @@ -37,7 +38,7 @@ extension ConvertPolylineOptions on PolylineOptions { strokeColor: strokeColor?.value, strokeJointType: strokeJointType?.toStrokeJointTypeDto(), strokePattern: - strokePattern?.map((PatternItem e) => e.toPatternItemDto()).toList(), + strokePattern?.map((PatternItem pi) => pi.toDto()).toList(), strokeWidth: strokeWidth, visible: visible, zIndex: zIndex, @@ -49,6 +50,7 @@ extension ConvertPolylineOptions on PolylineOptions { } /// [StyleSpan] convert extension. +/// @nodoc extension ConvertStyleSpan on StyleSpan { /// Convert [StyleSpan] to [NavigationViewStyleSpan]. StyleSpanDto toNavigationViewStyleSpan() { @@ -64,6 +66,7 @@ extension ConvertStyleSpan on StyleSpan { } /// [StrokeJointType] convert extension. +/// @nodoc extension ConvertStrokeJointType on StrokeJointType { /// Convert [StrokeJointType] to [StrokeJointTypeDto]. StrokeJointTypeDto toStrokeJointTypeDto() { @@ -79,6 +82,7 @@ extension ConvertStrokeJointType on StrokeJointType { } /// [PolylineDto] convert extension. +/// @nodoc extension ConvertNavigationViewPolyline on PolylineDto { /// Convert [PolylineDto] to [Polyline]. Polyline toPolyline() { @@ -90,6 +94,7 @@ extension ConvertNavigationViewPolyline on PolylineDto { } /// [PolylineOptionsDto] convert extension. +/// @nodoc extension ConvertPolylineOptionsDto on PolylineOptionsDto { /// Convert [PolylineOptionsDto] to [PolylineOptions]. PolylineOptions toPolylineOptions() { @@ -103,7 +108,7 @@ extension ConvertPolylineOptionsDto on PolylineOptionsDto { strokeColor: strokeColor != null ? Color(strokeColor!) : null, strokeJointType: strokeJointType?.toStrokeJointType(), strokePattern: strokePattern - ?.map((PatternItemDto? e) => e?.toPatternItem()) + ?.map((PatternItemDto? pidto) => pidto?.toPatternItem()) .whereType() .toList(), strokeWidth: strokeWidth, @@ -113,6 +118,7 @@ extension ConvertPolylineOptionsDto on PolylineOptionsDto { } /// [StrokeJointTypeDto] convert extension. +/// @nodoc extension ConvertStrokeJointTypeDto on StrokeJointTypeDto { /// Convert [StrokeJointTypeDto] to [StrokeJointType]. StrokeJointType toStrokeJointType() { @@ -126,18 +132,3 @@ extension ConvertStrokeJointTypeDto on StrokeJointTypeDto { } } } - -/// [PatternItemDto] convert extension. -extension ConvertPatternItemDto on PatternItemDto { - /// Convert [PatternItemDto] to [PatternItem]. - PatternItem toPatternItem() { - switch (type) { - case PatternTypeDto.dash: - return DashPattern(length: length!); - case PatternTypeDto.dot: - return const DotPattern(); - case PatternTypeDto.gap: - return GapPattern(length: length!); - } - } -} diff --git a/lib/src/method_channel/convert/simulation.dart b/lib/src/method_channel/convert/simulation.dart new file mode 100644 index 0000000..c5c7dd1 --- /dev/null +++ b/lib/src/method_channel/convert/simulation.dart @@ -0,0 +1,22 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import '../../../google_maps_navigation.dart'; +import '../method_channel.dart'; + +/// Converts navigation options to the Pigeon DTO format. +/// @nodoc +SimulationOptionsDto simulationOptionsToDto(SimulationOptions options) { + return SimulationOptionsDto(speedMultiplier: options.speedMultiplier); +} diff --git a/lib/src/method_channel/messages.g.dart b/lib/src/method_channel/messages.g.dart index 896f5c9..42cd47d 100644 --- a/lib/src/method_channel/messages.g.dart +++ b/lib/src/method_channel/messages.g.dart @@ -40,6 +40,16 @@ List wrapResponse( return [error.code, error.message, error.details]; } +/// Determines the initial visibility of the navigation UI on map initialization. +enum NavigationUIEnabledPreferenceDto { + /// Navigation UI gets enabled if the navigation + /// session has already been successfully started. + automatic, + + /// Navigation UI is disabled. + disabled, +} + enum MapTypeDto { none, normal, @@ -79,10 +89,13 @@ enum PatternTypeDto { gap, } -enum NavigationSessionEventTypeDto { - arrivalEvent, - routeChanged, - errorReceived, +enum CameraEventTypeDto { + moveStartedByApi, + moveStartedByGesture, + onCameraMove, + onCameraIdle, + onCameraStartedFollowingLocation, + onCameraStoppedFollowingLocation, } enum AlternateRoutesStrategyDto { @@ -248,22 +261,23 @@ class MapOptionsDto { /// Object containing navigation options used to initialize Google Navigation view. class NavigationViewOptionsDto { NavigationViewOptionsDto({ - required this.navigationUIEnabled, + required this.navigationUIEnabledPreference, }); /// Determines the initial visibility of the navigation UI on map initialization. - bool navigationUIEnabled; + NavigationUIEnabledPreferenceDto navigationUIEnabledPreference; Object encode() { return [ - navigationUIEnabled, + navigationUIEnabledPreference.index, ]; } static NavigationViewOptionsDto decode(Object result) { result as List; return NavigationViewOptionsDto( - navigationUIEnabled: result[0]! as bool, + navigationUIEnabledPreference: + NavigationUIEnabledPreferenceDto.values[result[0]! as int], ); } } @@ -375,6 +389,7 @@ class MarkerOptionsDto { required this.infoWindow, required this.visible, required this.zIndex, + required this.icon, }); double alpha; @@ -397,6 +412,8 @@ class MarkerOptionsDto { double zIndex; + ImageDescriptorDto icon; + Object encode() { return [ alpha, @@ -409,6 +426,7 @@ class MarkerOptionsDto { infoWindow.encode(), visible, zIndex, + icon.encode(), ]; } @@ -425,6 +443,43 @@ class MarkerOptionsDto { infoWindow: InfoWindowDto.decode(result[7]! as List), visible: result[8]! as bool, zIndex: result[9]! as double, + icon: ImageDescriptorDto.decode(result[10]! as List), + ); + } +} + +class ImageDescriptorDto { + ImageDescriptorDto({ + this.registeredImageId, + this.imagePixelRatio, + this.width, + this.height, + }); + + String? registeredImageId; + + double? imagePixelRatio; + + double? width; + + double? height; + + Object encode() { + return [ + registeredImageId, + imagePixelRatio, + width, + height, + ]; + } + + static ImageDescriptorDto decode(Object result) { + result as List; + return ImageDescriptorDto( + registeredImageId: result[0] as String?, + imagePixelRatio: result[1] as double?, + width: result[2] as double?, + height: result[3] as double?, ); } } @@ -486,73 +541,6 @@ class MarkerAnchorDto { } } -class MarkerEventDto { - MarkerEventDto({ - required this.viewId, - required this.markerId, - required this.eventType, - }); - - int viewId; - - String markerId; - - MarkerEventTypeDto eventType; - - Object encode() { - return [ - viewId, - markerId, - eventType.index, - ]; - } - - static MarkerEventDto decode(Object result) { - result as List; - return MarkerEventDto( - viewId: result[0]! as int, - markerId: result[1]! as String, - eventType: MarkerEventTypeDto.values[result[2]! as int], - ); - } -} - -class MarkerDragEventDto { - MarkerDragEventDto({ - required this.viewId, - required this.markerId, - required this.eventType, - required this.position, - }); - - int viewId; - - String markerId; - - MarkerDragEventTypeDto eventType; - - LatLngDto position; - - Object encode() { - return [ - viewId, - markerId, - eventType.index, - position.encode(), - ]; - } - - static MarkerDragEventDto decode(Object result) { - result as List; - return MarkerDragEventDto( - viewId: result[0]! as int, - markerId: result[1]! as String, - eventType: MarkerDragEventTypeDto.values[result[2]! as int], - position: LatLngDto.decode(result[3]! as List), - ); - } -} - class PolygonDto { PolygonDto({ required this.polygonId, @@ -661,32 +649,6 @@ class PolygonHoleDto { } } -class PolygonClickedEventDto { - PolygonClickedEventDto({ - required this.viewId, - required this.polygonId, - }); - - int viewId; - - String polygonId; - - Object encode() { - return [ - viewId, - polygonId, - ]; - } - - static PolygonClickedEventDto decode(Object result) { - result as List; - return PolygonClickedEventDto( - viewId: result[0]! as int, - polygonId: result[1]! as String, - ); - } -} - class StyleSpanStrokeStyleDto { StyleSpanStrokeStyleDto({ this.solidColor, @@ -864,32 +826,6 @@ class PolylineOptionsDto { } } -class PolylineClickedEventDto { - PolylineClickedEventDto({ - required this.viewId, - required this.polylineId, - }); - - int viewId; - - String polylineId; - - Object encode() { - return [ - viewId, - polylineId, - ]; - } - - static PolylineClickedEventDto decode(Object result) { - result as List; - return PolylineClickedEventDto( - viewId: result[0]! as int, - polylineId: result[1]! as String, - ); - } -} - class CircleDto { CircleDto({ required this.circleId, @@ -979,54 +915,29 @@ class CircleOptionsDto { } } -class CircleClickedEventDto { - CircleClickedEventDto({ - required this.viewId, - required this.circleId, - }); - - int viewId; - - String circleId; - - Object encode() { - return [ - viewId, - circleId, - ]; - } - - static CircleClickedEventDto decode(Object result) { - result as List; - return CircleClickedEventDto( - viewId: result[0]! as int, - circleId: result[1]! as String, - ); - } -} - -class NavigationSessionEventDto { - NavigationSessionEventDto({ - required this.type, - required this.message, +class RouteTokenOptionsDto { + RouteTokenOptionsDto({ + required this.routeToken, + this.travelMode, }); - NavigationSessionEventTypeDto type; + String routeToken; - String message; + TravelModeDto? travelMode; Object encode() { return [ - type.index, - message, + routeToken, + travelMode?.index, ]; } - static NavigationSessionEventDto decode(Object result) { + static RouteTokenOptionsDto decode(Object result) { result as List; - return NavigationSessionEventDto( - type: NavigationSessionEventTypeDto.values[result[0]! as int], - message: result[1]! as String, + return RouteTokenOptionsDto( + routeToken: result[0]! as String, + travelMode: + result[1] != null ? TravelModeDto.values[result[1]! as int] : null, ); } } @@ -1036,6 +947,7 @@ class DestinationsDto { required this.waypoints, required this.displayOptions, this.routingOptions, + this.routeTokenOptions, }); List waypoints; @@ -1044,11 +956,14 @@ class DestinationsDto { RoutingOptionsDto? routingOptions; + RouteTokenOptionsDto? routeTokenOptions; + Object encode() { return [ waypoints, displayOptions.encode(), routingOptions?.encode(), + routeTokenOptions?.encode(), ]; } @@ -1061,6 +976,9 @@ class DestinationsDto { routingOptions: result[2] != null ? RoutingOptionsDto.decode(result[2]! as List) : null, + routeTokenOptions: result[3] != null + ? RouteTokenOptionsDto.decode(result[3]! as List) + : null, ); } } @@ -1358,160 +1276,6 @@ class SpeedingUpdatedEventDto { } } -class RoadSnappedLocationUpdatedEventDto { - RoadSnappedLocationUpdatedEventDto({ - required this.location, - }); - - LatLngDto location; - - Object encode() { - return [ - location.encode(), - ]; - } - - static RoadSnappedLocationUpdatedEventDto decode(Object result) { - result as List; - return RoadSnappedLocationUpdatedEventDto( - location: LatLngDto.decode(result[0]! as List), - ); - } -} - -class RoadSnappedRawLocationUpdatedEventDto { - RoadSnappedRawLocationUpdatedEventDto({ - this.location, - }); - - LatLngDto? location; - - Object encode() { - return [ - location?.encode(), - ]; - } - - static RoadSnappedRawLocationUpdatedEventDto decode(Object result) { - result as List; - return RoadSnappedRawLocationUpdatedEventDto( - location: result[0] != null - ? LatLngDto.decode(result[0]! as List) - : null, - ); - } -} - -class OnArrivalEventDto { - OnArrivalEventDto({ - required this.waypoint, - }); - - NavigationWaypointDto waypoint; - - Object encode() { - return [ - waypoint.encode(), - ]; - } - - static OnArrivalEventDto decode(Object result) { - result as List; - return OnArrivalEventDto( - waypoint: NavigationWaypointDto.decode(result[0]! as List), - ); - } -} - -class RemainingTimeOrDistanceChangedEventDto { - RemainingTimeOrDistanceChangedEventDto({ - required this.remainingTime, - required this.remainingDistance, - }); - - double remainingTime; - - double remainingDistance; - - Object encode() { - return [ - remainingTime, - remainingDistance, - ]; - } - - static RemainingTimeOrDistanceChangedEventDto decode(Object result) { - result as List; - return RemainingTimeOrDistanceChangedEventDto( - remainingTime: result[0]! as double, - remainingDistance: result[1]! as double, - ); - } -} - -class RouteChangedEventDto { - RouteChangedEventDto({ - required this.message, - }); - - String message; - - Object encode() { - return [ - message, - ]; - } - - static RouteChangedEventDto decode(Object result) { - result as List; - return RouteChangedEventDto( - message: result[0]! as String, - ); - } -} - -class ReroutingEventDto { - ReroutingEventDto({ - required this.message, - }); - - String message; - - Object encode() { - return [ - message, - ]; - } - - static ReroutingEventDto decode(Object result) { - result as List; - return ReroutingEventDto( - message: result[0]! as String, - ); - } -} - -class TrafficUpdatedEventDto { - TrafficUpdatedEventDto({ - required this.message, - }); - - String message; - - Object encode() { - return [ - message, - ]; - } - - static TrafficUpdatedEventDto decode(Object result) { - result as List; - return TrafficUpdatedEventDto( - message: result[0]! as String, - ); - } -} - class SpeedAlertOptionsDto { SpeedAlertOptionsDto({ required this.severityUpgradeDurationSeconds, @@ -1744,51 +1508,54 @@ class _NavigationViewApiCodec extends StandardMessageCodec { } else if (value is CircleOptionsDto) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is InfoWindowDto) { + } else if (value is ImageDescriptorDto) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is LatLngBoundsDto) { + } else if (value is InfoWindowDto) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is LatLngDto) { + } else if (value is LatLngBoundsDto) { buffer.putUint8(133); writeValue(buffer, value.encode()); } else if (value is LatLngDto) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is MarkerAnchorDto) { + } else if (value is LatLngDto) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is MarkerDto) { + } else if (value is MarkerAnchorDto) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is MarkerOptionsDto) { + } else if (value is MarkerDto) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PatternItemDto) { + } else if (value is MarkerOptionsDto) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PolygonDto) { + } else if (value is PatternItemDto) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PolygonHoleDto) { + } else if (value is PolygonDto) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is PolygonOptionsDto) { + } else if (value is PolygonHoleDto) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is PolylineDto) { + } else if (value is PolygonOptionsDto) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is PolylineOptionsDto) { + } else if (value is PolylineDto) { buffer.putUint8(143); writeValue(buffer, value.encode()); - } else if (value is StyleSpanDto) { + } else if (value is PolylineOptionsDto) { buffer.putUint8(144); writeValue(buffer, value.encode()); - } else if (value is StyleSpanStrokeStyleDto) { + } else if (value is StyleSpanDto) { buffer.putUint8(145); writeValue(buffer, value.encode()); + } else if (value is StyleSpanStrokeStyleDto) { + buffer.putUint8(146); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1804,34 +1571,36 @@ class _NavigationViewApiCodec extends StandardMessageCodec { case 130: return CircleOptionsDto.decode(readValue(buffer)!); case 131: - return InfoWindowDto.decode(readValue(buffer)!); + return ImageDescriptorDto.decode(readValue(buffer)!); case 132: - return LatLngBoundsDto.decode(readValue(buffer)!); + return InfoWindowDto.decode(readValue(buffer)!); case 133: - return LatLngDto.decode(readValue(buffer)!); + return LatLngBoundsDto.decode(readValue(buffer)!); case 134: return LatLngDto.decode(readValue(buffer)!); case 135: - return MarkerAnchorDto.decode(readValue(buffer)!); + return LatLngDto.decode(readValue(buffer)!); case 136: - return MarkerDto.decode(readValue(buffer)!); + return MarkerAnchorDto.decode(readValue(buffer)!); case 137: - return MarkerOptionsDto.decode(readValue(buffer)!); + return MarkerDto.decode(readValue(buffer)!); case 138: - return PatternItemDto.decode(readValue(buffer)!); + return MarkerOptionsDto.decode(readValue(buffer)!); case 139: - return PolygonDto.decode(readValue(buffer)!); + return PatternItemDto.decode(readValue(buffer)!); case 140: - return PolygonHoleDto.decode(readValue(buffer)!); + return PolygonDto.decode(readValue(buffer)!); case 141: - return PolygonOptionsDto.decode(readValue(buffer)!); + return PolygonHoleDto.decode(readValue(buffer)!); case 142: - return PolylineDto.decode(readValue(buffer)!); + return PolygonOptionsDto.decode(readValue(buffer)!); case 143: - return PolylineOptionsDto.decode(readValue(buffer)!); + return PolylineDto.decode(readValue(buffer)!); case 144: - return StyleSpanDto.decode(readValue(buffer)!); + return PolylineOptionsDto.decode(readValue(buffer)!); case 145: + return StyleSpanDto.decode(readValue(buffer)!); + case 146: return StyleSpanStrokeStyleDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -1903,9 +1672,9 @@ class NavigationViewApi { } } - Future enableMyLocation(int viewId, bool enabled) async { + Future setMyLocationEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocation'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2057,9 +1826,10 @@ class NavigationViewApi { } } - Future enableNavigationTripProgressBar(int viewId, bool enabled) async { + Future setNavigationTripProgressBarEnabled( + int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationTripProgressBar'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationTripProgressBarEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2110,9 +1880,9 @@ class NavigationViewApi { } } - Future enableNavigationHeader(int viewId, bool enabled) async { + Future setNavigationHeaderEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationHeader'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationHeaderEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2163,9 +1933,9 @@ class NavigationViewApi { } } - Future enableNavigationFooter(int viewId, bool enabled) async { + Future setNavigationFooterEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationFooter'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationFooterEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2216,9 +1986,9 @@ class NavigationViewApi { } } - Future enableRecenterButton(int viewId, bool enabled) async { + Future setRecenterButtonEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRecenterButton'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRecenterButtonEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2269,9 +2039,9 @@ class NavigationViewApi { } } - Future enableSpeedLimitIcon(int viewId, bool enabled) async { + Future setSpeedLimitIconEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedLimitIcon'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedLimitIconEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2322,9 +2092,9 @@ class NavigationViewApi { } } - Future enableSpeedometer(int viewId, bool enabled) async { + Future setSpeedometerEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedometer'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedometerEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2346,9 +2116,9 @@ class NavigationViewApi { } } - Future isIncidentCardsEnabled(int viewId) async { + Future isTrafficIncidentCardsEnabled(int viewId) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isIncidentCardsEnabled'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isTrafficIncidentCardsEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2375,9 +2145,9 @@ class NavigationViewApi { } } - Future enableIncidentCards(int viewId, bool enabled) async { + Future setTrafficIncidentCardsEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableIncidentCards'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficIncidentCardsEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2428,9 +2198,9 @@ class NavigationViewApi { } } - Future enableNavigationUI(int viewId, bool enabled) async { + Future setNavigationUIEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationUI'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationUIEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2945,9 +2715,166 @@ class NavigationViewApi { } } - Future enableMyLocationButton(int viewId, bool enabled) async { + Future getMinZoomPreference(int viewId) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMinZoomPreference'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([viewId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as double?)!; + } + } + + Future getMaxZoomPreference(int viewId) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMaxZoomPreference'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([viewId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as double?)!; + } + } + + Future resetMinMaxZoomPreference(int viewId) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.resetMinMaxZoomPreference'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([viewId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future setMinZoomPreference( + int viewId, double minZoomPreference) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMinZoomPreference'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([viewId, minZoomPreference]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future setMaxZoomPreference( + int viewId, double maxZoomPreference) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMaxZoomPreference'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([viewId, maxZoomPreference]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future setMyLocationButtonEnabled(int viewId, bool enabled) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationButtonEnabled'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([viewId, enabled]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future setConsumeMyLocationButtonClickEventsEnabled( + int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocationButton'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setConsumeMyLocationButtonClickEventsEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2969,9 +2896,9 @@ class NavigationViewApi { } } - Future enableZoomGestures(int viewId, bool enabled) async { + Future setZoomGesturesEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomGestures'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomGesturesEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -2993,9 +2920,9 @@ class NavigationViewApi { } } - Future enableZoomControls(int viewId, bool enabled) async { + Future setZoomControlsEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomControls'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomControlsEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3017,9 +2944,9 @@ class NavigationViewApi { } } - Future enableCompass(int viewId, bool enabled) async { + Future setCompassEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableCompass'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setCompassEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3041,9 +2968,9 @@ class NavigationViewApi { } } - Future enableRotateGestures(int viewId, bool enabled) async { + Future setRotateGesturesEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRotateGestures'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRotateGesturesEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3065,9 +2992,9 @@ class NavigationViewApi { } } - Future enableScrollGestures(int viewId, bool enabled) async { + Future setScrollGesturesEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGestures'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3089,10 +3016,10 @@ class NavigationViewApi { } } - Future enableScrollGesturesDuringRotateOrZoom( + Future setScrollGesturesDuringRotateOrZoomEnabled( int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGesturesDuringRotateOrZoom'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesDuringRotateOrZoomEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3114,9 +3041,9 @@ class NavigationViewApi { } } - Future enableTiltGestures(int viewId, bool enabled) async { + Future setTiltGesturesEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTiltGestures'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTiltGesturesEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3138,9 +3065,9 @@ class NavigationViewApi { } } - Future enableMapToolbar(int viewId, bool enabled) async { + Future setMapToolbarEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMapToolbar'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMapToolbarEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3162,9 +3089,9 @@ class NavigationViewApi { } } - Future enableTraffic(int viewId, bool enabled) async { + Future setTrafficEnabled(int viewId, bool enabled) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTraffic'; + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficEnabled'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -3215,6 +3142,35 @@ class NavigationViewApi { } } + Future isConsumeMyLocationButtonClickEventsEnabled(int viewId) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isConsumeMyLocationButtonClickEventsEnabled'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([viewId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } + Future isZoomGesturesEnabled(int viewId) async { const String __pigeon_channelName = 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isZoomGesturesEnabled'; @@ -4047,30 +4003,42 @@ class NavigationViewApi { return; } } + + Future registerOnCameraChangedListener(int viewId) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.registerOnCameraChangedListener'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([viewId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } } -class _NavigationViewEventApiCodec extends StandardMessageCodec { - const _NavigationViewEventApiCodec(); +class _ImageRegistryApiCodec extends StandardMessageCodec { + const _ImageRegistryApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is CircleClickedEventDto) { + if (value is ImageDescriptorDto) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is LatLngDto) { + } else if (value is ImageDescriptorDto) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MarkerDragEventDto) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is MarkerEventDto) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is PolygonClickedEventDto) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is PolylineClickedEventDto) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -4080,42 +4048,197 @@ class _NavigationViewEventApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return CircleClickedEventDto.decode(readValue(buffer)!); + return ImageDescriptorDto.decode(readValue(buffer)!); case 129: - return LatLngDto.decode(readValue(buffer)!); - case 130: - return MarkerDragEventDto.decode(readValue(buffer)!); - case 131: - return MarkerEventDto.decode(readValue(buffer)!); - case 132: - return PolygonClickedEventDto.decode(readValue(buffer)!); - case 133: - return PolylineClickedEventDto.decode(readValue(buffer)!); + return ImageDescriptorDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -abstract class NavigationViewEventApi { - static const MessageCodec pigeonChannelCodec = - _NavigationViewEventApiCodec(); - - void onMapClickEvent(int viewId, LatLngDto latLng); - - void onMapLongClickEvent(int viewId, LatLngDto latLng); - - void onRecenterButtonClicked(int viewId); - - void onMarkerEvent(MarkerEventDto msg); - - void onMarkerDragEvent(MarkerDragEventDto msg); +class ImageRegistryApi { + /// Constructor for [ImageRegistryApi]. 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. + ImageRegistryApi({BinaryMessenger? binaryMessenger}) + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; - void onPolygonClicked(PolygonClickedEventDto msg); + static const MessageCodec pigeonChannelCodec = + _ImageRegistryApiCodec(); + + Future registerBitmapImage( + String imageId, + Uint8List bytes, + double imagePixelRatio, + double? width, + double? height) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([imageId, bytes, imagePixelRatio, width, height]) + as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as ImageDescriptorDto?)!; + } + } - void onPolylineClicked(PolylineClickedEventDto msg); + Future unregisterImage(ImageDescriptorDto imageDescriptor) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.unregisterImage'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([imageDescriptor]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future> getRegisteredImages() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.getRegisteredImages'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)! + .cast(); + } + } + + Future clearRegisteredImages() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.clearRegisteredImages'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } +} + +class _NavigationViewEventApiCodec extends StandardMessageCodec { + const _NavigationViewEventApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is CameraPositionDto) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is LatLngDto) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return CameraPositionDto.decode(readValue(buffer)!); + case 129: + return LatLngDto.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class NavigationViewEventApi { + static const MessageCodec pigeonChannelCodec = + _NavigationViewEventApiCodec(); + + void onMapClickEvent(int viewId, LatLngDto latLng); + + void onMapLongClickEvent(int viewId, LatLngDto latLng); + + void onRecenterButtonClicked(int viewId); + + void onMarkerEvent(int viewId, String markerId, MarkerEventTypeDto eventType); + + void onMarkerDragEvent(int viewId, String markerId, + MarkerDragEventTypeDto eventType, LatLngDto position); + + void onPolygonClicked(int viewId, String polygonId); + + void onPolylineClicked(int viewId, String polylineId); - void onCircleClicked(CircleClickedEventDto msg); + void onCircleClicked(int viewId, String circleId); + + void onNavigationUIEnabledChanged(int viewId, bool navigationUIEnabled); + + void onMyLocationClicked(int viewId); + + void onMyLocationButtonClicked(int viewId); + + void onCameraChanged( + int viewId, CameraEventTypeDto eventType, CameraPositionDto position); static void setup(NavigationViewEventApi? api, {BinaryMessenger? binaryMessenger}) { @@ -4222,11 +4345,19 @@ abstract class NavigationViewEventApi { assert(message != null, 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerEvent was null.'); final List args = (message as List?)!; - final MarkerEventDto? arg_msg = (args[0] as MarkerEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerEvent was null, expected non-null MarkerEventDto.'); + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerEvent was null, expected non-null int.'); + final String? arg_markerId = (args[1] as String?); + assert(arg_markerId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerEvent was null, expected non-null String.'); + final MarkerEventTypeDto? arg_eventType = args[2] == null + ? null + : MarkerEventTypeDto.values[args[2]! as int]; + assert(arg_eventType != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerEvent was null, expected non-null MarkerEventTypeDto.'); try { - api.onMarkerEvent(arg_msg!); + api.onMarkerEvent(arg_viewId!, arg_markerId!, arg_eventType!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -4250,11 +4381,23 @@ abstract class NavigationViewEventApi { assert(message != null, 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent was null.'); final List args = (message as List?)!; - final MarkerDragEventDto? arg_msg = (args[0] as MarkerDragEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent was null, expected non-null MarkerDragEventDto.'); + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent was null, expected non-null int.'); + final String? arg_markerId = (args[1] as String?); + assert(arg_markerId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent was null, expected non-null String.'); + final MarkerDragEventTypeDto? arg_eventType = args[2] == null + ? null + : MarkerDragEventTypeDto.values[args[2]! as int]; + assert(arg_eventType != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent was null, expected non-null MarkerDragEventTypeDto.'); + final LatLngDto? arg_position = (args[3] as LatLngDto?); + assert(arg_position != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMarkerDragEvent was null, expected non-null LatLngDto.'); try { - api.onMarkerDragEvent(arg_msg!); + api.onMarkerDragEvent( + arg_viewId!, arg_markerId!, arg_eventType!, arg_position!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -4278,12 +4421,14 @@ abstract class NavigationViewEventApi { assert(message != null, 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolygonClicked was null.'); final List args = (message as List?)!; - final PolygonClickedEventDto? arg_msg = - (args[0] as PolygonClickedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolygonClicked was null, expected non-null PolygonClickedEventDto.'); + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolygonClicked was null, expected non-null int.'); + final String? arg_polygonId = (args[1] as String?); + assert(arg_polygonId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolygonClicked was null, expected non-null String.'); try { - api.onPolygonClicked(arg_msg!); + api.onPolygonClicked(arg_viewId!, arg_polygonId!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -4307,12 +4452,14 @@ abstract class NavigationViewEventApi { assert(message != null, 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolylineClicked was null.'); final List args = (message as List?)!; - final PolylineClickedEventDto? arg_msg = - (args[0] as PolylineClickedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolylineClicked was null, expected non-null PolylineClickedEventDto.'); + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolylineClicked was null, expected non-null int.'); + final String? arg_polylineId = (args[1] as String?); + assert(arg_polylineId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onPolylineClicked was null, expected non-null String.'); try { - api.onPolylineClicked(arg_msg!); + api.onPolylineClicked(arg_viewId!, arg_polylineId!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -4336,12 +4483,139 @@ abstract class NavigationViewEventApi { assert(message != null, 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCircleClicked was null.'); final List args = (message as List?)!; - final CircleClickedEventDto? arg_msg = - (args[0] as CircleClickedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCircleClicked was null, expected non-null CircleClickedEventDto.'); + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCircleClicked was null, expected non-null int.'); + final String? arg_circleId = (args[1] as String?); + assert(arg_circleId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCircleClicked was null, expected non-null String.'); + try { + api.onCircleClicked(arg_viewId!, arg_circleId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onNavigationUIEnabledChanged', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onNavigationUIEnabledChanged was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onNavigationUIEnabledChanged was null, expected non-null int.'); + final bool? arg_navigationUIEnabled = (args[1] as bool?); + assert(arg_navigationUIEnabled != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onNavigationUIEnabledChanged was null, expected non-null bool.'); + try { + api.onNavigationUIEnabledChanged( + arg_viewId!, arg_navigationUIEnabled!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationClicked', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationClicked was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationClicked was null, expected non-null int.'); try { - api.onCircleClicked(arg_msg!); + api.onMyLocationClicked(arg_viewId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationButtonClicked', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationButtonClicked was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onMyLocationButtonClicked was null, expected non-null int.'); + try { + api.onMyLocationButtonClicked(arg_viewId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCameraChanged', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCameraChanged was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCameraChanged was null, expected non-null int.'); + final CameraEventTypeDto? arg_eventType = args[1] == null + ? null + : CameraEventTypeDto.values[args[1]! as int]; + assert(arg_eventType != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCameraChanged was null, expected non-null CameraEventTypeDto.'); + final CameraPositionDto? arg_position = + (args[2] as CameraPositionDto?); + assert(arg_position != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewEventApi.onCameraChanged was null, expected non-null CameraPositionDto.'); + try { + api.onCameraChanged(arg_viewId!, arg_eventType!, arg_position!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -4395,15 +4669,18 @@ class _NavigationSessionApiCodec extends StandardMessageCodec { } else if (value is RouteSegmentTrafficDataRoadStretchRenderingDataDto) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is RoutingOptionsDto) { + } else if (value is RouteTokenOptionsDto) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is SimulationOptionsDto) { + } else if (value is RoutingOptionsDto) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is SpeedAlertOptionsDto) { + } else if (value is SimulationOptionsDto) { buffer.putUint8(142); writeValue(buffer, value.encode()); + } else if (value is SpeedAlertOptionsDto) { + buffer.putUint8(143); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -4438,10 +4715,12 @@ class _NavigationSessionApiCodec extends StandardMessageCodec { return RouteSegmentTrafficDataRoadStretchRenderingDataDto.decode( readValue(buffer)!); case 140: - return RoutingOptionsDto.decode(readValue(buffer)!); + return RouteTokenOptionsDto.decode(readValue(buffer)!); case 141: - return SimulationOptionsDto.decode(readValue(buffer)!); + return RoutingOptionsDto.decode(readValue(buffer)!); case 142: + return SimulationOptionsDto.decode(readValue(buffer)!); + case 143: return SpeedAlertOptionsDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -4461,7 +4740,8 @@ class NavigationSessionApi { _NavigationSessionApiCodec(); /// General. - Future createNavigationSession() async { + Future createNavigationSession( + bool abnormalTerminationReportingEnabled) async { const String __pigeon_channelName = 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.createNavigationSession'; final BasicMessageChannel __pigeon_channel = @@ -4470,8 +4750,8 @@ class NavigationSessionApi { pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; + final List? __pigeon_replyList = await __pigeon_channel + .send([abnormalTerminationReportingEnabled]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -4625,6 +4905,35 @@ class NavigationSessionApi { } } + Future getNavSDKVersion() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.getNavSDKVersion'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + /// Navigation. Future isGuidanceRunning() async { const String __pigeon_channelName = @@ -4703,7 +5012,7 @@ class NavigationSessionApi { } } - Future setDestinations(DestinationsDto msg) async { + Future setDestinations(DestinationsDto destinations) async { const String __pigeon_channelName = 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.setDestinations'; final BasicMessageChannel __pigeon_channel = @@ -4713,7 +5022,7 @@ class NavigationSessionApi { binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([msg]) as List?; + await __pigeon_channel.send([destinations]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -5293,38 +5602,11 @@ class _NavigationSessionEventApiCodec extends StandardMessageCodec { if (value is LatLngDto) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is LatLngDto) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is NavigationSessionEventDto) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); } else if (value is NavigationWaypointDto) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is OnArrivalEventDto) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is RemainingTimeOrDistanceChangedEventDto) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else if (value is ReroutingEventDto) { - buffer.putUint8(134); - writeValue(buffer, value.encode()); - } else if (value is RoadSnappedLocationUpdatedEventDto) { - buffer.putUint8(135); - writeValue(buffer, value.encode()); - } else if (value is RoadSnappedRawLocationUpdatedEventDto) { - buffer.putUint8(136); - writeValue(buffer, value.encode()); - } else if (value is RouteChangedEventDto) { - buffer.putUint8(137); + buffer.putUint8(129); writeValue(buffer, value.encode()); } else if (value is SpeedingUpdatedEventDto) { - buffer.putUint8(138); - writeValue(buffer, value.encode()); - } else if (value is TrafficUpdatedEventDto) { - buffer.putUint8(139); + buffer.putUint8(130); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -5337,28 +5619,9 @@ class _NavigationSessionEventApiCodec extends StandardMessageCodec { case 128: return LatLngDto.decode(readValue(buffer)!); case 129: - return LatLngDto.decode(readValue(buffer)!); - case 130: - return NavigationSessionEventDto.decode(readValue(buffer)!); - case 131: return NavigationWaypointDto.decode(readValue(buffer)!); - case 132: - return OnArrivalEventDto.decode(readValue(buffer)!); - case 133: - return RemainingTimeOrDistanceChangedEventDto.decode( - readValue(buffer)!); - case 134: - return ReroutingEventDto.decode(readValue(buffer)!); - case 135: - return RoadSnappedLocationUpdatedEventDto.decode(readValue(buffer)!); - case 136: - return RoadSnappedRawLocationUpdatedEventDto.decode(readValue(buffer)!); - case 137: - return RouteChangedEventDto.decode(readValue(buffer)!); - case 138: + case 130: return SpeedingUpdatedEventDto.decode(readValue(buffer)!); - case 139: - return TrafficUpdatedEventDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -5369,34 +5632,34 @@ abstract class NavigationSessionEventApi { static const MessageCodec pigeonChannelCodec = _NavigationSessionEventApiCodec(); - void onNavigationSessionEvent(NavigationSessionEventDto msg); - void onSpeedingUpdated(SpeedingUpdatedEventDto msg); - void onRoadSnappedLocationUpdated(RoadSnappedLocationUpdatedEventDto msg); + void onRoadSnappedLocationUpdated(LatLngDto location); - void onRoadSnappedRawLocationUpdated( - RoadSnappedRawLocationUpdatedEventDto msg); + void onRoadSnappedRawLocationUpdated(LatLngDto location); - void onArrival(OnArrivalEventDto msg); + void onArrival(NavigationWaypointDto waypoint); - void onRouteChanged(RouteChangedEventDto msg); + void onRouteChanged(); void onRemainingTimeOrDistanceChanged( - RemainingTimeOrDistanceChangedEventDto msg); + double remainingTime, double remainingDistance); - /// Android only event. - void onTrafficUpdated(TrafficUpdatedEventDto msg); + /// Android-only event. + void onTrafficUpdated(); - /// Android only event. - void onRerouting(ReroutingEventDto msg); + /// Android-only event. + void onRerouting(); + + /// Android-only event. + void onGpsAvailabilityUpdate(bool available); static void setup(NavigationSessionEventApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onNavigationSessionEvent', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -5404,14 +5667,14 @@ abstract class NavigationSessionEventApi { } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onNavigationSessionEvent was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated was null.'); final List args = (message as List?)!; - final NavigationSessionEventDto? arg_msg = - (args[0] as NavigationSessionEventDto?); + final SpeedingUpdatedEventDto? arg_msg = + (args[0] as SpeedingUpdatedEventDto?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onNavigationSessionEvent was null, expected non-null NavigationSessionEventDto.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated was null, expected non-null SpeedingUpdatedEventDto.'); try { - api.onNavigationSessionEvent(arg_msg!); + api.onSpeedingUpdated(arg_msg!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5425,7 +5688,7 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -5433,14 +5696,13 @@ abstract class NavigationSessionEventApi { } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated was null.'); final List args = (message as List?)!; - final SpeedingUpdatedEventDto? arg_msg = - (args[0] as SpeedingUpdatedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onSpeedingUpdated was null, expected non-null SpeedingUpdatedEventDto.'); + final LatLngDto? arg_location = (args[0] as LatLngDto?); + assert(arg_location != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated was null, expected non-null LatLngDto.'); try { - api.onSpeedingUpdated(arg_msg!); + api.onRoadSnappedLocationUpdated(arg_location!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5454,7 +5716,7 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -5462,14 +5724,13 @@ abstract class NavigationSessionEventApi { } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated was null.'); final List args = (message as List?)!; - final RoadSnappedLocationUpdatedEventDto? arg_msg = - (args[0] as RoadSnappedLocationUpdatedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedLocationUpdated was null, expected non-null RoadSnappedLocationUpdatedEventDto.'); + final LatLngDto? arg_location = (args[0] as LatLngDto?); + assert(arg_location != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated was null, expected non-null LatLngDto.'); try { - api.onRoadSnappedLocationUpdated(arg_msg!); + api.onRoadSnappedRawLocationUpdated(arg_location!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5483,7 +5744,7 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -5491,14 +5752,14 @@ abstract class NavigationSessionEventApi { } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival was null.'); final List args = (message as List?)!; - final RoadSnappedRawLocationUpdatedEventDto? arg_msg = - (args[0] as RoadSnappedRawLocationUpdatedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRoadSnappedRawLocationUpdated was null, expected non-null RoadSnappedRawLocationUpdatedEventDto.'); + final NavigationWaypointDto? arg_waypoint = + (args[0] as NavigationWaypointDto?); + assert(arg_waypoint != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival was null, expected non-null NavigationWaypointDto.'); try { - api.onRoadSnappedRawLocationUpdated(arg_msg!); + api.onArrival(arg_waypoint!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5512,21 +5773,15 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival was null.'); - final List args = (message as List?)!; - final OnArrivalEventDto? arg_msg = (args[0] as OnArrivalEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onArrival was null, expected non-null OnArrivalEventDto.'); try { - api.onArrival(arg_msg!); + api.onRouteChanged(); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5540,7 +5795,7 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -5548,14 +5803,17 @@ abstract class NavigationSessionEventApi { } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged was null.'); final List args = (message as List?)!; - final RouteChangedEventDto? arg_msg = - (args[0] as RouteChangedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRouteChanged was null, expected non-null RouteChangedEventDto.'); + final double? arg_remainingTime = (args[0] as double?); + assert(arg_remainingTime != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged was null, expected non-null double.'); + final double? arg_remainingDistance = (args[1] as double?); + assert(arg_remainingDistance != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged was null, expected non-null double.'); try { - api.onRouteChanged(arg_msg!); + api.onRemainingTimeOrDistanceChanged( + arg_remainingTime!, arg_remainingDistance!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5569,22 +5827,15 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged was null.'); - final List args = (message as List?)!; - final RemainingTimeOrDistanceChangedEventDto? arg_msg = - (args[0] as RemainingTimeOrDistanceChangedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRemainingTimeOrDistanceChanged was null, expected non-null RemainingTimeOrDistanceChangedEventDto.'); try { - api.onRemainingTimeOrDistanceChanged(arg_msg!); + api.onTrafficUpdated(); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5598,22 +5849,15 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated was null.'); - final List args = (message as List?)!; - final TrafficUpdatedEventDto? arg_msg = - (args[0] as TrafficUpdatedEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onTrafficUpdated was null, expected non-null TrafficUpdatedEventDto.'); try { - api.onTrafficUpdated(arg_msg!); + api.onRerouting(); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -5627,7 +5871,7 @@ abstract class NavigationSessionEventApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting', + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onGpsAvailabilityUpdate', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -5635,13 +5879,13 @@ abstract class NavigationSessionEventApi { } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onGpsAvailabilityUpdate was null.'); final List args = (message as List?)!; - final ReroutingEventDto? arg_msg = (args[0] as ReroutingEventDto?); - assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onRerouting was null, expected non-null ReroutingEventDto.'); + final bool? arg_available = (args[0] as bool?); + assert(arg_available != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionEventApi.onGpsAvailabilityUpdate was null, expected non-null bool.'); try { - api.onRerouting(arg_msg!); + api.onGpsAvailabilityUpdate(arg_available!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/lib/src/types/zoom_constants.dart b/lib/src/method_channel/method_channel.dart similarity index 71% rename from lib/src/types/zoom_constants.dart rename to lib/src/method_channel/method_channel.dart index 092d330..d3ddeca 100644 --- a/lib/src/types/zoom_constants.dart +++ b/lib/src/method_channel/method_channel.dart @@ -12,10 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// The minimum zoom level for Google Maps. -/// {@category Navigation View} -const double googleMapsMinZoomLevel = 0; - -/// The maximum zoom level for Google Maps. -/// {@category Navigation View} -const double googleMapsMaxZoomLevel = 21; +export 'common_image_api.dart'; +export 'common_session_api.dart'; +export 'common_view_api.dart'; +export 'convert/convert.dart'; +export 'messages.g.dart'; diff --git a/lib/src/navigator/google_maps_navigation_simulator.dart b/lib/src/navigator/google_maps_navigation_simulator.dart index 8986128..27a862c 100644 --- a/lib/src/navigator/google_maps_navigation_simulator.dart +++ b/lib/src/navigator/google_maps_navigation_simulator.dart @@ -14,7 +14,6 @@ import '../../google_maps_navigation.dart'; import '../google_maps_navigation_platform_interface.dart'; -// ignore_for_file: avoid_classes_with_only_static_members /// Simulator handles actions of the navigation simulation /// {@category Navigation} diff --git a/lib/src/navigator/google_maps_navigator.dart b/lib/src/navigator/google_maps_navigator.dart index be7c903..d6bd610 100644 --- a/lib/src/navigator/google_maps_navigator.dart +++ b/lib/src/navigator/google_maps_navigator.dart @@ -16,7 +16,6 @@ import 'dart:async'; import '../../google_maps_navigation.dart'; import '../google_maps_navigation_platform_interface.dart'; -// ignore_for_file: avoid_classes_with_only_static_members import 'google_maps_navigation_simulator.dart'; @@ -42,8 +41,26 @@ class GoogleMapsNavigator { /// ([GoogleMapsNavigator.setDestinations], [GoogleMapsNavigator.startGuidance], etc.) /// throw [SessionNotInitializedException]. /// - static Future initializeNavigationSession() async { - await GoogleMapsNavigationPlatform.instance.createNavigationSession(); + /// Checks if there are active listeners for either road-snapped location + /// updates or raw location updates. If there are, this method re-enables the + /// emission of road-snapped location updates. This is necessary because the + /// native (Android/iOS) implementation clears the listeners during cleanup. + /// + /// Optional parameter [abnormalTerminationReportingEnabled] can be used enables/disables + /// reporting abnormal SDK terminations such as the app crashes while the SDK is still running. + /// + static Future initializeNavigationSession( + {bool abnormalTerminationReportingEnabled = true}) async { + await GoogleMapsNavigationPlatform.instance + .createNavigationSession(abnormalTerminationReportingEnabled); + + // Enable road-snapped location updates if there are subscriptions to them. + if ((_roadSnappedLocationUpdatedController?.hasListener ?? false) || + (_roadSnappedRawLocationUpdatedController?.hasListener ?? false) || + (_gpsAvailabilityUpdatedController?.hasListener ?? false)) { + await GoogleMapsNavigationPlatform.instance + .enableRoadSnappedLocationUpdates(); + } } /// Check whether navigator has been initialized. @@ -56,67 +73,185 @@ class GoogleMapsNavigator { return GoogleMapsNavigationPlatform.instance.isInitialized(); } - /// Sets the event channel listener for navigation session event. - static StreamSubscription - setNavigationSessionEventListener( - OnNavigationSessionEventCallback onNavigationSessionEvent) { - return GoogleMapsNavigationPlatform.instance - .getNavigationSessionEventStream() - .listen(onNavigationSessionEvent); - } - - /// Sets the event channel listener for the speeding updated event. + /// Sets the event channel listener for the [SpeedingUpdatedEvent]s. + /// + /// Returns a [StreamSubscription] for [SpeedingUpdatedEvent]s. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setSpeedingUpdatedListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` static StreamSubscription setSpeedingUpdatedListener( - OnSpeedingUpdatedEventCallback onSpeedingUpdatedEvent, + OnSpeedingUpdatedEventCallback listener, ) { return GoogleMapsNavigationPlatform.instance .getNavigationSpeedingEventStream() - .listen(onSpeedingUpdatedEvent); + .listen(listener); } - /// Sets the event channel listener for the on arrival event. + /// Sets the event channel listener for the [OnArrivalEvent]s. + /// + /// Returns a [StreamSubscription] for [OnArrivalEvent]s. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setOnArrivalListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` static StreamSubscription setOnArrivalListener( - OnArrivalEventCallback onArrivalEvent) { + OnArrivalEventCallback listener) { return GoogleMapsNavigationPlatform.instance .getNavigationOnArrivalEventStream() - .listen(onArrivalEvent); + .listen(listener); } - /// Sets the event channel listener for the rerouting event. (Android only) + /// Sets the event channel listener for the rerouting events. (Android only) + /// + /// Returns a [StreamSubscription] for rerouting events. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setOnReroutingListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` static StreamSubscription setOnReroutingListener( - OnReroutingEventCallback onReroutingEvent) { + OnReroutingEventCallback listener) { return GoogleMapsNavigationPlatform.instance .getNavigationOnReroutingEventStream() .listen((void event) { - onReroutingEvent.call(); + listener.call(); }); } - /// Sets the event channel listener for the traffic updated event. (Android only) + /// Sets the event channel listener for the GPS availability events. + /// (Android only). + /// + /// Setting this listener will also register road snapped location listener + /// on native side. + /// + /// DISCLAIMER: This is an EXPERIMENTAL API and its behaviors may be subject + /// to removal or breaking changes in future releases. + /// + /// Returns a [StreamSubscription] for GPS availability events. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setOnGpsAvailabilityListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` + static Future> + setOnGpsAvailabilityListener( + OnGpsAvailabilityEventCallback listener) async { + if (_gpsAvailabilityUpdatedController == null) { + _gpsAvailabilityUpdatedController = + StreamController.broadcast(onCancel: () { + _disableRoadSnappedLocationUpdatesIfNoActiveListeners(); + }, onListen: () { + GoogleMapsNavigationPlatform.instance + .enableRoadSnappedLocationUpdates(); + }); + unawaited(_gpsAvailabilityUpdatedController!.addStream( + GoogleMapsNavigationPlatform.instance + .getNavigationOnGpsAvailabilityUpdateEventStream())); + } + + return _gpsAvailabilityUpdatedController!.stream.listen(listener); + } + + static StreamController? + _gpsAvailabilityUpdatedController; + + /// Sets the event channel listener for the traffic updated events. (Android only) + /// + /// Returns a [StreamSubscription] for traffic updated events. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setTrafficUpdatedListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` static StreamSubscription setTrafficUpdatedListener( - OnTrafficUpdatedEventCallback onTrafficUpdatedEvent) { + OnTrafficUpdatedEventCallback listener) { return GoogleMapsNavigationPlatform.instance .getNavigationTrafficUpdatedEventStream() .listen((void event) { - onTrafficUpdatedEvent.call(); + listener.call(); }); } - /// Sets the event channel listener for the on route changed event. + /// Sets the event channel listener for the on route changed events. + /// + /// Returns a [StreamSubscription] for route changed events. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setOnRouteChangedListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` static StreamSubscription setOnRouteChangedListener( - OnRouteChangedEventCallback onRouteChangedEvent) { + OnRouteChangedEventCallback listener) { return GoogleMapsNavigationPlatform.instance .getNavigationOnRouteChangedEventStream() .listen((void event) { - onRouteChangedEvent.call(); + listener.call(); }); } - /// Sets the event channel listener for the on remaining time or distance changed event. + /// Sets the event channel listener for [RemainingTimeOrDistanceChangedEvent]s. + /// + /// Returns a [StreamSubscription] for handling [RemainingTimeOrDistanceChangedEvent]s. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setOnRemainingTimeOrDistanceChangedListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` static StreamSubscription setOnRemainingTimeOrDistanceChangedListener( - OnRemainingTimeOrDistanceChangedEventCallback - onRemainingTimeOrDistanceChangedEvent, + OnRemainingTimeOrDistanceChangedEventCallback listener, // iOS default value. {int remainingTimeThresholdSeconds = 1, // iOS default value. @@ -128,26 +263,37 @@ class GoogleMapsNavigator { remainingTimeThresholdSeconds, remainingDistanceThresholdMeters); return GoogleMapsNavigationPlatform.instance .getNavigationRemainingTimeOrDistanceChangedEventStream() - .listen(onRemainingTimeOrDistanceChangedEvent); + .listen(listener); } static StreamController? _roadSnappedLocationUpdatedController; - /// Enable road snapped location updates and set the event channel listener - /// for the road snapped location updated event. + /// Sets the event channel listener for [RoadSnappedLocationUpdatedEvent]s. + /// + /// Returns a Future for [StreamSubscription] for handling + /// [RoadSnappedLocationUpdatedEvent]s. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setRoadSnappedLocationUpdatedListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` static Future> setRoadSnappedLocationUpdatedListener( - OnRoadSnappedLocationUpdatedEventCallback onRoadSnappedLocationUpdatedEvent, + OnRoadSnappedLocationUpdatedEventCallback listener, ) async { if (_roadSnappedLocationUpdatedController == null) { _roadSnappedLocationUpdatedController = StreamController.broadcast( onCancel: () { - if (!_roadSnappedLocationUpdatedController!.hasListener) { - GoogleMapsNavigationPlatform.instance - .disableRoadSnappedLocationUpdates(); - } + _disableRoadSnappedLocationUpdatesIfNoActiveListeners(); }, onListen: () { GoogleMapsNavigationPlatform.instance @@ -159,14 +305,65 @@ class GoogleMapsNavigator { .getNavigationRoadSnappedLocationEventStream())); } - return _roadSnappedLocationUpdatedController!.stream - .listen(onRoadSnappedLocationUpdatedEvent); + return _roadSnappedLocationUpdatedController!.stream.listen(listener); + } + + static StreamController? + _roadSnappedRawLocationUpdatedController; + + /// Sets the event channel listener for [RoadSnappedRawLocationUpdatedEvent]s + /// (Android only). + /// + /// Returns a Future for [StreamSubscription] for handling + /// [RoadSnappedRawLocationUpdatedEvent]s. + /// This subscription must be canceled using `cancel()` when it is no longer + /// needed to stop receiving events and allow the stream to perform necessary + /// cleanup, such as releasing resources or shutting down event sources. The + /// cleanup is asynchronous, and the `cancel()` method returns a Future that + /// completes once the cleanup is done. + /// + /// Example usage: + /// ```dart + /// final subscription = setRoadSnappedRawLocationUpdatedListener(yourEventHandler); + /// // When done with the subscription + /// await subscription.cancel(); + /// ``` + static Future> + setRoadSnappedRawLocationUpdatedListener( + OnRoadSnappedRawLocationUpdatedEventCallback listener, + ) async { + if (_roadSnappedRawLocationUpdatedController == null) { + _roadSnappedRawLocationUpdatedController = + StreamController.broadcast( + onCancel: () { + _disableRoadSnappedLocationUpdatesIfNoActiveListeners(); + }, + onListen: () { + GoogleMapsNavigationPlatform.instance + .enableRoadSnappedLocationUpdates(); + }, + ); + unawaited(_roadSnappedRawLocationUpdatedController!.addStream( + GoogleMapsNavigationPlatform.instance + .getNavigationRoadSnappedRawLocationEventStream())); + } + + return _roadSnappedRawLocationUpdatedController!.stream.listen(listener); + } + + /// Disables road snapped location updates if there are no listeners. + static void _disableRoadSnappedLocationUpdatesIfNoActiveListeners() { + if (!(_roadSnappedLocationUpdatedController?.hasListener ?? false) && + !(_roadSnappedRawLocationUpdatedController?.hasListener ?? false) && + !(_gpsAvailabilityUpdatedController?.hasListener ?? false)) { + GoogleMapsNavigationPlatform.instance.disableRoadSnappedLocationUpdates(); + } } /// Cleans up the navigation session. /// /// Cleans up the navigator's internal state, clearing - /// any existing route waypoints and stopping ongoing + /// listeners, any existing route waypoints and stopping ongoing /// navigation guidance and simulation. /// /// On iOS the session is fully deleted and needs to be recreated @@ -174,7 +371,6 @@ class GoogleMapsNavigator { /// /// On Android the session is cleaned up, but never destroyed after the /// first initialization. - /// static Future cleanup() async { await GoogleMapsNavigationPlatform.instance.cleanup(); } @@ -189,7 +385,6 @@ class GoogleMapsNavigator { /// /// Returns true if the user accepts the terms, and false if not. If the terms /// have already been accepted returns true without showing the dialog again. - // static Future showTermsAndConditionsDialog( String title, String companyName, {bool shouldOnlyShowDriverAwarenessDisclaimer = false}) async { @@ -210,6 +405,11 @@ class GoogleMapsNavigator { return GoogleMapsNavigationPlatform.instance.resetTermsAccepted(); } + /// Gets the native navigation SDK version as string. + static Future getNavSDKVersion() async { + return GoogleMapsNavigationPlatform.instance.getNavSDKVersion(); + } + /// Starts the navigation guidance. static Future startGuidance() { return GoogleMapsNavigationPlatform.instance.startGuidance(); @@ -225,9 +425,18 @@ class GoogleMapsNavigator { return GoogleMapsNavigationPlatform.instance.isGuidanceRunning(); } - /// Sets destination waypoints and other settings. - static Future setDestinations(Destinations msg) { - return GoogleMapsNavigationPlatform.instance.setDestinations(msg); + /// Sets one or multiple destinations for navigation. + /// + /// Destinations are passed as [NavigationWaypoint] waypoints and options with + /// [destinations] parameter. Routing can be controlled by defining + /// [RoutingOptions], or using route tokens passed with [RouteTokenOptions]. + /// [NavigationDisplayOptions] will be used to display the route. + /// + /// If the options are omitted, the default routing and display options will + /// be used. + static Future setDestinations( + Destinations destinations) { + return GoogleMapsNavigationPlatform.instance.setDestinations(destinations); } /// Clears existing destinations. @@ -281,6 +490,7 @@ class GoogleMapsNavigator { } /// Possible errors that [GoogleMapsNavigator.initializeNavigationSession] can throw. +/// {@category Navigation} enum SessionInitializationError { /// The session initialization failed, because the required Maps API key is empty or invalid. notAuthorized, @@ -293,6 +503,7 @@ enum SessionInitializationError { } /// Exception thrown by [GoogleMapsNavigator.initializeNavigationSession]. +/// {@category Navigation} class SessionInitializationException implements Exception { /// Default constructor for [SessionInitializationException]. const SessionInitializationException(this.code); @@ -304,6 +515,7 @@ class SessionInitializationException implements Exception { /// Exception thrown by [GoogleMapsNavigator.resetTermsAccepted], /// when attempting to reset the terms and conditions after the session has /// already been initialized. +/// {@category Navigation} class ResetTermsAndConditionsException implements Exception { /// Default constructor for [ResetTermsAndConditionsException]. const ResetTermsAndConditionsException(); @@ -311,7 +523,15 @@ class ResetTermsAndConditionsException implements Exception { /// [GoogleMapsNavigator] navigation method call has failed, because the navigation /// session hasn't yet been successfully initialized. +/// {@category Navigation} class SessionNotInitializedException implements Exception { /// Default constructor for [SessionNotInitializedException]. const SessionNotInitializedException(); } + +/// [GoogleMapsNavigator.setDestinations] method call has failed, because the +/// [RouteTokenOptions.routeToken] is malformed. (Android only) +class RouteTokenMalformedException implements Exception { + /// Default constructor for [RouteTokenMalformedException]. + const RouteTokenMalformedException(); +} diff --git a/lib/src/types/circles.dart b/lib/src/types/circles.dart index 05f8b84..24ebb91 100644 --- a/lib/src/types/circles.dart +++ b/lib/src/types/circles.dart @@ -154,3 +154,16 @@ class CircleOptions { visible.hashCode, zIndex.hashCode); } + +/// Event emitted when a circle is clicked. +/// {@category Navigation View} +@immutable +class CircleClickedEvent { + /// Initialize [CircleClickedEvent] object. + const CircleClickedEvent({ + required this.circleId, + }); + + /// Id of the circle that has been tapped. + final String circleId; +} diff --git a/lib/src/types/images.dart b/lib/src/types/images.dart new file mode 100644 index 0000000..1674c79 --- /dev/null +++ b/lib/src/types/images.dart @@ -0,0 +1,60 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Defines an image. For marker this class can be used to set the +/// image of the marker icon. +/// {@category Image Registry} +@immutable +class ImageDescriptor { + /// Construct [ImageDescriptor]. + const ImageDescriptor( + {this.registeredImageId, this.imagePixelRatio, this.width, this.height}); + + /// If this class represents an image from image registry, [registeredImageId] is not null. + final String? registeredImageId; + + /// If image is bigger than it's intended display size, scale it down by this ratio. + final double? imagePixelRatio; + + /// Image width in logical pixels. + final double? width; + + /// Image height in logical pixels. + final double? height; + + /// Display default image on a marker or polyline. + static const ImageDescriptor defaultImage = ImageDescriptor(); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is ImageDescriptor && + registeredImageId == other.registeredImageId && + imagePixelRatio == other.imagePixelRatio && + width == other.width && + height == other.height; + } + + @override + int get hashCode => Object.hash(registeredImageId.hashCode, + imagePixelRatio.hashCode, width.hashCode, height.hashCode); +} diff --git a/lib/src/types/lat_lng.dart b/lib/src/types/lat_lng.dart index 5687bb4..a7f2837 100644 --- a/lib/src/types/lat_lng.dart +++ b/lib/src/types/lat_lng.dart @@ -13,7 +13,6 @@ // limitations under the License. import 'package:flutter/foundation.dart'; -import '../../google_maps_navigation.dart'; /// LatLng coordinate object. /// {@category Navigation} @@ -68,15 +67,3 @@ class LatLng { @override int get hashCode => Object.hash(latitude.hashCode, longitude.hashCode); } - -/// Converts lat lng point from the Pigeon DTO format. -/// @nodoc -LatLng latLngFromDto(LatLngDto point) { - return LatLng(latitude: point.latitude, longitude: point.longitude); -} - -/// Converts lat lng point to the Pigeon DTO format. -/// @nodoc -LatLngDto latLngToDto(LatLng point) { - return LatLngDto(latitude: point.latitude, longitude: point.longitude); -} diff --git a/lib/src/types/lat_lng_bounds.dart b/lib/src/types/lat_lng_bounds.dart index 9c8e9ce..93910b3 100644 --- a/lib/src/types/lat_lng_bounds.dart +++ b/lib/src/types/lat_lng_bounds.dart @@ -119,9 +119,3 @@ class LatLngBounds { @override int get hashCode => Object.hash(southwest.hashCode, northeast.hashCode); } - -/// Converts lat lng bounds to the Pigeon DTO format. -/// @nodoc -LatLngBoundsDto latLngBoundsToDto(LatLngBounds bounds) => LatLngBoundsDto( - northeast: latLngToDto(bounds.northeast), - southwest: latLngToDto(bounds.southwest)); diff --git a/lib/src/types/markers.dart b/lib/src/types/markers.dart index 08bcf92..eed0ecf 100644 --- a/lib/src/types/markers.dart +++ b/lib/src/types/markers.dart @@ -61,6 +61,7 @@ class MarkerOptions { this.anchor = const MarkerAnchor(u: 0.5, v: 1.0), this.draggable = false, this.flat = false, + this.icon = ImageDescriptor.defaultImage, this.consumeTapEvents = false, this.position = const LatLng(latitude: 0.0, longitude: 0.0), this.rotation = 0.0, @@ -88,6 +89,12 @@ class MarkerOptions { /// By default, the marker is drawn facing the camera; [flat] is false. final bool flat; + /// Specifies the image ID of the bitmap drawn as the marker on the map. + /// The bitmap must be registered with [registerBitmapImage] before creating marker. + /// + /// By default, the icon ID is [ImageDescriptor.defaultImage], this draws the default marker icon. + final ImageDescriptor icon; + /// Sets whether map view does the default behavior when clicking marker. /// If set to true default behaviour does not occur. /// If set to false the camera moves to marker and info window appears. @@ -126,12 +133,14 @@ class MarkerOptions { MarkerAnchor? anchor, bool? draggable, bool? flat, + ImageDescriptor? icon, bool? consumeTapEvents, MarkerAnchor? infoWindowAnchor, LatLng? position, double? rotation, String? snippet, String? title, + InfoWindow? infoWindow, bool? visible, double? zIndex}) { return MarkerOptions( @@ -139,10 +148,11 @@ class MarkerOptions { anchor: anchor ?? this.anchor, draggable: draggable ?? this.draggable, flat: flat ?? this.flat, + icon: icon ?? this.icon, consumeTapEvents: consumeTapEvents ?? this.consumeTapEvents, position: position ?? this.position, rotation: rotation ?? this.rotation, - infoWindow: infoWindow, + infoWindow: infoWindow ?? this.infoWindow, visible: visible ?? this.visible, zIndex: zIndex ?? this.zIndex); } @@ -160,6 +170,7 @@ class MarkerOptions { anchor == other.anchor && draggable == other.draggable && flat == other.flat && + icon == other.icon && consumeTapEvents == other.consumeTapEvents && position == other.position && rotation == other.rotation && @@ -174,6 +185,7 @@ class MarkerOptions { anchor.hashCode, draggable.hashCode, flat.hashCode, + icon.hashCode, consumeTapEvents.hashCode, position.hashCode, rotation.hashCode, @@ -259,3 +271,69 @@ class MarkerAnchor { @override int get hashCode => Object.hash(u.hashCode, v.hashCode); } + +/// Marker event types +/// {@category Navigation View} +enum MarkerEventType { + /// The marker has been tapped. + clicked, + + /// The marker info window has been tapped. + infoWindowClicked, + + /// The marker info window has been closed. + infoWindowClosed, + + /// The marker info window has been long clicked. + infoWindowLongClicked, +} + +/// Marker drag event types +/// {@category Navigation View} +enum MarkerDragEventType { + /// The marker is being dragged. + drag, + + /// The marker drag has been started. + dragStart, + + /// The marker drag has been ended. + dragEnd, +} + +/// Marker event sent from platform side. +/// {@category Navigation View} +@immutable +class MarkerEvent { + /// Initialize [MarkerEvent] object. + const MarkerEvent({ + required this.markerId, + required this.eventType, + }); + + /// Id of the marker that has been tapped. + final String markerId; + + /// Type of the event. + final MarkerEventType eventType; +} + +/// Marker drag event sent from platform side. +/// {@category Navigation View} +@immutable +class MarkerDragEvent { + /// Initialize [MarkerDragEvent] object. + const MarkerDragEvent( + {required this.markerId, + required this.eventType, + required this.position}); + + /// Id of the marker that has been tapped. + final String markerId; + + /// Position of the marker that has been dragged. + final LatLng position; + + /// Type of the event. + final MarkerDragEventType eventType; +} diff --git a/lib/src/types/navigation_destinations.dart b/lib/src/types/navigation_destinations.dart index d6abb60..1cbbee5 100644 --- a/lib/src/types/navigation_destinations.dart +++ b/lib/src/types/navigation_destinations.dart @@ -15,6 +15,9 @@ import '../../google_maps_navigation.dart'; /// Destinations main type. +/// +/// Asserts if both [routeTokenOptions] and [routingOptions] are provided. +/// /// {@category Navigation} class Destinations { /// Destinations initializer with waypoints and options. @@ -22,7 +25,9 @@ class Destinations { required this.waypoints, required this.displayOptions, this.routingOptions, - }); + this.routeTokenOptions, + }) : assert(routeTokenOptions == null || routingOptions == null, + 'Only one of routeTokenOptions or routingOptions can be provided'); /// List of navigation waypoints. final List waypoints; @@ -32,6 +37,40 @@ class Destinations { /// Navigation routing options. final RoutingOptions? routingOptions; + + /// Navigation route token options. + final RouteTokenOptions? routeTokenOptions; +} + +/// Provides options for routing using a route token +/// in the Google Maps Navigation SDK. +/// +/// This class is used to specify routing preferences when using the +/// [GoogleMapsNavigator.setDestinations] method. It allows the integration +/// of a predefined route token, which the SDK can utilize for +/// routing if possible. +/// +/// {@category Navigation} +class RouteTokenOptions { + /// Route token options initializer with token and travel mode. + /// + /// The [travelMode] must match the travel mode used to generate + /// the [routeToken]. + RouteTokenOptions({ + required this.routeToken, + required this.travelMode, + }); + + /// Route token. + final String routeToken; + + /// Specifies the type of [NavigationTravelMode] used to determine the + /// navigation directions. + /// + /// It must match the travel mode used to generate the [routeToken]. + /// If there is a mismatch, [travelMode] will override the travel mode used to + /// generate the [routeToken]. + final NavigationTravelMode? travelMode; } /// Alternative routes strategy. @@ -146,13 +185,15 @@ class NavigationDisplayOptions { /// initialization. /// {@category Navigation} class NavigationWaypoint { - NavigationWaypoint._({ + /// Initializer for [NavigationWaypoint]. + NavigationWaypoint({ required this.title, this.target, this.placeID, this.preferSameSideOfRoad, this.preferredSegmentHeading, - }); + }) : assert(target != null || placeID != null, + 'Either target or placeID must be provided'); /// Initialize waypoint with coordinates. NavigationWaypoint.withLatLngTarget({ @@ -189,181 +230,21 @@ class NavigationWaypoint { /// Place ID of the waypoint. String? placeID; - /// Same side of the road preference of the waypoint. + /// Prefer to arrive on the same side of the road as the waypoint—snapped to + /// the nearest sidewalk. bool? preferSameSideOfRoad; /// Preferred segment heading of the waypoint. + /// + /// An arrival heading that matches the direction of traffic flow on the same + /// side of the road as the waiting consumer. + /// + /// The Navigation SDK chooses the road segment closest to the waypoint—that + /// has a lane direction that aligns (within +/- 55 degrees) with the side of + /// the road that the waypoint is on. int? preferredSegmentHeading; } -/// Navigation event messages that are returned from the native platforms. -/// {@category Navigation} -class NavigationSessionEvent { - /// Initializer for the event with type and message. - NavigationSessionEvent({ - required this.type, - required this.message, - }); - - /// Type of the event - final NavigationSessionEventType type; - - /// Message of the event. - final String message; -} - -/// Navigation Session events that returned from the native platforms. -/// {@category Navigation} -enum NavigationSessionEventType { - /// Arrival event. - arrivalEvent, - - ///Route changes event. - routeChanged, - - /// Error received event. - errorReceived; -} - -/// Converts navigation session event message from DTO. -/// @nodoc -NavigationSessionEvent navigationSessionEventFromDto( - NavigationSessionEventDto msg) { - final NavigationSessionEventType type = (() { - switch (msg.type) { - case NavigationSessionEventTypeDto.arrivalEvent: - return NavigationSessionEventType.arrivalEvent; - case NavigationSessionEventTypeDto.routeChanged: - return NavigationSessionEventType.routeChanged; - case NavigationSessionEventTypeDto.errorReceived: - return NavigationSessionEventType.errorReceived; - } - })(); - - return NavigationSessionEvent( - type: type, - message: msg.message, - ); -} - -/// Converts navigation destination to the Pigeon DTO format. -/// @nodoc -DestinationsDto navigationDestinationToDto(Destinations data) { - return DestinationsDto( - waypoints: data.waypoints.map( - (NavigationWaypoint e) { - return navigationWaypointToDto(e); - }, - ).toList(), - displayOptions: navigationDisplayOptionsToDto(data.displayOptions), - routingOptions: data.routingOptions == null - ? null - : routingOptionsToDto(data.routingOptions!)); -} - -/// Converts waypoint from the Pigeon DTO format. -/// @nodoc -NavigationWaypoint navigationWaypointFromDto(NavigationWaypointDto waypoint) { - return NavigationWaypoint._( - title: waypoint.title, - target: waypoint.target != null ? latLngFromDto(waypoint.target!) : null, - placeID: waypoint.placeID, - preferSameSideOfRoad: waypoint.preferSameSideOfRoad, - preferredSegmentHeading: waypoint.preferredSegmentHeading, - ); -} - -/// Converts waypoint to the Pigeon DTO format. -/// @nodoc -NavigationWaypointDto navigationWaypointToDto(NavigationWaypoint waypoint) { - return NavigationWaypointDto( - title: waypoint.title, - target: waypoint.target != null ? latLngToDto(waypoint.target!) : null, - placeID: waypoint.placeID, - preferSameSideOfRoad: waypoint.preferSameSideOfRoad, - preferredSegmentHeading: waypoint.preferredSegmentHeading, - ); -} - -/// Converts display options to the Pigeon DTO format. -/// @nodoc -NavigationDisplayOptionsDto navigationDisplayOptionsToDto( - NavigationDisplayOptions options) { - return NavigationDisplayOptionsDto( - showDestinationMarkers: options.showDestinationMarkers, - showStopSigns: options.showStopSigns, - showTrafficLights: options.showTrafficLights, - ); -} - -/// Converts travel model to the Pigeon DTO format. -/// @nodoc -TravelModeDto? navigationTravelModeToDto(NavigationTravelMode? travelMode) { - switch (travelMode) { - case NavigationTravelMode.driving: - return TravelModeDto.driving; - case NavigationTravelMode.cycling: - return TravelModeDto.cycling; - case NavigationTravelMode.walking: - return TravelModeDto.walking; - case NavigationTravelMode.twoWheeler: - return TravelModeDto.twoWheeler; - case NavigationTravelMode.taxi: - return TravelModeDto.taxi; - case null: - return null; - } -} - -/// Converts routing options to the Pigeon DTO format. -/// @nodoc -RoutingOptionsDto routingOptionsToDto(RoutingOptions options) { - return RoutingOptionsDto( - alternateRoutesStrategy: - navigationAlternateRoutesStrategyToDto(options.alternateRoutesStrategy), - routingStrategy: - navigationRoutingStrategyToPigeonFormat(options.routingStrategy), - targetDistanceMeters: options.targetDistanceMeters, - travelMode: navigationTravelModeToDto(options.travelMode), - avoidFerries: options.avoidFerries, - avoidHighways: options.avoidHighways, - avoidTolls: options.avoidTolls, - locationTimeoutMs: options.locationTimeoutMs, - ); -} - -/// Converts alternate routes strategy to the Pigeon DTO format. -/// @nodoc -AlternateRoutesStrategyDto? navigationAlternateRoutesStrategyToDto( - NavigationAlternateRoutesStrategy? strategy) { - switch (strategy) { - case NavigationAlternateRoutesStrategy.all: - return AlternateRoutesStrategyDto.all; - case NavigationAlternateRoutesStrategy.none: - return AlternateRoutesStrategyDto.none; - case NavigationAlternateRoutesStrategy.one: - return AlternateRoutesStrategyDto.one; - case null: - return null; - } -} - -/// Converts routing strategy to the Pigeon DTO format. -/// @nodoc -RoutingStrategyDto? navigationRoutingStrategyToPigeonFormat( - NavigationRoutingStrategy? strategy) { - switch (strategy) { - case NavigationRoutingStrategy.defaultBest: - return RoutingStrategyDto.defaultBest; - case NavigationRoutingStrategy.deltaToTargetDistance: - return RoutingStrategyDto.deltaToTargetDistance; - case NavigationRoutingStrategy.shorter: - return RoutingStrategyDto.shorter; - case null: - return null; - } -} - /// Status of the navigation routing. /// {@category Navigation} enum NavigationRouteStatus { @@ -413,43 +294,6 @@ enum NavigationRouteStatus { quotaCheckFailed } -/// Converts route status from the Pigeon DTO format. -/// @nodoc -NavigationRouteStatus navigationRouteStatusFromDto(RouteStatusDto status) { - switch (status) { - case RouteStatusDto.internalError: - return NavigationRouteStatus.internalError; - case RouteStatusDto.statusOk: - return NavigationRouteStatus.statusOk; - case RouteStatusDto.routeNotFound: - return NavigationRouteStatus.routeNotFound; - case RouteStatusDto.networkError: - return NavigationRouteStatus.networkError; - case RouteStatusDto.quotaExceeded: - return NavigationRouteStatus.quotaExceeded; - case RouteStatusDto.apiKeyNotAuthorized: - return NavigationRouteStatus.apiKeyNotAuthorized; - case RouteStatusDto.statusCanceled: - return NavigationRouteStatus.statusCanceled; - case RouteStatusDto.duplicateWaypointsError: - return NavigationRouteStatus.duplicateWaypointsError; - case RouteStatusDto.noWaypointsError: - return NavigationRouteStatus.noWaypointsError; - case RouteStatusDto.locationUnavailable: - return NavigationRouteStatus.locationUnavailable; - case RouteStatusDto.waypointError: - return NavigationRouteStatus.waypointError; - case RouteStatusDto.travelModeUnsupported: - return NavigationRouteStatus.travelModeUnsupported; - case RouteStatusDto.unknown: - return NavigationRouteStatus.unknown; - case RouteStatusDto.locationUnknown: - return NavigationRouteStatus.locationUnknown; - case RouteStatusDto.quotaCheckFailed: - return NavigationRouteStatus.quotaCheckFailed; - } -} - /// Time and distance to next waypoint. /// {@category Navigation} class NavigationTimeAndDistance { @@ -466,13 +310,6 @@ class NavigationTimeAndDistance { final double distance; } -/// Converts time and distance from the Pigeon DTO format. -/// @nodoc -NavigationTimeAndDistance navigationTimeAndDistanceFromDto( - NavigationTimeAndDistanceDto td) { - return NavigationTimeAndDistance(time: td.time, distance: td.distance); -} - /// Navigation audio guidance type. /// {@category Navigation} enum NavigationAudioGuidanceType { @@ -505,26 +342,3 @@ class NavigationAudioGuidanceSettings { /// Guidance type. final NavigationAudioGuidanceType? guidanceType; } - -/// Converts audio guidance settings to the Pigeon DTO format. -/// @nodoc -NavigationAudioGuidanceSettingsDto navigationAudioGuidanceSettingsToDto( - NavigationAudioGuidanceSettings settings) { - final AudioGuidanceTypeDto? guidanceType = (() { - switch (settings.guidanceType) { - case NavigationAudioGuidanceType.silent: - return AudioGuidanceTypeDto.silent; - case NavigationAudioGuidanceType.alertsAndGuidance: - return AudioGuidanceTypeDto.alertsAndGuidance; - case NavigationAudioGuidanceType.alertsOnly: - return AudioGuidanceTypeDto.alertsOnly; - case null: - return null; - } - })(); - return NavigationAudioGuidanceSettingsDto( - isBluetoothAudioEnabled: settings.isBluetoothAudioEnabled, - isVibrationEnabled: settings.isVibrationEnabled, - guidanceType: guidanceType, - ); -} diff --git a/lib/src/types/navigation_view_types.dart b/lib/src/types/navigation_view_types.dart index a4b5445..82c94ac 100644 --- a/lib/src/types/navigation_view_types.dart +++ b/lib/src/types/navigation_view_types.dart @@ -39,58 +39,24 @@ enum MapType { hybrid, } -/// Converts Maptype map type to pigeon DTO object. -/// @nodoc -MapTypeDto mapTypeToDto(MapType type) { - switch (type) { - case MapType.none: - return MapTypeDto.none; - case MapType.hybrid: - return MapTypeDto.hybrid; - case MapType.normal: - return MapTypeDto.normal; - case MapType.satellite: - return MapTypeDto.satellite; - case MapType.terrain: - return MapTypeDto.terrain; - } -} - -/// Converts map type from pigeon DTO format. -/// @nodoc -MapType mapTypeFromDto(MapTypeDto type) { - switch (type) { - case MapTypeDto.none: - return MapType.none; - case MapTypeDto.hybrid: - return MapType.hybrid; - case MapTypeDto.normal: - return MapType.normal; - case MapTypeDto.satellite: - return MapType.satellite; - case MapTypeDto.terrain: - return MapType.terrain; - } -} - /// Represents the camera position in a Google Maps view. /// {@category Navigation View} class CameraPosition { - /// Creates a [CameraPosition] object. + /// Creates a [CameraPosition] object with map centered + /// to the [target] with the given [bearing], [tilt] and [zoom] level. /// - /// All parameters have default values of zero, with the [target] - /// set to the geographical coordinates (0,0). + /// By default, the camera centers on the geographical coordinates (0,0) with + /// no tilt (0 degrees), camera zoomed out (3.0) and the bearing heading north + /// (0 degrees). const CameraPosition( {this.bearing = 0, this.target = const LatLng(latitude: 0, longitude: 0), this.tilt = 0, - this.zoom = 0}) + this.zoom = 3.0}) : assert(0 <= bearing && bearing < 360, 'Bearing must be between 0 and 360 degrees.'), assert( - 0 <= tilt && tilt <= 90, 'Tilt must be between 0 and 90 degrees.'), - assert(googleMapsMinZoomLevel <= zoom && zoom <= googleMapsMaxZoomLevel, - 'Zoom must be between $googleMapsMinZoomLevel and $googleMapsMaxZoomLevel.'); + 0 <= tilt && tilt <= 90, 'Tilt must be between 0 and 90 degrees.'); /// Bearing of the camera, in degrees clockwise from true north. final double bearing; @@ -105,7 +71,7 @@ class CameraPosition { final double zoom; } -/// Parameter given to parameter given to the followMyLocation() +/// Parameter given to parameter given to the [GoogleNavigationViewController.followMyLocation] /// to specify the orientation of the camera. /// {@category Navigation View} enum CameraPerspective { @@ -119,39 +85,6 @@ enum CameraPerspective { topDownNorthUp } -/// Converts the view event type from the Pigeon DTO format. -/// @nodoc -CameraPosition cameraPositionfromDto(CameraPositionDto position) { - return CameraPosition( - bearing: position.bearing, - target: latLngFromDto(position.target), - tilt: position.tilt, - zoom: position.zoom); -} - -/// Converts the view event type to the Pigeon DTO format. -/// @nodoc -CameraPositionDto cameraPositionToDto(CameraPosition position) { - return CameraPositionDto( - bearing: position.bearing, - target: latLngToDto(position.target), - tilt: position.tilt, - zoom: position.zoom); -} - -/// Converts the camera perspective to the Pigeon DTO format. -/// @nodoc -CameraPerspectiveDto cameraPerspectiveTypeToDto(CameraPerspective perspective) { - switch (perspective) { - case CameraPerspective.tilted: - return CameraPerspectiveDto.tilted; - case CameraPerspective.topDownHeadingUp: - return CameraPerspectiveDto.topDownHeadingUp; - case CameraPerspective.topDownNorthUp: - return CameraPerspectiveDto.topDownNorthUp; - } -} - /// Represents the click position in a Google Maps view. /// {@category Navigation View} class MapClickEvent { @@ -162,6 +95,16 @@ class MapClickEvent { final LatLng target; } +/// Represents navigation UI changed event in a view. +/// {@category Navigation View} +class NavigationUIEnabledChangedEvent { + /// Creates a [NavigationUIEnabledChangedEvent] object. + const NavigationUIEnabledChangedEvent(this.navigationUIEnabled); + + /// Value representing whether UI changed or not. + final bool navigationUIEnabled; +} + /// Represents the long click position in a Google Maps view. /// {@category Navigation View} class MapLongClickEvent { @@ -257,74 +200,6 @@ class RouteSegment { final NavigationWaypoint? destinationWaypoint; } -/// Convert road stretch rendering data from Pigeon DTO format. -/// @nodoc -RouteSegmentTrafficDataRoadStretchRenderingData roadStretchRenderingDataFromDto( - RouteSegmentTrafficDataRoadStretchRenderingDataDto data) { - return RouteSegmentTrafficDataRoadStretchRenderingData( - style: () { - switch (data.style) { - case RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto - .slowerTraffic: - return RouteSegmentTrafficDataRoadStretchRenderingDataStyle - .slowerTraffic; - case RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto.trafficJam: - return RouteSegmentTrafficDataRoadStretchRenderingDataStyle - .trafficJam; - case RouteSegmentTrafficDataRoadStretchRenderingDataStyleDto.unknown: - return RouteSegmentTrafficDataRoadStretchRenderingDataStyle.unknown; - } - }(), - lengthMeters: data.lengthMeters, - offsetMeters: data.offsetMeters, - ); -} - -/// Convert route segment traffic data from DTO. -/// @nodoc -RouteSegmentTrafficData routeSegmentTrafficDataFromDto( - RouteSegmentTrafficDataDto data) { - return RouteSegmentTrafficData( - status: () { - switch (data.status) { - case RouteSegmentTrafficDataStatusDto.ok: - return RouteSegmentTrafficDataStatus.ok; - case RouteSegmentTrafficDataStatusDto.unavailable: - return RouteSegmentTrafficDataStatus.unavailable; - } - }(), - roadStretchRenderingDataList: data.roadStretchRenderingDataList - .where((RouteSegmentTrafficDataRoadStretchRenderingDataDto? d) => - d != null) - .cast() - .map((RouteSegmentTrafficDataRoadStretchRenderingDataDto d) => - roadStretchRenderingDataFromDto(d)) - .toList(), - ); -} - -/// Convert route segment from DTO. -/// @nodoc -RouteSegment routeSegmentFromDto(RouteSegmentDto segment) { - return RouteSegment( - destinationLatLng: LatLng( - latitude: segment.destinationLatLng.latitude, - longitude: segment.destinationLatLng.longitude), - destinationWaypoint: segment.destinationWaypoint != null - ? navigationWaypointFromDto(segment.destinationWaypoint!) - : null, - latLngs: segment.latLngs - ?.where((LatLngDto? p) => p != null) - .cast() - .map((LatLngDto p) => - LatLng(latitude: p.latitude, longitude: p.longitude)) - .toList(), - trafficData: segment.trafficData != null - ? routeSegmentTrafficDataFromDto(segment.trafficData!) - : null, - ); -} - /// Internal camera update type. /// {@category Navigation View} enum CameraUpdateType { @@ -388,6 +263,10 @@ class CameraUpdate { /// Returns a camera update that moves the camera target to the specified /// geographical location and zoom level. + /// + /// Zoom level is bound to minimum and maximum zoom preference. See + /// [GoogleNavigationViewController.setMinZoomPreference] and + /// [GoogleNavigationViewController.setMaxZoomPreference]. static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { final CameraUpdate update = CameraUpdate._(CameraUpdateType.latLngZoom); update.latLng = latLng; @@ -411,6 +290,10 @@ class CameraUpdate { /// Returns a camera update that modifies the camera zoom level by the /// specified amount. The optional [focus] is a screen point whose underlying /// geographical location should be invariant, if possible, by the movement. + /// + /// Zoom level is bound to minimum and maximum zoom preference. See + /// [GoogleNavigationViewController.setMinZoomPreference] and + /// [GoogleNavigationViewController.setMaxZoomPreference]. static CameraUpdate zoomBy(double amount, {Offset? focus}) { final CameraUpdate update = CameraUpdate._(CameraUpdateType.zoomBy); update.zoomByAmount = amount; @@ -423,6 +306,10 @@ class CameraUpdate { /// closer to the surface of the Earth. /// /// Equivalent to the result of calling `zoomBy(1.0)`. + /// + /// Zoom level is bound to minimum and maximum zoom preference. See + /// [GoogleNavigationViewController.setMinZoomPreference] and + /// [GoogleNavigationViewController.setMaxZoomPreference]. static CameraUpdate zoomIn() { final CameraUpdate update = CameraUpdate._(CameraUpdateType.zoomBy); update.zoomByAmount = 1.0; @@ -433,6 +320,10 @@ class CameraUpdate { /// further away from the surface of the Earth. /// /// Equivalent to the result of calling `zoomBy(-1.0)`. + /// + /// Zoom level is bound to minimum and maximum zoom preference. See + /// [GoogleNavigationViewController.setMinZoomPreference] and + /// [GoogleNavigationViewController.setMaxZoomPreference]. static CameraUpdate zoomOut() { final CameraUpdate update = CameraUpdate._(CameraUpdateType.zoomBy); update.zoomByAmount = -1.0; @@ -440,6 +331,10 @@ class CameraUpdate { } /// Returns a camera update that sets the camera zoom level. + /// + /// Zoom level is bound to minimum and maximum zoom preference. See + /// [GoogleNavigationViewController.setMinZoomPreference] and + /// [GoogleNavigationViewController.setMaxZoomPreference]. static CameraUpdate zoomTo(double zoom) { final CameraUpdate update = CameraUpdate._(CameraUpdateType.zoomTo); update.zoom = zoom; @@ -476,3 +371,47 @@ class CameraUpdate { /// The screen position co-ordinates for the zoom-by camera. Offset? focus; } + +/// My location clicked event. +/// {@category Navigation View} +class MyLocationClickedEvent {} + +/// My location button clicked event. +/// {@category Navigation View} +class MyLocationButtonClickedEvent {} + +/// Represents the event type for [CameraChangedEvent]. +/// {@category Navigation View} +enum CameraEventType { + /// Camera move initiated by developer or in response to user action. + /// For example: zoom buttons, my location button, or marker clicks. + moveStartedByApi, + + /// Camera move initiated in response to user gestures on the map. + moveStartedByGesture, + + /// Called repeatedly as the camera continues to move. + onCameraMove, + + /// Called when camera movement has ended. + onCameraIdle, + + /// Called when camera starts following location (Android only). + onCameraStartedFollowingLocation, + + /// Called when camera stops following location (Android only). + onCameraStoppedFollowingLocation +} + +/// Represents camera changed events in a Google Maps view. +/// {@category Navigation View} +class CameraChangedEvent { + /// Creates a [CameraChangedEvent] object. + const CameraChangedEvent({required this.eventType, required this.position}); + + /// Event type that happened. + final CameraEventType eventType; + + /// Current position of the camera. + final CameraPosition position; +} diff --git a/lib/src/types/polygons.dart b/lib/src/types/polygons.dart index 2a1e4ef..d258211 100644 --- a/lib/src/types/polygons.dart +++ b/lib/src/types/polygons.dart @@ -169,3 +169,16 @@ class PolygonOptions { visible.hashCode, zIndex.hashCode); } + +/// Event emitted when a polygon is clicked. +/// {@category Navigation View} +@immutable +class PolygonClickedEvent { + /// Initialize [PolygonClickedEvent] object. + const PolygonClickedEvent({ + required this.polygonId, + }); + + /// Id of the polygon that has been tapped. + final String polygonId; +} diff --git a/lib/src/types/polylines.dart b/lib/src/types/polylines.dart index f67c556..d3d020c 100644 --- a/lib/src/types/polylines.dart +++ b/lib/src/types/polylines.dart @@ -200,7 +200,7 @@ class StyleSpan { final double length; /// Style of a stroke. - final StyleSpanStrokeStyleDto style; + final StyleSpanStrokeStyle style; } /// Joint types for [Polyline] and outline of [Polygon]. @@ -215,3 +215,16 @@ enum StrokeJointType { /// Rounded on the outside of the joint by an arc of radius equal to half the stroke width, centered at the vertex. round } + +/// Event emitted when a polyline is clicked. +/// {@category Navigation View} +@immutable +class PolylineClickedEvent { + /// Initialize [PolylineClickedEvent] object. + const PolylineClickedEvent({ + required this.polylineId, + }); + + /// Id of the polyline that has been tapped. + final String polylineId; +} diff --git a/lib/src/types/simulation.dart b/lib/src/types/simulation.dart index ceb6d72..4d75a17 100644 --- a/lib/src/types/simulation.dart +++ b/lib/src/types/simulation.dart @@ -58,16 +58,26 @@ class RoadSnappedLocationUpdatedEvent { final LatLng location; } -/// RoadSnappedRawLocationUpdated event message. +/// RoadSnappedRawLocationUpdated event message (Android only). /// {@category Navigation} class RoadSnappedRawLocationUpdatedEvent { /// Initialize road snapped raw location updated event message. RoadSnappedRawLocationUpdatedEvent({ - required this.coordinate, + required this.location, }); /// Coordinate of the updated location. - final LatLng? coordinate; + final LatLng location; +} + +/// GpsAvailabilityUpdated event message (Android only). +/// {@category Navigation} +class GpsAvailabilityUpdatedEvent { + /// Initialize GPS availability updated event message. + GpsAvailabilityUpdatedEvent({required this.available}); + + /// GPS availability. + final bool available; } /// Navigation simulation options. @@ -82,61 +92,6 @@ class SimulationOptions { final double speedMultiplier; } -/// Converts speed alert severity from DTO -/// @nodoc -SpeedAlertSeverity navigationSpeedAlertSeverityFromDto( - SpeedAlertSeverityDto severity) { - switch (severity) { - case SpeedAlertSeverityDto.unknown: - return SpeedAlertSeverity.unknown; - case SpeedAlertSeverityDto.notSpeeding: - return SpeedAlertSeverity.notSpeeding; - case SpeedAlertSeverityDto.minor: - return SpeedAlertSeverity.minor; - case SpeedAlertSeverityDto.major: - return SpeedAlertSeverity.major; - } -} - -/// Converts navigation options to the Pigeon DTO format. -/// @nodoc -SimulationOptionsDto simulationOptionsToDto(SimulationOptions options) { - return SimulationOptionsDto(speedMultiplier: options.speedMultiplier); -} - -/// Converts speeding updated event message from Pigeon DTO. -/// @nodoc -SpeedingUpdatedEvent speedingUpdatedEventFromDto(SpeedingUpdatedEventDto msg) { - return SpeedingUpdatedEvent( - percentageAboveLimit: msg.percentageAboveLimit, - severity: navigationSpeedAlertSeverityFromDto(msg.severity)); -} - -/// Converts road snapped location updated event message from Pigeon DTO. -/// @nodoc -RoadSnappedLocationUpdatedEvent roadSnappedLocationUpdatedEventFromDto( - RoadSnappedLocationUpdatedEventDto msg) { - return RoadSnappedLocationUpdatedEvent( - location: LatLng( - latitude: msg.location.latitude, longitude: msg.location.longitude)); -} - -/// Converts road snapped raw location updated event message from Pigeon DTO. -/// @nodoc -RoadSnappedRawLocationUpdatedEvent roadSnappedRawLocationUpdatedEventFromDto( - RoadSnappedRawLocationUpdatedEventDto msg) { - if (msg.location != null) { - return RoadSnappedRawLocationUpdatedEvent( - coordinate: LatLng( - latitude: msg.location!.latitude, longitude: msg.location!.longitude), - ); - } else { - return RoadSnappedRawLocationUpdatedEvent( - coordinate: null, - ); - } -} - /// Remaining time or distance change event message. /// {@category Navigation} class RemainingTimeOrDistanceChangedEvent { @@ -164,13 +119,3 @@ class OnArrivalEvent { /// Arrival waypoint. final NavigationWaypoint waypoint; } - -/// Converts remaining distance or remaining time changed event from Pigeon DTO. -/// @nodoc -RemainingTimeOrDistanceChangedEvent remainingTimeOrDistanceChangedEventFromDto( - RemainingTimeOrDistanceChangedEventDto msg) { - return RemainingTimeOrDistanceChangedEvent( - remainingDistance: msg.remainingDistance, - remainingTime: msg.remainingTime, - ); -} diff --git a/lib/src/types/stroke_patterns.dart b/lib/src/types/stroke_patterns.dart index 6f76ed0..f13033f 100644 --- a/lib/src/types/stroke_patterns.dart +++ b/lib/src/types/stroke_patterns.dart @@ -17,19 +17,35 @@ import 'package:flutter/material.dart'; import '../../google_maps_navigation.dart'; +/// Pattern used in the stroke pattern for a [Polyline] or the outline of a [Polygon] or [Circle]. +/// {@category Navigation View} +enum PatternType { + /// Dash pattern. + dash, + + /// Dot pattern. + dot, + + /// Gap pattern. + gap, +} + /// Item used in the stroke pattern for a Polyline or the outline of a Polygon or Circle. /// {@category Navigation View} abstract class PatternItem { - /// Convert [PatternItem] to [PatternItemDto]; - PatternItemDto toPatternItemDto(); + /// Initialize [PatternItem] object. + const PatternItem(this.type); + + /// Type of the pattern item. + final PatternType type; } /// Class representing a dash used in the stroke pattern for a [Polyline] or the outline of a [Polygon] or [Circle]. /// {@category Navigation View} @immutable -class DashPattern implements PatternItem { +class DashPattern extends PatternItem { /// Initialize [DashPattern] object. - const DashPattern({required this.length}); + const DashPattern({required this.length}) : super(PatternType.dash); /// Length in pixels (non-negative). final double length; @@ -47,32 +63,22 @@ class DashPattern implements PatternItem { @override int get hashCode => length.hashCode; - - @override - PatternItemDto toPatternItemDto() { - return PatternItemDto(type: PatternTypeDto.dash, length: length); - } } /// Class representing a dot used in the stroke pattern for a [Polyline] or the outline of a [Polygon] or [Circle]. /// {@category Navigation View} @immutable -class DotPattern implements PatternItem { +class DotPattern extends PatternItem { /// Initialize [DotPattern] object. - const DotPattern(); - - @override - PatternItemDto toPatternItemDto() { - return PatternItemDto(type: PatternTypeDto.dot); - } + const DotPattern() : super(PatternType.dot); } /// Class representing a gap used in the stroke pattern for a [Polyline] or the outline of a [Polygon] or [Circle]. /// {@category Navigation View} @immutable -class GapPattern implements PatternItem { +class GapPattern extends PatternItem { /// Initialize [GapPattern] object. - const GapPattern({required this.length}); + const GapPattern({required this.length}) : super(PatternType.gap); /// Length in pixels (non-negative). final double length; @@ -90,9 +96,4 @@ class GapPattern implements PatternItem { @override int get hashCode => length.hashCode; - - @override - PatternItemDto toPatternItemDto() { - return PatternItemDto(type: PatternTypeDto.gap, length: length); - } } diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index fa137a9..aef14dc 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -13,6 +13,7 @@ // limitations under the License. export 'circles.dart'; +export 'images.dart'; export 'lat_lng.dart'; export 'lat_lng_bounds.dart'; export 'markers.dart'; @@ -23,4 +24,3 @@ export 'polylines.dart'; export 'simulation.dart'; export 'stroke_patterns.dart'; export 'view_initialization_options.dart'; -export 'zoom_constants.dart'; diff --git a/lib/src/types/view_initialization_options.dart b/lib/src/types/view_initialization_options.dart index 4835f72..24d9112 100644 --- a/lib/src/types/view_initialization_options.dart +++ b/lib/src/types/view_initialization_options.dart @@ -98,17 +98,7 @@ class MapOptions { required this.maxZoomPreference, required this.zoomControlsEnabled, required this.cameraTargetBounds, - }) : assert( - minZoomPreference == null || - (minZoomPreference >= googleMapsMinZoomLevel && - minZoomPreference <= googleMapsMaxZoomLevel), - 'minZoomPreference must be null or between $googleMapsMinZoomLevel and $googleMapsMaxZoomLevel.'), - assert( - maxZoomPreference == null || - (maxZoomPreference >= googleMapsMinZoomLevel && - maxZoomPreference <= googleMapsMaxZoomLevel), - 'maxZoomPreference must be null or between $googleMapsMinZoomLevel and $googleMapsMaxZoomLevel.'), - assert( + }) : assert( minZoomPreference == null || maxZoomPreference == null || minZoomPreference <= maxZoomPreference, @@ -159,6 +149,17 @@ class MapOptions { final LatLngBounds? cameraTargetBounds; } +/// Determines the initial visibility of the navigation UI on map initialization. +/// {@category Navigation View} +enum NavigationUIEnabledPreference { + /// Navigation UI gets enabled if the navigation + /// session has already been successfully started. + automatic, + + /// Navigation UI is disabled. + disabled, +} + /// Encapsulates the initial configuration required to initialize the navigation view. /// /// This class is used to specify various map settings at the time of map initialization. @@ -168,12 +169,17 @@ class NavigationViewOptions { /// Creates a new instance of [NavigationViewOptions] with the given initial /// parameters to configure the navigation view. const NavigationViewOptions({ - required this.navigationUIEnabled, + this.navigationUIEnabledPreference = + NavigationUIEnabledPreference.automatic, }); /// Determines the initial visibility of the navigation UI on map initialization. /// - /// If not set, by default the navigation UI is initially hidden - /// until the navigation is started. - final bool navigationUIEnabled; + /// If not set, by default set to [NavigationUIEnabledPreference.automatic], meaning + /// the navigation UI gets enabled if the navigation + /// session has already been successfully started. + /// + /// If set to [NavigationUIEnabledPreference.disabled], navigation view + /// initially displays a classic map view. + final NavigationUIEnabledPreference navigationUIEnabledPreference; } diff --git a/melos.yaml b/melos.yaml index 4954e9a..d72a0e5 100644 --- a/melos.yaml +++ b/melos.yaml @@ -70,7 +70,8 @@ scripts: format:android: run: | melos exec -c 1 --fail-fast -- \ - "cd android && ./gradlew ktfmtFormat" + "if [ ! -f android/gradlew ]; then flutter build apk --config-only; fi \ + && cd android && ./gradlew ktfmtFormat" description: | Formats the code of Android package with ktfmt. packageFilters: @@ -83,6 +84,8 @@ scripts: melos exec -c 1 --fail-fast -- \ "flutter pub get && flutter test" description: Flutter test + packageFilters: + fileExists: 'pigeons/messages.dart' flutter-build-android: run: | diff --git a/pigeons/messages.dart b/pigeons/messages.dart index 453246b..80ef31f 100644 --- a/pigeons/messages.dart +++ b/pigeons/messages.dart @@ -86,14 +86,24 @@ class MapOptionsDto { final LatLngBoundsDto? cameraTargetBounds; } +/// Determines the initial visibility of the navigation UI on map initialization. +enum NavigationUIEnabledPreferenceDto { + /// Navigation UI gets enabled if the navigation + /// session has already been successfully started. + automatic, + + /// Navigation UI is disabled. + disabled +} + /// Object containing navigation options used to initialize Google Navigation view. class NavigationViewOptionsDto { NavigationViewOptionsDto({ - required this.navigationUIEnabled, + required this.navigationUIEnabledPreference, }); /// Determines the initial visibility of the navigation UI on map initialization. - final bool navigationUIEnabled; + final NavigationUIEnabledPreferenceDto navigationUIEnabledPreference; } /// A message for creating a new navigation view. @@ -157,7 +167,8 @@ class MarkerOptionsDto { required this.rotation, required this.infoWindow, required this.visible, - required this.zIndex}); + required this.zIndex, + required this.icon}); final double alpha; final MarkerAnchorDto anchor; @@ -169,6 +180,16 @@ class MarkerOptionsDto { final InfoWindowDto infoWindow; final bool visible; final double zIndex; + final ImageDescriptorDto icon; +} + +class ImageDescriptorDto { + const ImageDescriptorDto( + {this.registeredImageId, this.imagePixelRatio, this.width, this.height}); + final String? registeredImageId; + final double? imagePixelRatio; + final double? width; + final double? height; } class InfoWindowDto { @@ -195,30 +216,8 @@ enum MarkerEventTypeDto { infoWindowLongClicked } -class MarkerEventDto { - MarkerEventDto( - {required this.viewId, required this.markerId, required this.eventType}); - - final int viewId; - final String markerId; - final MarkerEventTypeDto eventType; -} - enum MarkerDragEventTypeDto { drag, dragStart, dragEnd } -class MarkerDragEventDto { - MarkerDragEventDto( - {required this.viewId, - required this.markerId, - required this.eventType, - required this.position}); - - final int viewId; - final String markerId; - final MarkerDragEventTypeDto eventType; - final LatLngDto position; -} - class PolygonDto { const PolygonDto({required this.polygonId, required this.options}); @@ -254,12 +253,6 @@ class PolygonHoleDto { final List points; } -class PolygonClickedEventDto { - PolygonClickedEventDto({required this.viewId, required this.polygonId}); - final int viewId; - final String polygonId; -} - class StyleSpanStrokeStyleDto { StyleSpanStrokeStyleDto.solidColor({ required this.solidColor, @@ -330,16 +323,6 @@ class PolylineOptionsDto { final List spans; } -class PolylineClickedEventDto { - PolylineClickedEventDto({ - required this.viewId, - required this.polylineId, - }); - - final int viewId; - final String polylineId; -} - class CircleDto { CircleDto({required this.circleId, required this.options}); @@ -374,14 +357,13 @@ class CircleOptionsDto { final bool clickable; } -class CircleClickedEventDto { - CircleClickedEventDto({ - required this.viewId, - required this.circleId, - }); - - final int viewId; - final String circleId; +enum CameraEventTypeDto { + moveStartedByApi, + moveStartedByGesture, + onCameraMove, + onCameraIdle, + onCameraStartedFollowingLocation, + onCameraStoppedFollowingLocation } @HostApi(dartHostTestHandler: 'TestNavigationViewApi') @@ -390,28 +372,28 @@ abstract class NavigationViewApi { void awaitMapReady(int viewId); bool isMyLocationEnabled(int viewId); - void enableMyLocation(int viewId, bool enabled); + void setMyLocationEnabled(int viewId, bool enabled); LatLngDto? getMyLocation(int viewId); MapTypeDto getMapType(int viewId); void setMapType(int viewId, MapTypeDto mapType); void setMapStyle(int viewId, String styleJson); bool isNavigationTripProgressBarEnabled(int viewId); - void enableNavigationTripProgressBar(int viewId, bool enabled); + void setNavigationTripProgressBarEnabled(int viewId, bool enabled); bool isNavigationHeaderEnabled(int viewId); - void enableNavigationHeader(int viewId, bool enabled); + void setNavigationHeaderEnabled(int viewId, bool enabled); bool isNavigationFooterEnabled(int viewId); - void enableNavigationFooter(int viewId, bool enabled); + void setNavigationFooterEnabled(int viewId, bool enabled); bool isRecenterButtonEnabled(int viewId); - void enableRecenterButton(int viewId, bool enabled); + void setRecenterButtonEnabled(int viewId, bool enabled); bool isSpeedLimitIconEnabled(int viewId); - void enableSpeedLimitIcon(int viewId, bool enabled); + void setSpeedLimitIconEnabled(int viewId, bool enabled); bool isSpeedometerEnabled(int viewId); - void enableSpeedometer(int viewId, bool enabled); - bool isIncidentCardsEnabled(int viewId); - void enableIncidentCards(int viewId, bool enabled); + void setSpeedometerEnabled(int viewId, bool enabled); + bool isTrafficIncidentCardsEnabled(int viewId); + void setTrafficIncidentCardsEnabled(int viewId, bool enabled); bool isNavigationUIEnabled(int viewId); - void enableNavigationUI(int viewId, bool enabled); + void setNavigationUIEnabled(int viewId, bool enabled); CameraPositionDto getCameraPosition(int viewId); LatLngBoundsDto getVisibleRegion(int viewId); @@ -448,19 +430,26 @@ abstract class NavigationViewApi { int viewId, double zoomBy, double? focusDx, double? focusDy); void moveCameraToZoom(int viewId, double zoom); void showRouteOverview(int viewId); - - void enableMyLocationButton(int viewId, bool enabled); - void enableZoomGestures(int viewId, bool enabled); - void enableZoomControls(int viewId, bool enabled); - void enableCompass(int viewId, bool enabled); - void enableRotateGestures(int viewId, bool enabled); - void enableScrollGestures(int viewId, bool enabled); - void enableScrollGesturesDuringRotateOrZoom(int viewId, bool enabled); - void enableTiltGestures(int viewId, bool enabled); - void enableMapToolbar(int viewId, bool enabled); - void enableTraffic(int viewId, bool enabled); + double getMinZoomPreference(int viewId); + double getMaxZoomPreference(int viewId); + void resetMinMaxZoomPreference(int viewId); + void setMinZoomPreference(int viewId, double minZoomPreference); + void setMaxZoomPreference(int viewId, double maxZoomPreference); + + void setMyLocationButtonEnabled(int viewId, bool enabled); + void setConsumeMyLocationButtonClickEventsEnabled(int viewId, bool enabled); + void setZoomGesturesEnabled(int viewId, bool enabled); + void setZoomControlsEnabled(int viewId, bool enabled); + void setCompassEnabled(int viewId, bool enabled); + void setRotateGesturesEnabled(int viewId, bool enabled); + void setScrollGesturesEnabled(int viewId, bool enabled); + void setScrollGesturesDuringRotateOrZoomEnabled(int viewId, bool enabled); + void setTiltGesturesEnabled(int viewId, bool enabled); + void setMapToolbarEnabled(int viewId, bool enabled); + void setTrafficEnabled(int viewId, bool enabled); bool isMyLocationButtonEnabled(int viewId); + bool isConsumeMyLocationButtonClickEventsEnabled(int viewId); bool isZoomGesturesEnabled(int viewId); bool isZoomControlsEnabled(int viewId); bool isCompassEnabled(int viewId); @@ -495,6 +484,17 @@ abstract class NavigationViewApi { List updateCircles(int viewId, List circles); void removeCircles(int viewId, List circles); void clearCircles(int viewId); + + void registerOnCameraChangedListener(int viewId); +} + +@HostApi(dartHostTestHandler: 'TestImageRegistryApi') +abstract class ImageRegistryApi { + ImageDescriptorDto registerBitmapImage(String imageId, Uint8List bytes, + double imagePixelRatio, double? width, double? height); + void unregisterImage(ImageDescriptorDto imageDescriptor); + List getRegisteredImages(); + void clearRegisteredImages(); } @FlutterApi() @@ -502,26 +502,27 @@ abstract class NavigationViewEventApi { void onMapClickEvent(int viewId, LatLngDto latLng); void onMapLongClickEvent(int viewId, LatLngDto latLng); void onRecenterButtonClicked(int viewId); - void onMarkerEvent(MarkerEventDto msg); - void onMarkerDragEvent(MarkerDragEventDto msg); - void onPolygonClicked(PolygonClickedEventDto msg); - void onPolylineClicked(PolylineClickedEventDto msg); - void onCircleClicked(CircleClickedEventDto msg); -} - -class NavigationSessionEventDto { - NavigationSessionEventDto({ - required this.type, - required this.message, - }); - final NavigationSessionEventTypeDto type; - final String message; -} - -enum NavigationSessionEventTypeDto { - arrivalEvent, - routeChanged, - errorReceived; + void onMarkerEvent(int viewId, String markerId, MarkerEventTypeDto eventType); + void onMarkerDragEvent(int viewId, String markerId, + MarkerDragEventTypeDto eventType, LatLngDto position); + void onPolygonClicked(int viewId, String polygonId); + void onPolylineClicked(int viewId, String polylineId); + void onCircleClicked(int viewId, String circleId); + void onNavigationUIEnabledChanged(int viewId, bool navigationUIEnabled); + void onMyLocationClicked(int viewId); + void onMyLocationButtonClicked(int viewId); + void onCameraChanged( + int viewId, CameraEventTypeDto eventType, CameraPositionDto position); +} + +class RouteTokenOptionsDto { + RouteTokenOptionsDto({ + required this.routeToken, + required this.travelMode, + }); + + final String routeToken; + final TravelModeDto? travelMode; } class DestinationsDto { @@ -529,10 +530,12 @@ class DestinationsDto { required this.waypoints, required this.displayOptions, this.routingOptions, + this.routeTokenOptions, }); final List waypoints; final NavigationDisplayOptionsDto displayOptions; final RoutingOptionsDto? routingOptions; + final RouteTokenOptionsDto? routeTokenOptions; } enum AlternateRoutesStrategyDto { @@ -690,64 +693,6 @@ class SpeedingUpdatedEventDto { final SpeedAlertSeverityDto severity; } -class RoadSnappedLocationUpdatedEventDto { - RoadSnappedLocationUpdatedEventDto({ - required this.location, - }); - - final LatLngDto location; -} - -class RoadSnappedRawLocationUpdatedEventDto { - RoadSnappedRawLocationUpdatedEventDto({ - required this.location, - }); - - final LatLngDto? location; -} - -class OnArrivalEventDto { - OnArrivalEventDto({ - required this.waypoint, - }); - - final NavigationWaypointDto waypoint; -} - -class RemainingTimeOrDistanceChangedEventDto { - RemainingTimeOrDistanceChangedEventDto({ - required this.remainingTime, - required this.remainingDistance, - }); - - final double remainingTime; - final double remainingDistance; -} - -class RouteChangedEventDto { - RouteChangedEventDto({ - required this.message, - }); - - final String message; -} - -class ReroutingEventDto { - ReroutingEventDto({ - required this.message, - }); - - final String message; -} - -class TrafficUpdatedEventDto { - TrafficUpdatedEventDto({ - required this.message, - }); - - final String message; -} - class SpeedAlertOptionsThresholdPercentageDto { SpeedAlertOptionsThresholdPercentageDto({ required this.percentage, @@ -819,7 +764,7 @@ class RouteSegmentDto { abstract class NavigationSessionApi { /// General. @async - void createNavigationSession(); + void createNavigationSession(bool abnormalTerminationReportingEnabled); bool isInitialized(); void cleanup(); @async @@ -827,13 +772,14 @@ abstract class NavigationSessionApi { bool shouldOnlyShowDriverAwarenessDisclaimer); bool areTermsAccepted(); void resetTermsAccepted(); + String getNavSDKVersion(); /// Navigation. bool isGuidanceRunning(); void startGuidance(); void stopGuidance(); @async - RouteStatusDto setDestinations(DestinationsDto msg); + RouteStatusDto setDestinations(DestinationsDto destinations); void clearDestinations(); NavigationWaypointDto? continueToNextDestination(); NavigationTimeAndDistanceDto getCurrentTimeAndDistance(); @@ -881,22 +827,22 @@ abstract class NavigationSessionApi { @FlutterApi() abstract class NavigationSessionEventApi { - // Android and iOS errors need to be unified first. - void onNavigationSessionEvent(NavigationSessionEventDto msg); void onSpeedingUpdated(SpeedingUpdatedEventDto msg); - void onRoadSnappedLocationUpdated(RoadSnappedLocationUpdatedEventDto msg); - void onRoadSnappedRawLocationUpdated( - RoadSnappedRawLocationUpdatedEventDto msg); - void onArrival(OnArrivalEventDto msg); - void onRouteChanged(RouteChangedEventDto msg); + void onRoadSnappedLocationUpdated(LatLngDto location); + void onRoadSnappedRawLocationUpdated(LatLngDto location); + void onArrival(NavigationWaypointDto waypoint); + void onRouteChanged(); void onRemainingTimeOrDistanceChanged( - RemainingTimeOrDistanceChangedEventDto msg); + double remainingTime, double remainingDistance); + + /// Android-only event. + void onTrafficUpdated(); - /// Android only event. - void onTrafficUpdated(TrafficUpdatedEventDto msg); + /// Android-only event. + void onRerouting(); - /// Android only event. - void onRerouting(ReroutingEventDto msg); + /// Android-only event. + void onGpsAvailabilityUpdate(bool available); } @HostApi() diff --git a/pubspec.yaml b/pubspec.yaml index 279ddd3..ef3a88a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,9 @@ name: google_maps_navigation description: A Google Maps Navigation plugin -version: 0.0.1 +repository: https://github.com/googlemaps/flutter-navigation-sdk +issue_tracker: https://github.com/googlemaps/flutter-navigation-sdk/issues +version: 0.2.0-beta publish_to: none environment: @@ -35,7 +37,7 @@ dev_dependencies: flutter_test: sdk: flutter melos: ^3.0.1 - mockito: ^5.4.3 + mockito: 5.4.4 pigeon: 15.0.0 flutter: @@ -47,4 +49,4 @@ flutter: dartPluginClass: GoogleMapsNavigationAndroid ios: pluginClass: GoogleMapsNavigationPlugin - dartPluginClass: GoogleMapsNavigationIOS \ No newline at end of file + dartPluginClass: GoogleMapsNavigationIOS diff --git a/test/google_maps_navigation_mock.dart b/test/google_maps_navigation_mock.dart deleted file mode 100644 index 1c31764..0000000 --- a/test/google_maps_navigation_mock.dart +++ /dev/null @@ -1,968 +0,0 @@ -// Copyright 2023 Google LLC -// -// 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 -// -// https://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. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:google_maps_navigation/google_maps_navigation.dart'; -import 'package:google_maps_navigation/src/google_maps_navigation_platform_interface.dart'; -import 'package:google_maps_navigation/src/types/util/circle_conversion.dart'; -import 'package:google_maps_navigation/src/types/util/marker_conversion.dart'; -import 'package:google_maps_navigation/src/types/util/polygon_conversion.dart'; -import 'package:google_maps_navigation/src/types/util/polyline_conversion.dart'; -import 'package:mockito/mockito.dart'; - -import 'google_maps_navigation_test.mocks.dart'; - -class NavigationSessionEventSubscription extends Mock - implements StreamSubscription {} - -class NavigationSessionEventStream extends Mock - implements Stream { - @override - StreamSubscription listen( - void Function(NavigationSessionEvent event)? onData, - {Function? onError, - void Function()? onDone, - bool? cancelOnError}) { - return NavigationSessionEventSubscription(); - } -} - -class NavigationViewMarkerEventSubscription extends Mock - implements StreamSubscription {} - -class NavigationViewMarkerEventStream extends Mock - implements Stream { - @override - StreamSubscription listen( - void Function(MarkerEventDto event)? onData, - {Function? onError, - void Function()? onDone, - bool? cancelOnError}) { - return NavigationViewMarkerEventSubscription(); - } -} - -class NavigationViewMarkerDragEventSubscription extends Mock - implements StreamSubscription {} - -class NavigationViewMarkerDragEventStream extends Mock - implements Stream { - @override - StreamSubscription listen( - void Function(MarkerDragEventDto event)? onData, - {Function? onError, - void Function()? onDone, - bool? cancelOnError}) { - return NavigationViewMarkerDragEventSubscription(); - } -} - -class PolygonDtoClickedEventSubscription extends Mock - implements StreamSubscription {} - -class PolygonDtoClickedEventStream extends Mock - implements Stream { - @override - StreamSubscription listen( - void Function(PolygonClickedEventDto event)? onData, - {Function? onError, - void Function()? onDone, - bool? cancelOnError}) { - return PolygonDtoClickedEventSubscription(); - } -} - -class NavigationViewPolylineClickedEventSubscription extends Mock - implements StreamSubscription {} - -class NavigationViewPolylineClickedEventStream extends Mock - implements Stream { - @override - StreamSubscription listen( - void Function(PolylineClickedEventDto event)? onData, - {Function? onError, - void Function()? onDone, - bool? cancelOnError}) { - return NavigationViewPolylineClickedEventSubscription(); - } -} - -class CircleDtoClickedEventSubscription extends Mock - implements StreamSubscription {} - -class CircleDtoClickedEventStream extends Mock - implements Stream { - @override - StreamSubscription listen( - void Function(CircleClickedEventDto event)? onData, - {Function? onError, - void Function()? onDone, - bool? cancelOnError}) { - return CircleDtoClickedEventSubscription(); - } -} - -class MockGoogleMapsNavigationPlatform extends GoogleMapsNavigationPlatform { - MockGoogleMapsNavigationPlatform( - {required this.sessionApi, required this.viewApi}); - final MockTestNavigationSessionApi sessionApi; - final MockTestNavigationViewApi viewApi; - - @override - Widget buildView( - {required NavigationViewInitializationOptions initializationOptions, - required MapReadyCallback onMapReady}) { - return const Text('Navigation'); - } - - @override - Future createNavigationSession() async { - return sessionApi.createNavigationSession(); - } - - @override - Future isInitialized() async { - return sessionApi.isInitialized(); - } - - @override - Future cleanup() async { - return sessionApi.cleanup(); - } - - @override - Stream getNavigationSessionEventStream() { - return NavigationSessionEventStream(); - } - - @override - Future areTermsAccepted() async { - return sessionApi.areTermsAccepted(); - } - - @override - Future resetTermsAccepted() async { - sessionApi.resetTermsAccepted(); - } - - @override - Future setMapType({required int viewId, required MapType mapType}) { - throw UnimplementedError(); - } - - @override - Future setMapStyle(int viewId, String? styleJson) async { - return viewApi.setMapStyle(viewId, styleJson ?? '[]'); - } - - @override - Future getCameraPosition({required int viewId}) async { - final CameraPositionDto position = viewApi.getCameraPosition(viewId); - return cameraPositionfromDto(position); - } - - @override - Future getVisibleRegion({required int viewId}) async { - throw UnimplementedError(); - } - - @override - Future animateCamera( - {required int viewId, - required CameraUpdate cameraUpdate, - required int? duration, - AnimationFinishedCallback? onFinished}) async { - switch (cameraUpdate.type) { - case CameraUpdateType.cameraPosition: - unawaited(viewApi - .animateCameraToCameraPosition(viewId, - cameraPositionToDto(cameraUpdate.cameraPosition!), duration) - .then((bool success) => - onFinished != null ? onFinished(success) : null)); - case CameraUpdateType.latLng: - unawaited(viewApi - .animateCameraToLatLng( - viewId, cameraUpdate.latLng!.toDto(), duration) - .then((bool success) => - onFinished != null ? onFinished(success) : null)); - case CameraUpdateType.latLngBounds: - unawaited(viewApi - .animateCameraToLatLngBounds( - viewId, - latLngBoundsToDto(cameraUpdate.bounds!), - cameraUpdate.padding, - duration) - .then((bool success) => - onFinished != null ? onFinished(success) : null)); - case CameraUpdateType.latLngZoom: - unawaited(viewApi - .animateCameraToLatLngZoom(viewId, cameraUpdate.latLng!.toDto(), - cameraUpdate.zoom, duration) - .then((bool success) => - onFinished != null ? onFinished(success) : null)); - case CameraUpdateType.scrollBy: - unawaited(viewApi - .animateCameraByScroll(viewId, cameraUpdate.scrollByDx, - cameraUpdate.scrollByDy, duration) - .then((bool success) => - onFinished != null ? onFinished(success) : null)); - case CameraUpdateType.zoomBy: - unawaited(viewApi - .animateCameraByZoom(viewId, cameraUpdate.zoomByAmount, - cameraUpdate.focus?.dx, cameraUpdate.focus?.dy, duration) - .then((bool success) => - onFinished != null ? onFinished(success) : null)); - case CameraUpdateType.zoomTo: - return unawaited(viewApi - .animateCameraToZoom(viewId, cameraUpdate.zoom, duration) - .then((bool success) => - onFinished != null ? onFinished(success) : null)); - } - } - - @override - Future moveCamera( - {required int viewId, required CameraUpdate cameraUpdate}) async { - switch (cameraUpdate.type) { - case CameraUpdateType.cameraPosition: - return viewApi.moveCameraToCameraPosition( - viewId, cameraPositionToDto(cameraUpdate.cameraPosition!)); - case CameraUpdateType.latLng: - return viewApi.moveCameraToLatLng(viewId, cameraUpdate.latLng!.toDto()); - case CameraUpdateType.latLngBounds: - return viewApi.moveCameraToLatLngBounds(viewId, - latLngBoundsToDto(cameraUpdate.bounds!), cameraUpdate.padding); - case CameraUpdateType.latLngZoom: - return viewApi.moveCameraToLatLngZoom( - viewId, cameraUpdate.latLng!.toDto(), cameraUpdate.zoom); - case CameraUpdateType.scrollBy: - return viewApi.moveCameraByScroll( - viewId, cameraUpdate.scrollByDx, cameraUpdate.scrollByDy); - case CameraUpdateType.zoomBy: - return viewApi.moveCameraByZoom(viewId, cameraUpdate.zoomByAmount, - cameraUpdate.focus?.dx, cameraUpdate.focus?.dy); - case CameraUpdateType.zoomTo: - return viewApi.moveCameraToZoom(viewId, cameraUpdate.zoom); - } - } - - @override - Future followMyLocation( - {required int viewId, - required CameraPerspective perspective, - required double? zoomLevel}) async { - return viewApi.followMyLocation( - viewId, cameraPerspectiveTypeToDto(perspective), zoomLevel); - } - - @override - Future enableMyLocation( - {required int viewId, required bool enabled}) async { - return viewApi.enableMyLocation(viewId, enabled); - } - - @override - Future enableMyLocationButton( - {required int viewId, required bool enabled}) async { - return viewApi.enableMyLocationButton(viewId, enabled); - } - - @override - Future enableZoomGestures( - {required int viewId, required bool enabled}) async { - return viewApi.enableZoomGestures(viewId, enabled); - } - - @override - Future enableZoomControls( - {required int viewId, required bool enabled}) async { - return viewApi.enableZoomControls(viewId, enabled); - } - - @override - Future enableCompass( - {required int viewId, required bool enabled}) async { - return viewApi.enableCompass(viewId, enabled); - } - - @override - Future enableRotateGestures( - {required int viewId, required bool enabled}) async { - return viewApi.enableRotateGestures(viewId, enabled); - } - - @override - Future enableScrollGestures( - {required int viewId, required bool enabled}) async { - return viewApi.enableScrollGestures(viewId, enabled); - } - - @override - Future enableScrollGesturesDuringRotateOrZoom( - {required int viewId, required bool enabled}) async { - return viewApi.enableScrollGesturesDuringRotateOrZoom(viewId, enabled); - } - - @override - Future enableTiltGestures( - {required int viewId, required bool enabled}) async { - return viewApi.enableTiltGestures(viewId, enabled); - } - - @override - Future enableMapToolbar( - {required int viewId, required bool enabled}) async { - return viewApi.enableMapToolbar(viewId, enabled); - } - - @override - Future enableTraffic( - {required int viewId, required bool enabled}) async { - return viewApi.enableTraffic(viewId, enabled); - } - - @override - Future isMyLocationEnabled({required int viewId}) async { - return viewApi.isMyLocationEnabled(viewId); - } - - @override - Future isMyLocationButtonEnabled({required int viewId}) async { - return viewApi.isMyLocationButtonEnabled(viewId); - } - - @override - Future isZoomGesturesEnabled({required int viewId}) async { - return viewApi.isZoomGesturesEnabled(viewId); - } - - @override - Future isZoomControlsEnabled({required int viewId}) async { - return viewApi.isZoomControlsEnabled(viewId); - } - - @override - Future isCompassEnabled({required int viewId}) async { - return viewApi.isCompassEnabled(viewId); - } - - @override - Future isRotateGesturesEnabled({required int viewId}) async { - return viewApi.isRotateGesturesEnabled(viewId); - } - - @override - Future isScrollGesturesEnabled({required int viewId}) async { - return viewApi.isScrollGesturesEnabled(viewId); - } - - @override - Future isScrollGesturesEnabledDuringRotateOrZoom( - {required int viewId}) async { - return viewApi.isScrollGesturesEnabledDuringRotateOrZoom(viewId); - } - - @override - Future isTiltGesturesEnabled({required int viewId}) async { - return viewApi.isTiltGesturesEnabled(viewId); - } - - @override - Future isMapToolbarEnabled({required int viewId}) async { - return viewApi.isMapToolbarEnabled(viewId); - } - - @override - Future isTrafficEnabled({required int viewId}) async { - return viewApi.isTrafficEnabled(viewId); - } - - @override - Future showTermsAndConditionsDialog(String title, String companyName, - bool shouldOnlyShowDriverAwarenessDisclaimer) { - return sessionApi.showTermsAndConditionsDialog( - title, companyName, shouldOnlyShowDriverAwarenessDisclaimer); - } - - @override - Future clearDestinations() async { - sessionApi.clearDestinations(); - } - - @override - Future continueToNextDestination() async { - final NavigationWaypointDto? waypoint = - sessionApi.continueToNextDestination(); - if (waypoint == null) { - return null; - } - return navigationWaypointFromDto(waypoint); - } - - @override - Future getCurrentTimeAndDistance() async { - final NavigationTimeAndDistanceDto timeAndDistance = - sessionApi.getCurrentTimeAndDistance(); - return navigationTimeAndDistanceFromDto(timeAndDistance); - } - - @override - Future setAudioGuidance( - NavigationAudioGuidanceSettings settings) async { - return sessionApi - .setAudioGuidance(navigationAudioGuidanceSettingsToDto(settings)); - } - - @override - Future setDestinations(Destinations msg) async { - final RouteStatusDto status = - await sessionApi.setDestinations(navigationDestinationToDto(msg)); - return navigationRouteStatusFromDto(status); - } - - @override - Future startGuidance() async { - return sessionApi.startGuidance(); - } - - @override - Future stopGuidance() async { - return sessionApi.stopGuidance(); - } - - @override - Future simulateLocationsAlongExistingRoute() async { - return sessionApi.simulateLocationsAlongExistingRoute(); - } - - @override - Future allowBackgroundLocationUpdates(bool allow) async { - return sessionApi.allowBackgroundLocationUpdates(allow); - } - - @override - Stream - getNavigationRoadSnappedLocationEventStream() { - throw UnimplementedError(); - } - - @override - Stream getNavigationSpeedingEventStream() { - throw UnimplementedError(); - } - - @override - Future pauseSimulation() async { - return sessionApi.pauseSimulation(); - } - - @override - Future resumeSimulation() async { - return sessionApi.resumeSimulation(); - } - - @override - Future setUserLocation(LatLng location) async { - return sessionApi.setUserLocation(latLngToDto(location)); - } - - @override - Future simulateLocationsAlongExistingRouteWithOptions( - SimulationOptions options) async { - return sessionApi.simulateLocationsAlongExistingRouteWithOptions( - simulationOptionsToDto(options)); - } - - @override - Future simulateLocationsAlongNewRoute( - List waypoints) async { - final RouteStatusDto routeStatus = - await sessionApi.simulateLocationsAlongNewRoute(waypoints.map( - (NavigationWaypoint e) { - return navigationWaypointToDto(e); - }, - ).toList()); - return navigationRouteStatusFromDto(routeStatus); - } - - @override - Future - simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( - List waypoints, - RoutingOptions routingOptions, - SimulationOptions simulationOptions) async { - final RouteStatusDto routeStatus = await sessionApi - .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( - waypoints.map( - (NavigationWaypoint e) { - return navigationWaypointToDto(e); - }, - ).toList(), - routingOptionsToDto(routingOptions), - simulationOptionsToDto(simulationOptions), - ); - return navigationRouteStatusFromDto(routeStatus); - } - - @override - Future - simulateLocationsAlongNewRouteWithRoutingOptions( - List waypoints, - RoutingOptions routingOptions) async { - final RouteStatusDto routeStatus = - await sessionApi.simulateLocationsAlongNewRouteWithRoutingOptions( - waypoints.map( - (NavigationWaypoint e) { - return navigationWaypointToDto(e); - }, - ).toList(), - routingOptionsToDto(routingOptions), - ); - return navigationRouteStatusFromDto(routeStatus); - } - - @override - Future removeUserLocation() async { - return sessionApi.removeUserLocation(); - } - - @override - Stream getNavigationOnArrivalEventStream() { - throw UnimplementedError(); - } - - @override - Stream getNavigationOnReroutingEventStream() { - throw UnimplementedError(); - } - - @override - Stream getNavigationOnRouteChangedEventStream() { - throw UnimplementedError(); - } - - @override - Stream - getNavigationRemainingTimeOrDistanceChangedEventStream() { - throw UnimplementedError(); - } - - @override - Stream getNavigationTrafficUpdatedEventStream() { - throw UnimplementedError(); - } - - @override - Stream - getNavigationRoadSnappedRawLocationEventStream() { - throw UnimplementedError(); - } - - @override - Stream - getNavigationRecenterButtonClickedEventStream({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future enableNavigationFooter( - {required int viewId, required bool enabled}) async { - throw UnimplementedError(); - } - - @override - Future enableNavigationHeader( - {required int viewId, required bool enabled}) async { - throw UnimplementedError(); - } - - @override - Future enableNavigationTripProgressBar( - {required int viewId, required bool enabled}) async { - throw UnimplementedError(); - } - - @override - Future enableNavigationUI( - {required int viewId, required bool enabled}) async { - throw UnimplementedError(); - } - - @override - Future enableRecenterButton( - {required int viewId, required bool enabled}) async { - throw UnimplementedError(); - } - - @override - Future enableSpeedLimitIcon( - {required int viewId, required bool enable}) { - throw UnimplementedError(); - } - - @override - Future enableSpeedometer({required int viewId, required bool enable}) { - throw UnimplementedError(); - } - - @override - Future enableIncidentCards( - {required int viewId, required bool enable}) { - throw UnimplementedError(); - } - - @override - Future showRouteOverview({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future disableRoadSnappedLocationUpdates() { - throw UnimplementedError(); - } - - @override - Future enableRoadSnappedLocationUpdates() { - throw UnimplementedError(); - } - - @override - Future> getMarkers({required int viewId}) async { - return viewApi - .getMarkers(viewId) - .map((MarkerDto? e) => e?.toMarker()) - .toList(); - } - - @override - Future> addMarkers( - {required int viewId, required List markerOptions}) async { - int markerIndex = 0; - final List markerDtos = markerOptions.map((MarkerOptions e) { - final MarkerDto marker = - MarkerDto(markerId: 'Marker_$markerIndex', options: e.toDto()); - markerIndex += 1; - return marker; - }).toList(); - - final List addedMarkers = - viewApi.addMarkers(viewId, markerDtos); - return addedMarkers.map((MarkerDto? e) => e?.toMarker()).toList(); - } - - @override - Future clearMarkers({required int viewId}) async { - return viewApi.clearMarkers(viewId); - } - - @override - Future clear({required int viewId}) async { - return viewApi.clear(viewId); - } - - @override - Future removeMarkers( - {required int viewId, required List markers}) async { - return viewApi.removeMarkers( - viewId, markers.map((Marker e) => e.toDto()).toList()); - } - - @override - Future> updateMarkers( - {required int viewId, required List markers}) async { - final List updatedMarkers = viewApi.updateMarkers( - viewId, markers.map((Marker e) => e.toDto()).toList()); - - return updatedMarkers - .whereType() - .map((MarkerDto m) => m.toMarker()) - .toList(); - } - - @override - Stream getMarkerDragEventStream({required int viewId}) { - return NavigationViewMarkerDragEventStream(); - } - - @override - Stream getMarkerEventStream({required int viewId}) { - return NavigationViewMarkerEventStream(); - } - - @override - Future getMapType({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isGuidanceRunning() { - throw UnimplementedError(); - } - - @override - Future isNavigationUIEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isIncidentCardsEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isNavigationFooterEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isNavigationHeaderEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isNavigationTripProgressBarEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isRecenterButtonEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isSpeedLimitIconEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future isSpeedometerEnabled({required int viewId}) { - throw UnimplementedError(); - } - - @override - Stream getMapClickEventStream({required int viewId}) { - throw UnimplementedError(); - } - - @override - Stream getMapLongClickEventStream({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future getCurrentRouteSegment() { - throw UnimplementedError(); - } - - @override - Future getMyLocation({required int viewId}) { - throw UnimplementedError(); - } - - @override - Future> getRouteSegments() { - throw UnimplementedError(); - } - - @override - Future> getTraveledRoute() { - throw UnimplementedError(); - } - - @override - Future> addPolygons( - {required int viewId, - required List polygonOptions}) async { - int polygonIndex = 0; - final List polygons = polygonOptions.map((PolygonOptions e) { - final PolygonDto polygon = - PolygonDto(polygonId: 'Polygon_$polygonIndex', options: e.toDto()); - polygonIndex += 1; - return polygon; - }).toList(); - - final List addedPolygons = - viewApi.addPolygons(viewId, polygons); - return addedPolygons.map((PolygonDto? e) => e?.toPolygon()).toList(); - } - - @override - Future clearPolygons({required int viewId}) async { - return viewApi.clearPolygons(viewId); - } - - @override - Stream getPolygonClickedEventStream( - {required int viewId}) { - return PolygonDtoClickedEventStream(); - } - - @override - Future removePolygons( - {required int viewId, required List polygons}) async { - return viewApi.removePolygons( - viewId, polygons.map((Polygon e) => e.toDto()).toList()); - } - - @override - Future> updatePolygons( - {required int viewId, required List polygons}) async { - final List updatedPolygons = viewApi.updatePolygons( - viewId, polygons.map((Polygon e) => e.toDto()).toList()); - - return updatedPolygons - .whereType() - .map((PolygonDto e) => e.toPolygon()) - .toList(); - } - - @override - Future> getPolygons({required int viewId}) async { - final List polygons = viewApi.getPolygons(viewId); - - return polygons - .whereType() - .map((PolygonDto e) => e.toPolygon()) - .toList(); - } - - @override - Future awaitMapReady({required int viewId}) { - return viewApi.awaitMapReady(viewId); - } - - @override - Future> addPolylines( - {required int viewId, - required List polylineOptions}) async { - int polylineIndex = 0; - final List polylines = - polylineOptions.map((PolylineOptions e) { - final PolylineDto polyline = PolylineDto( - polylineId: 'Polyline_$polylineIndex', - options: e.toPolylineOptionsDto()); - polylineIndex += 1; - return polyline; - }).toList(); - - final List addedPolylines = - viewApi.addPolylines(viewId, polylines); - return addedPolylines.map((PolylineDto? e) => e?.toPolyline()).toList(); - } - - @override - Future clearPolylines({required int viewId}) async { - return viewApi.clearPolylines(viewId); - } - - @override - Stream getPolylineDtoClickedEventStream( - {required int viewId}) { - return NavigationViewPolylineClickedEventStream(); - } - - @override - Future> getPolylines({required int viewId}) async { - final List polylines = viewApi.getPolylines(viewId); - - return polylines - .whereType() - .map((PolylineDto e) => e.toPolyline()) - .toList(); - } - - @override - Future removePolylines( - {required int viewId, required List polylines}) async { - return viewApi.removePolylines(viewId, - polylines.map((Polyline e) => e.toNavigationViewPolyline()).toList()); - } - - @override - Future> updatePolylines( - {required int viewId, required List polylines}) async { - final List updatedPolylines = viewApi.updatePolylines(viewId, - polylines.map((Polyline e) => e.toNavigationViewPolyline()).toList()); - - return updatedPolylines - .whereType() - .map((PolylineDto e) => e.toPolyline()) - .toList(); - } - - @override - Future registerRemainingTimeOrDistanceChangedListener( - int remainingTimeThresholdSeconds, - int remainingDistanceThresholdMeters) async { - return sessionApi.registerRemainingTimeOrDistanceChangedListener( - remainingTimeThresholdSeconds, remainingDistanceThresholdMeters); - } - - @override - Future> getCircles({required int viewId}) async { - final List circles = viewApi.getCircles(viewId); - - return circles - .whereType() - .map((CircleDto e) => e.toCircle()) - .toList(); - } - - @override - Future> addCircles( - {required int viewId, required List options}) async { - int circleIndex = 0; - final List circles = options.map((CircleOptions e) { - final CircleDto circle = - CircleDto(circleId: 'Circle_$circleIndex', options: e.toDto()); - circleIndex += 1; - return circle; - }).toList(); - - final List addedCircles = viewApi.addCircles(viewId, circles); - return addedCircles.map((CircleDto? e) => e?.toCircle()).toList(); - } - - @override - Future clearCircles({required int viewId}) async { - return viewApi.clearCircles(viewId); - } - - @override - Future removeCircles( - {required int viewId, required List circles}) async { - return viewApi.removeCircles( - viewId, circles.map((Circle e) => e.toDto()).toList()); - } - - @override - Future> updateCircles( - {required int viewId, required List circles}) async { - final List updatedCircles = viewApi.updateCircles( - viewId, circles.map((Circle e) => e.toDto()).toList()); - - return updatedCircles - .whereType() - .map((CircleDto e) => e.toCircle()) - .toList(); - } - - @override - Stream getCircleDtoClickedEventStream( - {required int viewId}) { - return CircleDtoClickedEventStream(); - } -} diff --git a/test/google_maps_navigation_test.dart b/test/google_maps_navigation_test.dart index 1d83551..3337f4e 100644 --- a/test/google_maps_navigation_test.dart +++ b/test/google_maps_navigation_test.dart @@ -16,30 +16,38 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_navigation/google_maps_navigation.dart'; import 'package:google_maps_navigation/src/google_maps_navigation_platform_interface.dart'; -import 'package:google_maps_navigation/src/types/util/marker_conversion.dart'; -import 'package:google_maps_navigation/src/types/util/polygon_conversion.dart'; +import 'package:google_maps_navigation/src/method_channel/method_channel.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; - -import 'google_maps_navigation_mock.dart'; import 'google_maps_navigation_test.mocks.dart'; import 'messages_test.g.dart'; -@GenerateMocks([TestNavigationSessionApi, TestNavigationViewApi]) +@GenerateMocks([ + TestNavigationSessionApi, + TestNavigationViewApi, + TestImageRegistryApi +]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); void onViewCreated(GoogleNavigationViewController controller) {} late MockTestNavigationSessionApi sessionMockApi; late MockTestNavigationViewApi viewMockApi; + late MockTestImageRegistryApi imageRegistryMockApi; + + final List platforms = + [ + GoogleMapsNavigationAndroid(), + GoogleMapsNavigationIOS(), + ]; setUp(() { sessionMockApi = MockTestNavigationSessionApi(); viewMockApi = MockTestNavigationViewApi(); + imageRegistryMockApi = MockTestImageRegistryApi(); TestNavigationSessionApi.setup(sessionMockApi); TestNavigationViewApi.setup(viewMockApi); - GoogleMapsNavigationPlatform.instance = MockGoogleMapsNavigationPlatform( - sessionApi: sessionMockApi, viewApi: viewMockApi); + TestImageRegistryApi.setup(imageRegistryMockApi); }); void verifyEnabled(VerificationResult result, bool enabled) { @@ -47,1106 +55,1159 @@ void main() { expect(enabledOut, enabled); } - testWidgets('renders Google Maps Navigation', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: GoogleMapsNavigationView( - onViewCreated: onViewCreated, - ), - ), - ); - expect(find.byType(GoogleMapsNavigationView), findsOneWidget); - }); - - group('Navigation view API', () { - test('Await map ready api call', () async { - // Mock api response - when(viewMockApi.awaitMapReady(any)) - .thenAnswer((Invocation _) async => ()); - - // Await map ready - await GoogleMapsNavigationPlatform.instance.awaitMapReady(viewId: 0); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.awaitMapReady(captureAny)); - final int viewId = result.captured[0] as int; - - // Verify message - expect(viewId, 0); - }); - - testWidgets('Test camera position and modes', (WidgetTester tester) async { - const int viewIdIn = 1; - final GoogleNavigationViewController controller = - GoogleNavigationViewController(viewIdIn); - - final LatLngDto targetIn = LatLngDto(latitude: 5.0, longitude: 6.0); - final CameraPositionDto positionIn = - CameraPositionDto(bearing: 20, target: targetIn, tilt: 30, zoom: 2.0); - when(viewMockApi.getCameraPosition(any)).thenReturn(positionIn); - - // Get camera position - - final CameraPosition positionOut = await controller.getCameraPosition(); - final VerificationResult result = - verify(viewMockApi.getCameraPosition(captureAny)); - final int viewIdOut = result.captured[0] as int; - - expect(viewIdOut, viewIdIn); - expect(positionIn.bearing, positionOut.bearing); - expect(positionIn.target.latitude, positionOut.target.latitude); - expect(positionIn.target.longitude, positionOut.target.longitude); - expect(positionIn.tilt, positionOut.tilt); - expect(positionIn.zoom, positionOut.zoom); - - // Follow my position without zoom level - - CameraPerspective perspectiveIn = CameraPerspective.topDownHeadingUp; - await controller.followMyLocation(perspectiveIn); - VerificationResult perspectiveResult = verify( - viewMockApi.followMyLocation(captureAny, captureAny, captureAny)); - CameraPerspectiveDto perspective = - perspectiveResult.captured[1] as CameraPerspectiveDto; - double? zoomLevelOut = perspectiveResult.captured[2] as double?; - expect(cameraPerspectiveTypeToDto(perspectiveIn), perspective); - expect(zoomLevelOut, null); - - // Follow my position with zoom level - - perspectiveIn = CameraPerspective.topDownHeadingUp; - const double zoomLevelIn = 5.0; - await controller.followMyLocation(perspectiveIn, zoomLevel: zoomLevelIn); - perspectiveResult = verify( - viewMockApi.followMyLocation(captureAny, captureAny, captureAny)); - perspective = perspectiveResult.captured[1] as CameraPerspectiveDto; - zoomLevelOut = perspectiveResult.captured[2] as double?; - expect(cameraPerspectiveTypeToDto(perspectiveIn), perspective); - expect(zoomLevelOut, 5.0); - }); - - testWidgets('Test camera animations', (WidgetTester tester) async { - const int viewId = 1; - final GoogleNavigationViewController controller = - GoogleNavigationViewController(viewId); - - final LatLngDto targetIn = LatLngDto(latitude: 5.0, longitude: 6.0); - final CameraPositionDto positionIn = - CameraPositionDto(bearing: 20, target: targetIn, tilt: 30, zoom: 2.0); - when(viewMockApi.getCameraPosition(any)).thenReturn(positionIn); - - // Animate camera to camera position - - const LatLng latLngIn = LatLng(latitude: 5.0, longitude: 6.0); - const int durationMsec = 600; - const Duration duration = Duration(milliseconds: durationMsec); - - when(viewMockApi.animateCameraToCameraPosition(any, any, any)) - .thenAnswer((Invocation _) async => true); - - const CameraPosition positionIn2 = - CameraPosition(bearing: 20, target: latLngIn, tilt: 30, zoom: 2.0); - await controller.animateCamera( - CameraUpdate.newCameraPosition(positionIn2), - duration: duration, onFinished: (bool success) { - expect(success, true); - }); - - VerificationResult result = verify(viewMockApi - .animateCameraToCameraPosition(captureAny, captureAny, captureAny)); - final CameraPositionDto positionOut2 = - result.captured[1] as CameraPositionDto; - - expect(viewId, result.captured[0] as int); - expect(positionIn2.bearing, positionOut2.bearing); - expect(positionIn2.target.latitude, positionOut2.target.latitude); - expect(positionIn2.target.longitude, positionOut2.target.longitude); - expect(positionIn2.tilt, positionOut2.tilt); - expect(positionIn2.zoom, positionOut2.zoom); - expect(durationMsec, result.captured[2] as int); - - // Animate camera to co-ordinates - - when(viewMockApi.animateCameraToLatLng(any, any, any)) - .thenAnswer((Invocation _) async => true); - - await controller.animateCamera(CameraUpdate.newLatLng(latLngIn), - duration: duration, onFinished: (bool success) { - expect(success, true); + for (final GoogleMapsNavigationPlatform platform in platforms) { + group(platform.runtimeType, () { + setUp(() { + GoogleMapsNavigationPlatform.instance = platform; }); - result = verify(viewMockApi.animateCameraToLatLng( - captureAny, captureAny, captureAny)); - final LatLngDto latLngOut = result.captured[1] as LatLngDto; - - expect(viewId, result.captured[0] as int); - expect(latLngIn.latitude, latLngOut.latitude); - expect(latLngIn.longitude, latLngOut.longitude); - expect(durationMsec, result.captured[2] as int); - - // Animate camera to co-ordinate bounds - - when(viewMockApi.animateCameraToLatLngBounds(any, any, any, any)) - .thenAnswer((Invocation _) async => true); - - const LatLng latLngIn2 = LatLng(latitude: 7.0, longitude: 9.0); - final LatLngBounds boundsIn = - LatLngBounds(southwest: latLngIn, northeast: latLngIn2); - const double paddingIn = 2.0; - await controller.animateCamera( - CameraUpdate.newLatLngBounds(boundsIn, padding: paddingIn), - duration: duration, onFinished: (bool success) { - expect(success, true); + testWidgets('renders Google Maps Navigation', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: GoogleMapsNavigationView( + onViewCreated: onViewCreated, + ), + ), + ); + expect(find.byType(GoogleMapsNavigationView), findsOneWidget); }); - result = verify(viewMockApi.animateCameraToLatLngBounds( - captureAny, captureAny, captureAny, captureAny)); - final LatLngBoundsDto boundsOut = result.captured[1] as LatLngBoundsDto; - - expect(viewId, result.captured[0] as int); - expect(boundsIn.southwest.latitude, boundsOut.southwest.latitude); - expect(boundsIn.southwest.longitude, boundsOut.southwest.longitude); - expect(boundsIn.northeast.latitude, boundsOut.northeast.latitude); - expect(boundsIn.northeast.longitude, boundsOut.northeast.longitude); - expect(paddingIn, result.captured[2] as double); - expect(durationMsec, result.captured[3] as int); - - // Animate camera to co-ordinates and zoom + group('Navigation view API', () { + test('Await map ready api call', () async { + // Mock api response + when(viewMockApi.awaitMapReady(any)) + .thenAnswer((Invocation _) async => ()); + + // Await map ready + await GoogleMapsNavigationPlatform.instance.awaitMapReady(viewId: 0); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.awaitMapReady(captureAny)); + final int viewId = result.captured[0] as int; + + // Verify message + expect(viewId, 0); + }); + + testWidgets('Test camera position and modes', + (WidgetTester tester) async { + const int viewIdIn = 1; + final GoogleNavigationViewController controller = + GoogleNavigationViewController(viewIdIn); + + final LatLngDto targetIn = LatLngDto(latitude: 5.0, longitude: 6.0); + final CameraPositionDto positionIn = CameraPositionDto( + bearing: 20, target: targetIn, tilt: 30, zoom: 2.0); + when(viewMockApi.getCameraPosition(any)).thenReturn(positionIn); + + // Get camera position + + final CameraPosition positionOut = + await controller.getCameraPosition(); + final VerificationResult result = + verify(viewMockApi.getCameraPosition(captureAny)); + final int viewIdOut = result.captured[0] as int; + + expect(viewIdOut, viewIdIn); + expect(positionIn.bearing, positionOut.bearing); + expect(positionIn.target.latitude, positionOut.target.latitude); + expect(positionIn.target.longitude, positionOut.target.longitude); + expect(positionIn.tilt, positionOut.tilt); + expect(positionIn.zoom, positionOut.zoom); + + // Follow my position without zoom level + + CameraPerspective perspectiveIn = CameraPerspective.topDownHeadingUp; + await controller.followMyLocation(perspectiveIn); + VerificationResult perspectiveResult = verify( + viewMockApi.followMyLocation(captureAny, captureAny, captureAny)); + CameraPerspectiveDto perspective = + perspectiveResult.captured[1] as CameraPerspectiveDto; + double? zoomLevelOut = perspectiveResult.captured[2] as double?; + expect(perspectiveIn.toDto(), perspective); + expect(zoomLevelOut, null); + + // Follow my position with zoom level + + perspectiveIn = CameraPerspective.topDownHeadingUp; + const double zoomLevelIn = 5.0; + await controller.followMyLocation(perspectiveIn, + zoomLevel: zoomLevelIn); + perspectiveResult = verify( + viewMockApi.followMyLocation(captureAny, captureAny, captureAny)); + perspective = perspectiveResult.captured[1] as CameraPerspectiveDto; + zoomLevelOut = perspectiveResult.captured[2] as double?; + expect(perspectiveIn.toDto(), perspective); + expect(zoomLevelOut, 5.0); + }); + + testWidgets('Test camera animations', (WidgetTester tester) async { + const int viewId = 1; + final GoogleNavigationViewController controller = + GoogleNavigationViewController(viewId); + + final LatLngDto targetIn = LatLngDto(latitude: 5.0, longitude: 6.0); + final CameraPositionDto positionIn = CameraPositionDto( + bearing: 20, target: targetIn, tilt: 30, zoom: 2.0); + when(viewMockApi.getCameraPosition(any)).thenReturn(positionIn); + + // Animate camera to camera position + + const LatLng latLngIn = LatLng(latitude: 5.0, longitude: 6.0); + const int durationMsec = 600; + const Duration duration = Duration(milliseconds: durationMsec); + + when(viewMockApi.animateCameraToCameraPosition(any, any, any)) + .thenAnswer((Invocation _) async => true); + + const CameraPosition positionIn2 = + CameraPosition(bearing: 20, target: latLngIn, tilt: 30); + await controller.animateCamera( + CameraUpdate.newCameraPosition(positionIn2), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + VerificationResult result = verify( + viewMockApi.animateCameraToCameraPosition( + captureAny, captureAny, captureAny)); + final CameraPositionDto positionOut2 = + result.captured[1] as CameraPositionDto; + + expect(viewId, result.captured[0] as int); + expect(positionIn2.bearing, positionOut2.bearing); + expect(positionIn2.target.latitude, positionOut2.target.latitude); + expect(positionIn2.target.longitude, positionOut2.target.longitude); + expect(positionIn2.tilt, positionOut2.tilt); + expect(positionIn2.zoom, positionOut2.zoom); + expect(durationMsec, result.captured[2] as int); + + // Animate camera to co-ordinates + + when(viewMockApi.animateCameraToLatLng(any, any, any)) + .thenAnswer((Invocation _) async => true); + + await controller.animateCamera(CameraUpdate.newLatLng(latLngIn), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraToLatLng( + captureAny, captureAny, captureAny)); + final LatLngDto latLngOut = result.captured[1] as LatLngDto; + + expect(viewId, result.captured[0] as int); + expect(latLngIn.latitude, latLngOut.latitude); + expect(latLngIn.longitude, latLngOut.longitude); + expect(durationMsec, result.captured[2] as int); + + // Animate camera to co-ordinate bounds + + when(viewMockApi.animateCameraToLatLngBounds(any, any, any, any)) + .thenAnswer((Invocation _) async => true); + + const LatLng latLngIn2 = LatLng(latitude: 7.0, longitude: 9.0); + final LatLngBounds boundsIn = + LatLngBounds(southwest: latLngIn, northeast: latLngIn2); + const double paddingIn = 2.0; + await controller.animateCamera( + CameraUpdate.newLatLngBounds(boundsIn, padding: paddingIn), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraToLatLngBounds( + captureAny, captureAny, captureAny, captureAny)); + final LatLngBoundsDto boundsOut = + result.captured[1] as LatLngBoundsDto; + + expect(viewId, result.captured[0] as int); + expect(boundsIn.southwest.latitude, boundsOut.southwest.latitude); + expect(boundsIn.southwest.longitude, boundsOut.southwest.longitude); + expect(boundsIn.northeast.latitude, boundsOut.northeast.latitude); + expect(boundsIn.northeast.longitude, boundsOut.northeast.longitude); + expect(paddingIn, result.captured[2] as double); + expect(durationMsec, result.captured[3] as int); + + // Animate camera to co-ordinates and zoom + + when(viewMockApi.animateCameraToLatLngZoom(any, any, any, any)) + .thenAnswer((Invocation _) async => true); + + const double zoomIn = 4.0; + await controller.animateCamera( + CameraUpdate.newLatLngZoom(latLngIn, zoomIn), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraToLatLngZoom( + captureAny, captureAny, captureAny, captureAny)); + final LatLngDto latLngOut2 = result.captured[1] as LatLngDto; + + expect(viewId, result.captured[0] as int); + expect(latLngIn.latitude, latLngOut2.latitude); + expect(latLngIn.longitude, latLngOut2.longitude); + expect(zoomIn, result.captured[2] as double); + expect(durationMsec, result.captured[3] as int); + + // Animate camera by scrolling + + when(viewMockApi.animateCameraByScroll(any, any, any, any)) + .thenAnswer((Invocation _) async => true); + + const double scrollByDx = 4.0; + const double scrollByDy = 5.0; + await controller.animateCamera( + CameraUpdate.scrollBy(scrollByDx, scrollByDy), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraByScroll( + captureAny, captureAny, captureAny, captureAny)); + + expect(viewId, result.captured[0] as int); + expect(scrollByDx, result.captured[1] as double); + expect(scrollByDy, result.captured[2] as double); + expect(durationMsec, result.captured[3] as int); + + // Animate camera by zoom + + when(viewMockApi.animateCameraByZoom(any, any, any, any, any)) + .thenAnswer((Invocation _) async => true); + + const double zoomBy = 2.0; + const Offset focusIn = Offset(3.0, 4.0); + await controller.animateCamera( + CameraUpdate.zoomBy(zoomBy, focus: focusIn), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraByZoom( + captureAny, captureAny, captureAny, captureAny, captureAny)); + + expect(viewId, result.captured[0] as int); + expect(zoomBy, result.captured[1] as double); + expect(focusIn.dx, result.captured[2] as double); + expect(focusIn.dy, result.captured[3] as double); + expect(durationMsec, result.captured[4] as int); + + // Animate camera by zooming in + + // Calls the same viewMockApi.animateCameraByZoom() that has already been mocked + await controller.animateCamera(CameraUpdate.zoomIn(), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraByZoom( + captureAny, captureAny, captureAny, captureAny, captureAny)); + + expect(viewId, result.captured[0] as int); + expect(1.0 /* zoom by 1.0 */, result.captured[1] as double); + expect(null /* focus dx not used */, result.captured[2] as double?); + expect(null /* focus xy not used */, result.captured[3] as double?); + expect(durationMsec, result.captured[4] as int); + + // Animate camera by zooming out + + await controller.animateCamera(CameraUpdate.zoomOut(), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraByZoom( + captureAny, captureAny, captureAny, captureAny, captureAny)); + + expect(viewId, result.captured[0] as int); + expect(-1.0, result.captured[1] as double); + expect(null /* focus dx not used */, result.captured[2] as double?); + expect(null /* focus xy not used */, result.captured[3] as double?); + expect(durationMsec, result.captured[4] as int); + + // Animate camera by a fixed zoom value - when(viewMockApi.animateCameraToLatLngZoom(any, any, any, any)) - .thenAnswer((Invocation _) async => true); + when(viewMockApi.animateCameraToZoom(any, any, any)) + .thenAnswer((Invocation _) async => true); + + await controller.animateCamera(CameraUpdate.zoomTo(zoomIn), + duration: duration, onFinished: (bool success) { + expect(success, true); + }); + + result = verify(viewMockApi.animateCameraToZoom( + captureAny, captureAny, captureAny)); + expect(viewId, result.captured[0] as int); + expect(zoomIn, result.captured[1] as double); + expect(durationMsec, result.captured[2] as int); + }); - const double zoomIn = 4.0; - await controller.animateCamera( - CameraUpdate.newLatLngZoom(latLngIn, zoomIn), - duration: duration, onFinished: (bool success) { - expect(success, true); - }); + testWidgets('Test camera movement', (WidgetTester tester) async { + const int viewId = 1; + final GoogleNavigationViewController controller = + GoogleNavigationViewController(viewId); - result = verify(viewMockApi.animateCameraToLatLngZoom( - captureAny, captureAny, captureAny, captureAny)); - final LatLngDto latLngOut2 = result.captured[1] as LatLngDto; + final LatLngDto targetIn = LatLngDto(latitude: 5.0, longitude: 6.0); + final CameraPositionDto positionIn = CameraPositionDto( + bearing: 20, target: targetIn, tilt: 30, zoom: 2.0); + when(viewMockApi.getCameraPosition(any)).thenReturn(positionIn); - expect(viewId, result.captured[0] as int); - expect(latLngIn.latitude, latLngOut2.latitude); - expect(latLngIn.longitude, latLngOut2.longitude); - expect(zoomIn, result.captured[2] as double); - expect(durationMsec, result.captured[3] as int); + // Move camera to camera position - // Animate camera by scrolling + const LatLng latLngIn = LatLng(latitude: 5.0, longitude: 6.0); - when(viewMockApi.animateCameraByScroll(any, any, any, any)) - .thenAnswer((Invocation _) async => true); + const CameraPosition positionIn2 = + CameraPosition(bearing: 20, target: latLngIn, tilt: 30); + await controller + .moveCamera(CameraUpdate.newCameraPosition(positionIn2)); - const double scrollByDx = 4.0; - const double scrollByDy = 5.0; - await controller.animateCamera( - CameraUpdate.scrollBy(scrollByDx, scrollByDy), - duration: duration, onFinished: (bool success) { - expect(success, true); - }); + VerificationResult result = verify( + viewMockApi.moveCameraToCameraPosition(captureAny, captureAny)); + final CameraPositionDto positionOut2 = + result.captured[1] as CameraPositionDto; - result = verify(viewMockApi.animateCameraByScroll( - captureAny, captureAny, captureAny, captureAny)); + expect(viewId, result.captured[0] as int); + expect(positionIn2.bearing, positionOut2.bearing); + expect(positionIn2.target.latitude, positionOut2.target.latitude); + expect(positionIn2.target.longitude, positionOut2.target.longitude); + expect(positionIn2.tilt, positionOut2.tilt); + expect(positionIn2.zoom, positionOut2.zoom); - expect(viewId, result.captured[0] as int); - expect(scrollByDx, result.captured[1] as double); - expect(scrollByDy, result.captured[2] as double); - expect(durationMsec, result.captured[3] as int); + // Move camera to co-ordinates - // Animate camera by zoom + await controller.moveCamera(CameraUpdate.newLatLng(latLngIn)); - when(viewMockApi.animateCameraByZoom(any, any, any, any, any)) - .thenAnswer((Invocation _) async => true); + result = + verify(viewMockApi.moveCameraToLatLng(captureAny, captureAny)); + final LatLngDto latLngOut = result.captured[1] as LatLngDto; - const double zoomBy = 2.0; - const Offset focusIn = Offset(3.0, 4.0); - await controller.animateCamera( - CameraUpdate.zoomBy(zoomBy, focus: focusIn), - duration: duration, onFinished: (bool success) { - expect(success, true); - }); + expect(viewId, result.captured[0] as int); + expect(latLngIn.latitude, latLngOut.latitude); + expect(latLngIn.longitude, latLngOut.longitude); - result = verify(viewMockApi.animateCameraByZoom( - captureAny, captureAny, captureAny, captureAny, captureAny)); + // Move camera to co-ordinate bounds - expect(viewId, result.captured[0] as int); - expect(zoomBy, result.captured[1] as double); - expect(focusIn.dx, result.captured[2] as double); - expect(focusIn.dy, result.captured[3] as double); - expect(durationMsec, result.captured[4] as int); + const LatLng latLngIn2 = LatLng(latitude: 7.0, longitude: 9.0); - // Animate camera by zooming in + final LatLngBounds boundsIn = + LatLngBounds(southwest: latLngIn, northeast: latLngIn2); + const double paddingIn = 2.0; + await controller.moveCamera( + CameraUpdate.newLatLngBounds(boundsIn, padding: paddingIn)); - // Calls the same viewMockApi.animateCameraByZoom() that has already been mocked - await controller.animateCamera(CameraUpdate.zoomIn(), duration: duration, - onFinished: (bool success) { - expect(success, true); - }); + result = verify(viewMockApi.moveCameraToLatLngBounds( + captureAny, captureAny, captureAny)); + final LatLngBoundsDto boundsOut = + result.captured[1] as LatLngBoundsDto; - result = verify(viewMockApi.animateCameraByZoom( - captureAny, captureAny, captureAny, captureAny, captureAny)); + expect(viewId, result.captured[0] as int); + expect(boundsIn.southwest.latitude, boundsOut.southwest.latitude); + expect(boundsIn.southwest.longitude, boundsOut.southwest.longitude); + expect(boundsIn.northeast.latitude, boundsOut.northeast.latitude); + expect(boundsIn.northeast.longitude, boundsOut.northeast.longitude); + expect(paddingIn, result.captured[2] as double); - expect(viewId, result.captured[0] as int); - expect(1.0 /* zoom by 1.0 */, result.captured[1] as double); - expect(null /* focus dx not used */, result.captured[2] as double?); - expect(null /* focus xy not used */, result.captured[3] as double?); - expect(durationMsec, result.captured[4] as int); + // Move camera to co-ordinates and zoom - // Animate camera by zooming out + const double zoomIn = 4.0; + await controller + .moveCamera(CameraUpdate.newLatLngZoom(latLngIn, zoomIn)); - await controller.animateCamera(CameraUpdate.zoomOut(), duration: duration, - onFinished: (bool success) { - expect(success, true); - }); + result = verify(viewMockApi.moveCameraToLatLngZoom( + captureAny, captureAny, captureAny)); + final LatLngDto latLngOut2 = result.captured[1] as LatLngDto; - result = verify(viewMockApi.animateCameraByZoom( - captureAny, captureAny, captureAny, captureAny, captureAny)); + expect(viewId, result.captured[0] as int); + expect(latLngIn.latitude, latLngOut2.latitude); + expect(latLngIn.longitude, latLngOut2.longitude); + expect(zoomIn, result.captured[2] as double); - expect(viewId, result.captured[0] as int); - expect(-1.0, result.captured[1] as double); - expect(null /* focus dx not used */, result.captured[2] as double?); - expect(null /* focus xy not used */, result.captured[3] as double?); - expect(durationMsec, result.captured[4] as int); + // Move camera by scrolling - // Animate camera by a fixed zoom value + const double scrollByDx = 4.0; + const double scrollByDy = 5.0; + await controller + .moveCamera(CameraUpdate.scrollBy(scrollByDx, scrollByDy)); - when(viewMockApi.animateCameraToZoom(any, any, any)) - .thenAnswer((Invocation _) async => true); + result = verify(viewMockApi.moveCameraByScroll( + captureAny, captureAny, captureAny)); - await controller.animateCamera(CameraUpdate.zoomTo(zoomIn), - duration: duration, onFinished: (bool success) { - expect(success, true); + expect(viewId, result.captured[0] as int); + expect(scrollByDx, result.captured[1] as double); + expect(scrollByDy, result.captured[2] as double); + + // Move camera by zoom + + const double zoomBy = 2.0; + const Offset focusIn = Offset(3.0, 4.0); + await controller + .moveCamera(CameraUpdate.zoomBy(zoomBy, focus: focusIn)); + + result = verify(viewMockApi.moveCameraByZoom( + captureAny, captureAny, captureAny, captureAny)); + + expect(viewId, result.captured[0] as int); + expect(zoomBy, result.captured[1] as double); + expect(focusIn.dx, result.captured[2] as double); + expect(focusIn.dy, result.captured[3] as double); + + // Move camera by zooming in + + await controller.moveCamera(CameraUpdate.zoomIn()); + + result = verify(viewMockApi.moveCameraByZoom( + captureAny, captureAny, captureAny, captureAny)); + + expect(viewId, result.captured[0] as int); + expect(1.0 /* zoom by 1.0 */, result.captured[1] as double); + expect(null /* focus dx not used */, result.captured[2] as double?); + expect(null /* focus xy not used */, result.captured[3] as double?); + + // Move camera by zooming out + + await controller.moveCamera(CameraUpdate.zoomOut()); + + result = verify(viewMockApi.moveCameraByZoom( + captureAny, captureAny, captureAny, captureAny)); + + expect(viewId, result.captured[0] as int); + expect(-1.0, result.captured[1] as double); + expect(null /* focus dx not used */, result.captured[2] as double?); + expect(null /* focus xy not used */, result.captured[3] as double?); + + // Move camera by a fixed zoom value + + await controller.moveCamera(CameraUpdate.zoomTo(zoomIn)); + + result = verify(viewMockApi.moveCameraToZoom(captureAny, captureAny)); + expect(viewId, result.captured[0] as int); + expect(zoomIn, result.captured[1] as double); + }); + + testWidgets('Test map UI elements', (WidgetTester tester) async { + const int viewId = 1; + final GoogleNavigationViewController controller = + GoogleNavigationViewController(viewId); + + // Mock UI element visibility getters. + when(viewMockApi.isMyLocationEnabled(any)).thenReturn(true); + when(viewMockApi.isMyLocationButtonEnabled(any)).thenReturn(true); + when(viewMockApi.isZoomGesturesEnabled(any)).thenReturn(true); + when(viewMockApi.isZoomControlsEnabled(any)).thenReturn(true); + when(viewMockApi.isCompassEnabled(any)).thenReturn(true); + when(viewMockApi.isRotateGesturesEnabled(any)).thenReturn(true); + when(viewMockApi.isScrollGesturesEnabled(any)).thenReturn(true); + when(viewMockApi.isScrollGesturesEnabledDuringRotateOrZoom(any)) + .thenReturn(true); + when(viewMockApi.isTiltGesturesEnabled(any)).thenReturn(true); + when(viewMockApi.isMapToolbarEnabled(any)).thenReturn(true); + + // Test UI element visibility getter return values. + expect(await controller.isMyLocationEnabled(), true); + expect(await controller.settings.isMyLocationButtonEnabled(), true); + expect(await controller.settings.isZoomGesturesEnabled(), true); + expect(await controller.settings.isZoomControlsEnabled(), true); + expect(await controller.settings.isCompassEnabled(), true); + expect(await controller.settings.isRotateGesturesEnabled(), true); + expect(await controller.settings.isScrollGesturesEnabled(), true); + expect( + await controller.settings + .isScrollGesturesEnabledDuringRotateOrZoom(), + true); + expect(await controller.settings.isTiltGesturesEnabled(), true); + expect(await controller.settings.isMapToolbarEnabled(), true); + + // Verify calls went through to the platform side. + verify(viewMockApi.isMyLocationEnabled(captureAny)); + verify(viewMockApi.isMyLocationButtonEnabled(captureAny)); + verify(viewMockApi.isZoomGesturesEnabled(captureAny)); + verify(viewMockApi.isZoomControlsEnabled(captureAny)); + verify(viewMockApi.isCompassEnabled(captureAny)); + verify(viewMockApi.isRotateGesturesEnabled(captureAny)); + verify(viewMockApi.isScrollGesturesEnabled(captureAny)); + verify(viewMockApi + .isScrollGesturesEnabledDuringRotateOrZoom(captureAny)); + verify(viewMockApi.isTiltGesturesEnabled(captureAny)); + verify(viewMockApi.isMapToolbarEnabled(captureAny)); + + // Call UI element visibility setters. + await controller.setMyLocationEnabled(true); + await controller.settings.setMyLocationButtonEnabled(true); + await controller.settings.setZoomGesturesEnabled(true); + await controller.settings.setZoomControlsEnabled(true); + await controller.settings.setCompassEnabled(true); + await controller.settings.setRotateGesturesEnabled(true); + await controller.settings.setScrollGesturesEnabled(true); + await controller.settings + .setScrollGesturesDuringRotateOrZoomEnabled(true); + await controller.settings.setTiltGesturesEnabled(true); + await controller.settings.setMapToolbarEnabled(true); + + // Verify getters went through with the right parameters. + verifyEnabled( + verify(viewMockApi.setMyLocationEnabled(captureAny, captureAny)), + true); + verifyEnabled( + verify(viewMockApi.setMyLocationButtonEnabled( + captureAny, captureAny)), + true); + verifyEnabled( + verify( + viewMockApi.setZoomGesturesEnabled(captureAny, captureAny)), + true); + verifyEnabled( + verify( + viewMockApi.setZoomControlsEnabled(captureAny, captureAny)), + true); + verifyEnabled( + verify(viewMockApi.setCompassEnabled(captureAny, captureAny)), + true); + verifyEnabled( + verify( + viewMockApi.setRotateGesturesEnabled(captureAny, captureAny)), + true); + verifyEnabled( + verify( + viewMockApi.setScrollGesturesEnabled(captureAny, captureAny)), + true); + verifyEnabled( + verify(viewMockApi.setScrollGesturesDuringRotateOrZoomEnabled( + captureAny, captureAny)), + true); + verifyEnabled( + verify( + viewMockApi.setTiltGesturesEnabled(captureAny, captureAny)), + true); + verifyEnabled( + verify(viewMockApi.setMapToolbarEnabled(captureAny, captureAny)), + true); + }); }); - result = verify( - viewMockApi.animateCameraToZoom(captureAny, captureAny, captureAny)); - expect(viewId, result.captured[0] as int); - expect(zoomIn, result.captured[1] as double); - expect(durationMsec, result.captured[2] as int); - }); - - testWidgets('Test camera movement', (WidgetTester tester) async { - const int viewId = 1; - final GoogleNavigationViewController controller = - GoogleNavigationViewController(viewId); - - final LatLngDto targetIn = LatLngDto(latitude: 5.0, longitude: 6.0); - final CameraPositionDto positionIn = - CameraPositionDto(bearing: 20, target: targetIn, tilt: 30, zoom: 2.0); - when(viewMockApi.getCameraPosition(any)).thenReturn(positionIn); - - // Move camera to camera position - - const LatLng latLngIn = LatLng(latitude: 5.0, longitude: 6.0); - - const CameraPosition positionIn2 = - CameraPosition(bearing: 20, target: latLngIn, tilt: 30, zoom: 2.0); - await controller.moveCamera(CameraUpdate.newCameraPosition(positionIn2)); - - VerificationResult result = verify( - viewMockApi.moveCameraToCameraPosition(captureAny, captureAny)); - final CameraPositionDto positionOut2 = - result.captured[1] as CameraPositionDto; - - expect(viewId, result.captured[0] as int); - expect(positionIn2.bearing, positionOut2.bearing); - expect(positionIn2.target.latitude, positionOut2.target.latitude); - expect(positionIn2.target.longitude, positionOut2.target.longitude); - expect(positionIn2.tilt, positionOut2.tilt); - expect(positionIn2.zoom, positionOut2.zoom); - - // Move camera to co-ordinates - - await controller.moveCamera(CameraUpdate.newLatLng(latLngIn)); - - result = verify(viewMockApi.moveCameraToLatLng(captureAny, captureAny)); - final LatLngDto latLngOut = result.captured[1] as LatLngDto; - - expect(viewId, result.captured[0] as int); - expect(latLngIn.latitude, latLngOut.latitude); - expect(latLngIn.longitude, latLngOut.longitude); - - // Move camera to co-ordinate bounds - - const LatLng latLngIn2 = LatLng(latitude: 7.0, longitude: 9.0); - - final LatLngBounds boundsIn = - LatLngBounds(southwest: latLngIn, northeast: latLngIn2); - const double paddingIn = 2.0; - await controller.moveCamera( - CameraUpdate.newLatLngBounds(boundsIn, padding: paddingIn)); - - result = verify(viewMockApi.moveCameraToLatLngBounds( - captureAny, captureAny, captureAny)); - final LatLngBoundsDto boundsOut = result.captured[1] as LatLngBoundsDto; - - expect(viewId, result.captured[0] as int); - expect(boundsIn.southwest.latitude, boundsOut.southwest.latitude); - expect(boundsIn.southwest.longitude, boundsOut.southwest.longitude); - expect(boundsIn.northeast.latitude, boundsOut.northeast.latitude); - expect(boundsIn.northeast.longitude, boundsOut.northeast.longitude); - expect(paddingIn, result.captured[2] as double); - - // Move camera to co-ordinates and zoom - - const double zoomIn = 4.0; - await controller.moveCamera(CameraUpdate.newLatLngZoom(latLngIn, zoomIn)); - - result = verify(viewMockApi.moveCameraToLatLngZoom( - captureAny, captureAny, captureAny)); - final LatLngDto latLngOut2 = result.captured[1] as LatLngDto; - - expect(viewId, result.captured[0] as int); - expect(latLngIn.latitude, latLngOut2.latitude); - expect(latLngIn.longitude, latLngOut2.longitude); - expect(zoomIn, result.captured[2] as double); - - // Move camera by scrolling - - const double scrollByDx = 4.0; - const double scrollByDy = 5.0; - await controller - .moveCamera(CameraUpdate.scrollBy(scrollByDx, scrollByDy)); - - result = verify( - viewMockApi.moveCameraByScroll(captureAny, captureAny, captureAny)); - - expect(viewId, result.captured[0] as int); - expect(scrollByDx, result.captured[1] as double); - expect(scrollByDy, result.captured[2] as double); - - // Move camera by zoom - - const double zoomBy = 2.0; - const Offset focusIn = Offset(3.0, 4.0); - await controller.moveCamera(CameraUpdate.zoomBy(zoomBy, focus: focusIn)); - - result = verify(viewMockApi.moveCameraByZoom( - captureAny, captureAny, captureAny, captureAny)); - - expect(viewId, result.captured[0] as int); - expect(zoomBy, result.captured[1] as double); - expect(focusIn.dx, result.captured[2] as double); - expect(focusIn.dy, result.captured[3] as double); - - // Move camera by zooming in - - await controller.moveCamera(CameraUpdate.zoomIn()); - - result = verify(viewMockApi.moveCameraByZoom( - captureAny, captureAny, captureAny, captureAny)); - - expect(viewId, result.captured[0] as int); - expect(1.0 /* zoom by 1.0 */, result.captured[1] as double); - expect(null /* focus dx not used */, result.captured[2] as double?); - expect(null /* focus xy not used */, result.captured[3] as double?); - - // Move camera by zooming out - - await controller.moveCamera(CameraUpdate.zoomOut()); - - result = verify(viewMockApi.moveCameraByZoom( - captureAny, captureAny, captureAny, captureAny)); - - expect(viewId, result.captured[0] as int); - expect(-1.0, result.captured[1] as double); - expect(null /* focus dx not used */, result.captured[2] as double?); - expect(null /* focus xy not used */, result.captured[3] as double?); - - // Move camera by a fixed zoom value - - await controller.moveCamera(CameraUpdate.zoomTo(zoomIn)); - - result = verify(viewMockApi.moveCameraToZoom(captureAny, captureAny)); - expect(viewId, result.captured[0] as int); - expect(zoomIn, result.captured[1] as double); - }); - - testWidgets('Test map UI elements', (WidgetTester tester) async { - const int viewId = 1; - final GoogleNavigationViewController controller = - GoogleNavigationViewController(viewId); - - // Mock UI element visibility getters. - when(viewMockApi.isMyLocationEnabled(any)).thenReturn(true); - when(viewMockApi.isMyLocationButtonEnabled(any)).thenReturn(true); - when(viewMockApi.isZoomGesturesEnabled(any)).thenReturn(true); - when(viewMockApi.isZoomControlsEnabled(any)).thenReturn(true); - when(viewMockApi.isCompassEnabled(any)).thenReturn(true); - when(viewMockApi.isRotateGesturesEnabled(any)).thenReturn(true); - when(viewMockApi.isScrollGesturesEnabled(any)).thenReturn(true); - when(viewMockApi.isScrollGesturesEnabledDuringRotateOrZoom(any)) - .thenReturn(true); - when(viewMockApi.isTiltGesturesEnabled(any)).thenReturn(true); - when(viewMockApi.isMapToolbarEnabled(any)).thenReturn(true); - - // Test UI element visibility getter return values. - expect(await controller.isMyLocationEnabled(), true); - expect(await controller.settings.isMyLocationButtonEnabled(), true); - expect(await controller.settings.isZoomGesturesEnabled(), true); - expect(await controller.settings.isZoomControlsEnabled(), true); - expect(await controller.settings.isCompassEnabled(), true); - expect(await controller.settings.isRotateGesturesEnabled(), true); - expect(await controller.settings.isScrollGesturesEnabled(), true); - expect( - await controller.settings.isScrollGesturesEnabledDuringRotateOrZoom(), - true); - expect(await controller.settings.isTiltGesturesEnabled(), true); - expect(await controller.settings.isMapToolbarEnabled(), true); - - // Verify calls went through to the platform side. - verify(viewMockApi.isMyLocationEnabled(captureAny)); - verify(viewMockApi.isMyLocationButtonEnabled(captureAny)); - verify(viewMockApi.isZoomGesturesEnabled(captureAny)); - verify(viewMockApi.isZoomControlsEnabled(captureAny)); - verify(viewMockApi.isCompassEnabled(captureAny)); - verify(viewMockApi.isRotateGesturesEnabled(captureAny)); - verify(viewMockApi.isScrollGesturesEnabled(captureAny)); - verify(viewMockApi.isScrollGesturesEnabledDuringRotateOrZoom(captureAny)); - verify(viewMockApi.isTiltGesturesEnabled(captureAny)); - verify(viewMockApi.isMapToolbarEnabled(captureAny)); - - // Call UI element visibility setters. - await controller.enableMyLocation(enabled: true); - await controller.settings.enableMyLocationButton(enabled: true); - await controller.settings.enableZoomGestures(enabled: true); - await controller.settings.enableZoomControls(enabled: true); - await controller.settings.enableCompass(enabled: true); - await controller.settings.enableRotateGestures(enabled: true); - await controller.settings.enableScrollGestures(enabled: true); - await controller.settings - .enableScrollGesturesDuringRotateOrZoom(enabled: true); - await controller.settings.enableTiltGestures(enabled: true); - await controller.settings.enableMapToolbar(enabled: true); - - // Verify getters went through with the right parameters. - verifyEnabled( - verify(viewMockApi.enableMyLocation(captureAny, captureAny)), true); - verifyEnabled( - verify(viewMockApi.enableMyLocationButton(captureAny, captureAny)), - true); - verifyEnabled( - verify(viewMockApi.enableZoomGestures(captureAny, captureAny)), true); - verifyEnabled( - verify(viewMockApi.enableZoomControls(captureAny, captureAny)), true); - verifyEnabled( - verify(viewMockApi.enableCompass(captureAny, captureAny)), true); - verifyEnabled( - verify(viewMockApi.enableRotateGestures(captureAny, captureAny)), - true); - verifyEnabled( - verify(viewMockApi.enableScrollGestures(captureAny, captureAny)), - true); - verifyEnabled( - verify(viewMockApi.enableScrollGesturesDuringRotateOrZoom( - captureAny, captureAny)), - true); - verifyEnabled( - verify(viewMockApi.enableTiltGestures(captureAny, captureAny)), true); - verifyEnabled( - verify(viewMockApi.enableMapToolbar(captureAny, captureAny)), true); - }); - }); - - group('Navigation session API', () { - test('Test terms and conditions flows', () async { - // Show terms and conditions. - const String title = 'Title'; - const String companyName = 'Temp co.'; - const bool showDriverAwareness = true; - when(sessionMockApi.showTermsAndConditionsDialog(any, any, any)) - .thenAnswer((Invocation _) async => true); - final bool accepted = - await GoogleMapsNavigator.showTermsAndConditionsDialog( - title, companyName, - shouldOnlyShowDriverAwarenessDisclaimer: showDriverAwareness); - expect(accepted, true); - - final VerificationResult result = verify(sessionMockApi - .showTermsAndConditionsDialog(captureAny, captureAny, captureAny)); - expect(result.captured[0] as String, title); - expect(result.captured[1] as String, companyName); - expect(result.captured[2] as bool, showDriverAwareness); - - // Reset terms and conditions. - await GoogleMapsNavigator.resetTermsAccepted(); - verify(sessionMockApi.resetTermsAccepted()); - - // Are terms accepted. - when(sessionMockApi.areTermsAccepted()).thenReturn(true); - await GoogleMapsNavigator.areTermsAccepted(); - expect(sessionMockApi.areTermsAccepted(), true); - }); - - test('Test navigation session', () async { - // Initialize session and session controller. - await GoogleMapsNavigator.initializeNavigationSession(); - - // Start/stop guidance. - - await GoogleMapsNavigator.startGuidance(); - verify(sessionMockApi.startGuidance()); - - await GoogleMapsNavigator.stopGuidance(); - verify(sessionMockApi.stopGuidance()); - - // Location updates. - await GoogleMapsNavigator.allowBackgroundLocationUpdates(true); - final VerificationResult backgroundResult = - verify(sessionMockApi.allowBackgroundLocationUpdates(captureAny)); - expect(backgroundResult.captured[0] as bool, true); - - // Continue to the next destination. - - final NavigationWaypointDto waypointIn = NavigationWaypointDto( - title: 'Title', - target: LatLngDto(latitude: 0.4, longitude: 0.5), - placeID: 'id', - preferSameSideOfRoad: true, - preferredSegmentHeading: 50); - - when(sessionMockApi.continueToNextDestination()).thenReturn(waypointIn); - final NavigationWaypoint? waypointOut = - await GoogleMapsNavigator.continueToNextDestination(); - expect(waypointOut, isNotNull); - if (waypointOut != null) { - expect(waypointIn.title, waypointOut.title); - expect(waypointIn.target?.latitude, waypointOut.target?.latitude); - expect(waypointIn.target?.longitude, waypointOut.target?.longitude); - expect(waypointIn.placeID, waypointOut.placeID); - expect( - waypointIn.preferSameSideOfRoad, waypointOut.preferSameSideOfRoad); - expect(waypointIn.preferredSegmentHeading, - waypointOut.preferredSegmentHeading); - } - - // Set destinations. - - final NavigationDisplayOptions navigationDisplayOptionsIn = - NavigationDisplayOptions( - showDestinationMarkers: false, - showStopSigns: true, - showTrafficLights: true); - - final RoutingOptions routingOptionsIn = RoutingOptions( - alternateRoutesStrategy: NavigationAlternateRoutesStrategy.all, - routingStrategy: NavigationRoutingStrategy.defaultBest, - targetDistanceMeters: [1, 1, 1], - avoidFerries: true, - avoidHighways: true, - avoidTolls: true, - locationTimeoutMs: 5000, - ); - - final NavigationWaypoint destinationWaypointIn = - NavigationWaypoint.withLatLngTarget( - title: 'title', - target: const LatLng(latitude: 5.0, longitude: 6.0), - ); - final Destinations destinationIn = Destinations( - waypoints: [destinationWaypointIn], - displayOptions: navigationDisplayOptionsIn, - routingOptions: routingOptionsIn); - - const RouteStatusDto statusIn = RouteStatusDto.quotaExceeded; - when(sessionMockApi.setDestinations(any)) - .thenAnswer((Invocation _) async => statusIn); - - final NavigationRouteStatus statusOut = - await GoogleMapsNavigator.setDestinations(destinationIn); - expect(statusOut, NavigationRouteStatus.quotaExceeded); - final VerificationResult result = - verify(sessionMockApi.setDestinations(captureAny)); - final DestinationsDto destinationOut = - result.captured[0] as DestinationsDto; - final NavigationWaypointDto? destinationWaypointOut = - destinationOut.waypoints[0]; - expect(destinationWaypointOut, isNotNull); - if (destinationWaypointOut != null) { - expect(destinationWaypointIn.title, destinationWaypointOut.title); - expect(destinationWaypointIn.target?.latitude, - destinationWaypointOut.target?.latitude); - expect(destinationWaypointIn.target?.longitude, - destinationWaypointOut.target?.longitude); - } - expect(destinationIn.displayOptions.showDestinationMarkers, - destinationOut.displayOptions.showDestinationMarkers); - expect(destinationIn.displayOptions.showStopSigns, - destinationOut.displayOptions.showStopSigns); - expect(destinationIn.displayOptions.showTrafficLights, - destinationOut.displayOptions.showTrafficLights); - - expect(AlternateRoutesStrategyDto.all, - destinationOut.routingOptions!.alternateRoutesStrategy); - expect(RoutingStrategyDto.defaultBest, - destinationOut.routingOptions!.routingStrategy); - expect(destinationIn.routingOptions!.avoidFerries, - destinationOut.routingOptions!.avoidFerries); - expect(destinationIn.routingOptions!.avoidHighways, - destinationOut.routingOptions!.avoidHighways); - expect(destinationIn.routingOptions!.avoidTolls, - destinationOut.routingOptions!.avoidTolls); - expect(destinationIn.routingOptions!.locationTimeoutMs, - destinationOut.routingOptions!.locationTimeoutMs); - - // Get current time and distance. - final NavigationTimeAndDistanceDto timeAndDistanceIn = - NavigationTimeAndDistanceDto(time: 5.0, distance: 6.0); - when(sessionMockApi.getCurrentTimeAndDistance()) - .thenReturn(timeAndDistanceIn); - final NavigationTimeAndDistance timeAndDistanceOut = - await GoogleMapsNavigator.getCurrentTimeAndDistance(); - expect(timeAndDistanceIn.time, timeAndDistanceOut.time); - expect(timeAndDistanceIn.distance, timeAndDistanceOut.distance); - - final NavigationAudioGuidanceSettings settingsIn = - NavigationAudioGuidanceSettings( - isBluetoothAudioEnabled: true, - isVibrationEnabled: true, - guidanceType: NavigationAudioGuidanceType.alertsOnly); - - // Set audio guidance. - await GoogleMapsNavigator.setAudioGuidance(settingsIn); - final VerificationResult settingsResult = - verify(sessionMockApi.setAudioGuidance(captureAny)); - final NavigationAudioGuidanceSettingsDto settingsOut = - settingsResult.captured[0] as NavigationAudioGuidanceSettingsDto; - expect(settingsIn.isBluetoothAudioEnabled, - settingsOut.isBluetoothAudioEnabled); - expect(settingsIn.isVibrationEnabled, settingsOut.isVibrationEnabled); - expect(settingsOut.guidanceType, AudioGuidanceTypeDto.alertsOnly); - }); - - test('Test navigation simulator', () async { - // Pause/resume simulation. - - await GoogleMapsNavigator.simulator.pauseSimulation(); - verify(sessionMockApi.pauseSimulation()); - - await GoogleMapsNavigator.simulator.resumeSimulation(); - verify(sessionMockApi.resumeSimulation()); - - // Control user location. - - const LatLng pointIn = LatLng(latitude: 0.4, longitude: 0.5); - await GoogleMapsNavigator.simulator.setUserLocation(pointIn); - final VerificationResult result = - verify(sessionMockApi.setUserLocation(captureAny)); - final LatLngDto pointOut = result.captured[0] as LatLngDto; - expect(pointIn.latitude, pointOut.latitude); - expect(pointIn.longitude, pointOut.longitude); - - await GoogleMapsNavigator.simulator.removeUserLocation(); - verify(sessionMockApi.removeUserLocation()); - - // Simulate locations. - - await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRoute(); - verify(sessionMockApi.simulateLocationsAlongExistingRoute()); - - await GoogleMapsNavigator.simulator.simulateLocationsAlongExistingRoute(); - verify(sessionMockApi.simulateLocationsAlongExistingRoute()); - - final SimulationOptions simOptionsIn = - SimulationOptions(speedMultiplier: 5.5); - await GoogleMapsNavigator.simulator - .simulateLocationsAlongExistingRouteWithOptions(simOptionsIn); - final VerificationResult optionsResult = verify(sessionMockApi - .simulateLocationsAlongExistingRouteWithOptions(captureAny)); - final SimulationOptionsDto optionsOut = - optionsResult.captured[0] as SimulationOptionsDto; - expect(simOptionsIn.speedMultiplier, optionsOut.speedMultiplier); - - // Simulate the locations along a new route. - - final NavigationWaypoint waypointIn = NavigationWaypoint.withLatLngTarget( - title: 'title', - target: const LatLng(latitude: 5.0, longitude: 6.0), - ); - - when(sessionMockApi.simulateLocationsAlongNewRoute(any)) - .thenAnswer((Invocation _) async => RouteStatusDto.statusOk); - - final NavigationRouteStatus statusOut = await GoogleMapsNavigator - .simulator - .simulateLocationsAlongNewRoute([waypointIn]); - expect(statusOut, NavigationRouteStatus.statusOk); - - final VerificationResult routeResult = - verify(sessionMockApi.simulateLocationsAlongNewRoute(captureAny)); - final List waypoints = - routeResult.captured[0] as List; - final NavigationWaypointDto waypointOut = waypoints[0]; - expect(waypointIn.title, waypointOut.title); - expect(waypointIn.target?.latitude, waypointOut.target?.latitude); - expect(waypointIn.target?.longitude, waypointOut.target?.longitude); - - // Simulate the locations along a new route with routing options. - - final RoutingOptions routingOptionsIn = RoutingOptions( - alternateRoutesStrategy: NavigationAlternateRoutesStrategy.none, - routingStrategy: NavigationRoutingStrategy.deltaToTargetDistance, - targetDistanceMeters: [1, 1, 1], - travelMode: NavigationTravelMode.taxi, - avoidTolls: true, - avoidFerries: true, - avoidHighways: true, - locationTimeoutMs: 10000); - - when(sessionMockApi.simulateLocationsAlongNewRouteWithRoutingOptions( - any, any)) - .thenAnswer((Invocation _) async => RouteStatusDto.statusCanceled); - - final NavigationRouteStatus statusOut2 = await GoogleMapsNavigator - .simulator - .simulateLocationsAlongNewRouteWithRoutingOptions( - [waypointIn], routingOptionsIn); - expect(statusOut2, NavigationRouteStatus.statusCanceled); - - final VerificationResult routeResult2 = verify( - sessionMockApi.simulateLocationsAlongNewRouteWithRoutingOptions( - captureAny, captureAny)); - - final List waypoints2 = - routeResult2.captured[0] as List; - final NavigationWaypointDto waypointOut2 = waypoints2[0]; - expect(waypointIn.title, waypointOut2.title); - expect(waypointIn.target?.latitude, waypointOut2.target?.latitude); - expect(waypointIn.target?.longitude, waypointOut2.target?.longitude); - - final RoutingOptionsDto routingOptionsOut = - routeResult2.captured[1] as RoutingOptionsDto; - expect(routingOptionsOut.alternateRoutesStrategy, - AlternateRoutesStrategyDto.none); - expect(routingOptionsOut.routingStrategy, - RoutingStrategyDto.deltaToTargetDistance); - expect(routingOptionsOut.targetDistanceMeters, [1, 1, 1]); - expect(routingOptionsOut.travelMode, TravelModeDto.taxi); - expect(routingOptionsOut.avoidTolls, true); - expect(routingOptionsOut.avoidFerries, true); - expect(routingOptionsOut.avoidHighways, true); - expect(routingOptionsOut.locationTimeoutMs, 10000); - - // Simulate the locations along a new route with routing and simulation options. - - when(sessionMockApi + group('Navigation session API', () { + test('Test terms and conditions flows', () async { + // Show terms and conditions. + const String title = 'Title'; + const String companyName = 'Temp co.'; + const bool showDriverAwareness = true; + when(sessionMockApi.showTermsAndConditionsDialog(any, any, any)) + .thenAnswer((Invocation _) async => true); + final bool accepted = + await GoogleMapsNavigator.showTermsAndConditionsDialog( + title, companyName, + shouldOnlyShowDriverAwarenessDisclaimer: showDriverAwareness); + expect(accepted, true); + + final VerificationResult result = verify( + sessionMockApi.showTermsAndConditionsDialog( + captureAny, captureAny, captureAny)); + expect(result.captured[0] as String, title); + expect(result.captured[1] as String, companyName); + expect(result.captured[2] as bool, showDriverAwareness); + + // Reset terms and conditions. + await GoogleMapsNavigator.resetTermsAccepted(); + verify(sessionMockApi.resetTermsAccepted()); + + // Are terms accepted. + when(sessionMockApi.areTermsAccepted()).thenReturn(true); + await GoogleMapsNavigator.areTermsAccepted(); + expect(sessionMockApi.areTermsAccepted(), true); + }); + + test('Test navigation session', () async { + // Initialize session and session controller. + await GoogleMapsNavigator.initializeNavigationSession( + abnormalTerminationReportingEnabled: false); + VerificationResult result = + verify(sessionMockApi.createNavigationSession(captureAny)); + expect(result.captured[0] as bool, false); + + // Start/stop guidance. + + await GoogleMapsNavigator.startGuidance(); + verify(sessionMockApi.startGuidance()); + + await GoogleMapsNavigator.stopGuidance(); + verify(sessionMockApi.stopGuidance()); + + // Location updates. + if (platform is GoogleMapsNavigationIOS) { + await GoogleMapsNavigator.allowBackgroundLocationUpdates(true); + final VerificationResult backgroundResult = verify( + sessionMockApi.allowBackgroundLocationUpdates(captureAny)); + expect(backgroundResult.captured[0] as bool, true); + } else if (platform is GoogleMapsNavigationAndroid) { + expect( + () => GoogleMapsNavigator.allowBackgroundLocationUpdates(true), + throwsUnsupportedError); + } + + // Continue to the next destination. + final NavigationWaypointDto waypointIn = NavigationWaypointDto( + title: 'Title', + target: LatLngDto(latitude: 0.4, longitude: 0.5), + placeID: 'id', + preferSameSideOfRoad: true, + preferredSegmentHeading: 50); + + when(sessionMockApi.continueToNextDestination()) + .thenReturn(waypointIn); + final NavigationWaypoint? waypointOut = + await GoogleMapsNavigator.continueToNextDestination(); + expect(waypointOut, isNotNull); + if (waypointOut != null) { + expect(waypointIn.title, waypointOut.title); + expect(waypointIn.target?.latitude, waypointOut.target?.latitude); + expect(waypointIn.target?.longitude, waypointOut.target?.longitude); + expect(waypointIn.placeID, waypointOut.placeID); + expect(waypointIn.preferSameSideOfRoad, + waypointOut.preferSameSideOfRoad); + expect(waypointIn.preferredSegmentHeading, + waypointOut.preferredSegmentHeading); + } + + // Set destinations. + + final NavigationDisplayOptions navigationDisplayOptionsIn = + NavigationDisplayOptions( + showDestinationMarkers: false, + showStopSigns: true, + showTrafficLights: true); + + final RoutingOptions routingOptionsIn = RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.all, + routingStrategy: NavigationRoutingStrategy.defaultBest, + targetDistanceMeters: [1, 1, 1], + avoidFerries: true, + avoidHighways: true, + avoidTolls: true, + locationTimeoutMs: 5000, + ); + + final NavigationWaypoint destinationWaypointIn = + NavigationWaypoint.withLatLngTarget( + title: 'title', + target: const LatLng(latitude: 5.0, longitude: 6.0), + ); + final Destinations destinationIn = Destinations( + waypoints: [destinationWaypointIn], + displayOptions: navigationDisplayOptionsIn, + routingOptions: routingOptionsIn); + + const RouteStatusDto statusIn = RouteStatusDto.quotaExceeded; + when(sessionMockApi.setDestinations(any)) + .thenAnswer((Invocation _) async => statusIn); + + final NavigationRouteStatus statusOut = + await GoogleMapsNavigator.setDestinations(destinationIn); + expect(statusOut, NavigationRouteStatus.quotaExceeded); + result = verify(sessionMockApi.setDestinations(captureAny)); + final DestinationsDto destinationOut = + result.captured[0] as DestinationsDto; + final NavigationWaypointDto? destinationWaypointOut = + destinationOut.waypoints[0]; + expect(destinationWaypointOut, isNotNull); + if (destinationWaypointOut != null) { + expect(destinationWaypointIn.title, destinationWaypointOut.title); + expect(destinationWaypointIn.target?.latitude, + destinationWaypointOut.target?.latitude); + expect(destinationWaypointIn.target?.longitude, + destinationWaypointOut.target?.longitude); + } + expect(destinationIn.displayOptions.showDestinationMarkers, + destinationOut.displayOptions.showDestinationMarkers); + expect(destinationIn.displayOptions.showStopSigns, + destinationOut.displayOptions.showStopSigns); + expect(destinationIn.displayOptions.showTrafficLights, + destinationOut.displayOptions.showTrafficLights); + + expect(AlternateRoutesStrategyDto.all, + destinationOut.routingOptions!.alternateRoutesStrategy); + expect(RoutingStrategyDto.defaultBest, + destinationOut.routingOptions!.routingStrategy); + expect(destinationIn.routingOptions!.avoidFerries, + destinationOut.routingOptions!.avoidFerries); + expect(destinationIn.routingOptions!.avoidHighways, + destinationOut.routingOptions!.avoidHighways); + expect(destinationIn.routingOptions!.avoidTolls, + destinationOut.routingOptions!.avoidTolls); + expect(destinationIn.routingOptions!.locationTimeoutMs, + destinationOut.routingOptions!.locationTimeoutMs); + + // Get current time and distance. + final NavigationTimeAndDistanceDto timeAndDistanceIn = + NavigationTimeAndDistanceDto(time: 5.0, distance: 6.0); + when(sessionMockApi.getCurrentTimeAndDistance()) + .thenReturn(timeAndDistanceIn); + final NavigationTimeAndDistance timeAndDistanceOut = + await GoogleMapsNavigator.getCurrentTimeAndDistance(); + expect(timeAndDistanceIn.time, timeAndDistanceOut.time); + expect(timeAndDistanceIn.distance, timeAndDistanceOut.distance); + + final NavigationAudioGuidanceSettings settingsIn = + NavigationAudioGuidanceSettings( + isBluetoothAudioEnabled: true, + isVibrationEnabled: true, + guidanceType: NavigationAudioGuidanceType.alertsOnly); + + // Set audio guidance. + await GoogleMapsNavigator.setAudioGuidance(settingsIn); + final VerificationResult settingsResult = + verify(sessionMockApi.setAudioGuidance(captureAny)); + final NavigationAudioGuidanceSettingsDto settingsOut = + settingsResult.captured[0] as NavigationAudioGuidanceSettingsDto; + expect(settingsIn.isBluetoothAudioEnabled, + settingsOut.isBluetoothAudioEnabled); + expect(settingsIn.isVibrationEnabled, settingsOut.isVibrationEnabled); + expect(settingsOut.guidanceType, AudioGuidanceTypeDto.alertsOnly); + }); + + test('Test navigation simulator', () async { + // Pause/resume simulation. + + await GoogleMapsNavigator.simulator.pauseSimulation(); + verify(sessionMockApi.pauseSimulation()); + + await GoogleMapsNavigator.simulator.resumeSimulation(); + verify(sessionMockApi.resumeSimulation()); + + // Control user location. + + const LatLng pointIn = LatLng(latitude: 0.4, longitude: 0.5); + await GoogleMapsNavigator.simulator.setUserLocation(pointIn); + final VerificationResult result = + verify(sessionMockApi.setUserLocation(captureAny)); + final LatLngDto pointOut = result.captured[0] as LatLngDto; + expect(pointIn.latitude, pointOut.latitude); + expect(pointIn.longitude, pointOut.longitude); + + await GoogleMapsNavigator.simulator.removeUserLocation(); + verify(sessionMockApi.removeUserLocation()); + + // Simulate locations. + + await GoogleMapsNavigator.simulator + .simulateLocationsAlongExistingRoute(); + verify(sessionMockApi.simulateLocationsAlongExistingRoute()); + + await GoogleMapsNavigator.simulator + .simulateLocationsAlongExistingRoute(); + verify(sessionMockApi.simulateLocationsAlongExistingRoute()); + + final SimulationOptions simOptionsIn = + SimulationOptions(speedMultiplier: 5.5); + await GoogleMapsNavigator.simulator + .simulateLocationsAlongExistingRouteWithOptions(simOptionsIn); + final VerificationResult optionsResult = verify(sessionMockApi + .simulateLocationsAlongExistingRouteWithOptions(captureAny)); + final SimulationOptionsDto optionsOut = + optionsResult.captured[0] as SimulationOptionsDto; + expect(simOptionsIn.speedMultiplier, optionsOut.speedMultiplier); + + // Simulate the locations along a new route. + + final NavigationWaypoint waypointIn = + NavigationWaypoint.withLatLngTarget( + title: 'title', + target: const LatLng(latitude: 5.0, longitude: 6.0), + ); + + when(sessionMockApi.simulateLocationsAlongNewRoute(any)) + .thenAnswer((Invocation _) async => RouteStatusDto.statusOk); + + final NavigationRouteStatus statusOut = await GoogleMapsNavigator + .simulator + .simulateLocationsAlongNewRoute([waypointIn]); + expect(statusOut, NavigationRouteStatus.statusOk); + + final VerificationResult routeResult = + verify(sessionMockApi.simulateLocationsAlongNewRoute(captureAny)); + final List waypoints = + routeResult.captured[0] as List; + final NavigationWaypointDto? waypointOut = waypoints[0]; + expect(waypointIn.title, waypointOut?.title); + expect(waypointIn.target?.latitude, waypointOut?.target?.latitude); + expect(waypointIn.target?.longitude, waypointOut?.target?.longitude); + + // Simulate the locations along a new route with routing options. + + final RoutingOptions routingOptionsIn = RoutingOptions( + alternateRoutesStrategy: NavigationAlternateRoutesStrategy.none, + routingStrategy: NavigationRoutingStrategy.deltaToTargetDistance, + targetDistanceMeters: [1, 1, 1], + travelMode: NavigationTravelMode.taxi, + avoidTolls: true, + avoidFerries: true, + avoidHighways: true, + locationTimeoutMs: 10000); + + when(sessionMockApi.simulateLocationsAlongNewRouteWithRoutingOptions( + any, any)) + .thenAnswer( + (Invocation _) async => RouteStatusDto.statusCanceled); + + final NavigationRouteStatus statusOut2 = await GoogleMapsNavigator + .simulator + .simulateLocationsAlongNewRouteWithRoutingOptions( + [waypointIn], routingOptionsIn); + expect(statusOut2, NavigationRouteStatus.statusCanceled); + + final VerificationResult routeResult2 = verify( + sessionMockApi.simulateLocationsAlongNewRouteWithRoutingOptions( + captureAny, captureAny)); + + final List waypoints2 = + routeResult2.captured[0] as List; + final NavigationWaypointDto? waypointOut2 = waypoints2[0]; + expect(waypointIn.title, waypointOut2?.title); + expect(waypointIn.target?.latitude, waypointOut2?.target?.latitude); + expect(waypointIn.target?.longitude, waypointOut2?.target?.longitude); + + final RoutingOptionsDto routingOptionsOut = + routeResult2.captured[1] as RoutingOptionsDto; + expect(routingOptionsOut.alternateRoutesStrategy, + AlternateRoutesStrategyDto.none); + expect(routingOptionsOut.routingStrategy, + RoutingStrategyDto.deltaToTargetDistance); + expect(routingOptionsOut.targetDistanceMeters, [1, 1, 1]); + expect(routingOptionsOut.travelMode, TravelModeDto.taxi); + expect(routingOptionsOut.avoidTolls, true); + expect(routingOptionsOut.avoidFerries, true); + expect(routingOptionsOut.avoidHighways, true); + expect(routingOptionsOut.locationTimeoutMs, 10000); + + // Simulate the locations along a new route with routing and simulation options. + + when(sessionMockApi + .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( + any, any, any)) + .thenAnswer((Invocation _) async => RouteStatusDto.statusOk); + final NavigationRouteStatus statusOut3 = await GoogleMapsNavigator + .simulator .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( - any, any, any)) - .thenAnswer((Invocation _) async => RouteStatusDto.statusOk); - final NavigationRouteStatus statusOut3 = await GoogleMapsNavigator - .simulator - .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( - [waypointIn], routingOptionsIn, simOptionsIn); - expect(statusOut3, NavigationRouteStatus.statusOk); - - final VerificationResult routeResult3 = verify(sessionMockApi - .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( - captureAny, captureAny, captureAny)); - - final List waypoints3 = - routeResult3.captured[0] as List; - final NavigationWaypointDto waypointOut3 = waypoints3[0]; - expect(waypointIn.title, waypointOut3.title); - expect(waypointIn.target?.latitude, waypointOut3.target?.latitude); - expect(waypointIn.target?.longitude, waypointOut3.target?.longitude); - - final RoutingOptionsDto routingOptionsOut2 = - routeResult3.captured[1] as RoutingOptionsDto; - expect(routingOptionsOut2.alternateRoutesStrategy, - AlternateRoutesStrategyDto.none); - expect(routingOptionsOut2.routingStrategy, - RoutingStrategyDto.deltaToTargetDistance); - expect(routingOptionsOut2.targetDistanceMeters, [1, 1, 1]); - expect(routingOptionsOut2.travelMode, TravelModeDto.taxi); - expect(routingOptionsOut2.avoidTolls, true); - expect(routingOptionsOut2.avoidFerries, true); - expect(routingOptionsOut2.avoidHighways, true); - expect(routingOptionsOut2.locationTimeoutMs, 10000); - - final SimulationOptionsDto simOptionsOut = - routeResult3.captured[2] as SimulationOptionsDto; - expect(simOptionsIn.speedMultiplier, simOptionsOut.speedMultiplier); - }); - }); - - group('Markers', () { - test('get markers', () async { - // Create marker - const Marker marker = - Marker(markerId: 'Marker_0', options: MarkerOptions()); - - // Mock api response - final MarkerDto messageMarker = - MarkerDto(markerId: 'Marker_0', options: marker.options.toDto()); - when(viewMockApi.getMarkers(any)) - .thenAnswer((Invocation _) => [messageMarker]); - - // Get markers - final List markersOut = - await GoogleMapsNavigationPlatform.instance.getMarkers(viewId: 0); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.getMarkers(captureAny)); - final int viewId = result.captured[0] as int; - - // Verify response polygon options - expect(viewId, 0); - expect(marker.options, markersOut[0]!.options); - }); - - test('add marker', () async { - // Create options - const MarkerOptions optionsIn = MarkerOptions( - alpha: 0.5, - anchor: MarkerAnchor(u: 0.1, v: 0.2), - draggable: true, - flat: true, - consumeTapEvents: true, - position: LatLng(latitude: 50, longitude: 60), - rotation: 70, - infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), - zIndex: 2); - - // Mock api response - final MarkerDto markerIn = - MarkerDto(markerId: 'Marker_0', options: optionsIn.toDto()); - when(viewMockApi.addMarkers(any, any)) - .thenAnswer((Invocation _) => [markerIn]); - - // Add marker - final List markersOut = await GoogleMapsNavigationPlatform - .instance - .addMarkers(viewId: 0, markerOptions: [optionsIn]); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.addMarkers(captureAny, captureAny)); - final List markersInMessage = - result.captured[1] as List; - - // Verify message and response marker options - expect(markerIn.markerId, markersInMessage[0].markerId); - expect(optionsIn, markersInMessage[0].options.toMarkerOptions()); - expect(optionsIn, markersOut[0]!.options); - }); - - test('update marker', () async { - // Create marker - const Marker marker = - Marker(markerId: 'Marker_0', options: MarkerOptions()); - - // Mock api response - final MarkerDto messageMarker = - MarkerDto(markerId: 'Marker_0', options: marker.options.toDto()); - when(viewMockApi.updateMarkers(any, any)) - .thenAnswer((Invocation _) => [messageMarker]); - - // Edit marker - final List markersOut = await GoogleMapsNavigationPlatform - .instance - .updateMarkers(viewId: 0, markers: [marker]); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.updateMarkers(captureAny, captureAny)); - final List markersInMessage = - result.captured[1] as List; - - // Verify message and response marker options - expect(marker.markerId, markersInMessage[0].markerId); - expect(marker.options, markersInMessage[0].options.toMarkerOptions()); - expect(marker.options, markersOut[0]!.options); - }); - - test('remove marker', () async { - // Create marker - const Marker marker = - Marker(markerId: 'Marker_0', options: MarkerOptions()); - - // Mock api response - when(viewMockApi.removeMarkers(any, any)) - .thenAnswer((Invocation _) => ()); - - // Remove marker - await GoogleMapsNavigationPlatform.instance - .removeMarkers(viewId: 0, markers: [marker]); + [waypointIn], + routingOptionsIn, + simOptionsIn); + expect(statusOut3, NavigationRouteStatus.statusOk); - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.removeMarkers(captureAny, captureAny)); - final List markersInMessage = - result.captured[1] as List; - - // Verify message - expect(marker.markerId, markersInMessage[0].markerId); - }); - - test('clear markers', () async { - // Mock api response - when(viewMockApi.clearMarkers(any)) - .thenAnswer((Invocation _) async => ()); - - // Clear map - await GoogleMapsNavigationPlatform.instance.clearMarkers(viewId: 0); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.clearMarkers(captureAny)); - final int viewId = result.captured[0] as int; - - // Verify message - expect(0, viewId); - }); - - test('clear map', () async { - // Mock api response - when(viewMockApi.clear(any)).thenAnswer((Invocation _) async => ()); - - // Clear map - await GoogleMapsNavigationPlatform.instance.clear(viewId: 0); - - // Verify correct message sent from view api - final VerificationResult result = verify(viewMockApi.clear(captureAny)); - final int viewId = result.captured[0] as int; - - // Verify message - expect(0, viewId); - }); - }); - - group('Polygons', () { - test('get polygons', () async { - // Create polygon - const Polygon polygon = - Polygon(polygonId: 'Polygon_0', options: PolygonOptions()); - - // Mock api response - final PolygonDto messagePolygon = - PolygonDto(polygonId: 'Polygon_0', options: polygon.options.toDto()); - when(viewMockApi.getPolygons(any)) - .thenAnswer((Invocation _) => [messagePolygon]); - - // Get polygons - final List polygonsOut = - await GoogleMapsNavigationPlatform.instance.getPolygons(viewId: 0); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.getPolygons(captureAny)); - final int viewId = result.captured[0] as int; - - // Verify response polygon options - expect(viewId, 0); - expect(polygon.options, polygonsOut[0]!.options); - }); - - test('add polygon', () async { - // Create options - const PolygonOptions optionsIn = PolygonOptions( - points: [ - LatLng(latitude: 40.0, longitude: 50.0) - ], - holes: >[ - [LatLng(latitude: 60.0, longitude: 70.0)] - ], - clickable: true, - fillColor: Colors.amber, - geodesic: true, - strokeColor: Colors.cyan, - strokeWidth: 4, - zIndex: 3); - - // Mock api response - final PolygonDto polygonIn = - PolygonDto(polygonId: 'Polygon_0', options: optionsIn.toDto()); - when(viewMockApi.addPolygons(any, any)) - .thenAnswer((Invocation _) => [polygonIn]); - - // Add polygon - final List polygonsOut = await GoogleMapsNavigationPlatform - .instance - .addPolygons(viewId: 0, polygonOptions: [optionsIn]); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.addPolygons(captureAny, captureAny)); - final List polygonsInMessage = - result.captured[1] as List; - - // Verify message and response polygon options - expect(polygonIn.polygonId, polygonsInMessage[0].polygonId); - expect(optionsIn, polygonsInMessage[0].options.toPolygonOptions()); - expect(optionsIn, polygonsOut[0]!.options); - }); - - test('update polygon', () async { - // Create polygon - const Polygon polygon = - Polygon(polygonId: 'Polygon_0', options: PolygonOptions()); - - // Mock api response - final PolygonDto messagePolygon = - PolygonDto(polygonId: 'Polygon_0', options: polygon.options.toDto()); - when(viewMockApi.updatePolygons(any, any)) - .thenAnswer((Invocation _) => [messagePolygon]); - - // Edit polygon - final List polygonsOut = await GoogleMapsNavigationPlatform - .instance - .updatePolygons(viewId: 0, polygons: [polygon]); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.updatePolygons(captureAny, captureAny)); - final List polygonsInMessage = - result.captured[1] as List; - - // Verify message and response polygon options - expect(polygon.polygonId, polygonsInMessage[0].polygonId); - expect(polygon.options, polygonsInMessage[0].options.toPolygonOptions()); - expect(polygon.options, polygonsOut[0]!.options); - }); - - test('remove polygon', () async { - // Create polygon - const Polygon polygon = - Polygon(polygonId: 'Polygon_0', options: PolygonOptions()); - - // Mock api response - when(viewMockApi.removePolygons(any, any)) - .thenAnswer((Invocation _) async => ()); - - // Remove polygon - await GoogleMapsNavigationPlatform.instance - .removePolygons(viewId: 0, polygons: [polygon]); - - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.removePolygons(captureAny, captureAny)); - final List polygonsInMessage = - result.captured[1] as List; - - // Verify message - expect(polygon.polygonId, polygonsInMessage[0].polygonId); - }); - - test('clear polygons', () async { - // Mock api response - when(viewMockApi.clearPolygons(any)) - .thenAnswer((Invocation _) async => ()); - - // Clear map - await GoogleMapsNavigationPlatform.instance.clearPolygons(viewId: 0); + final VerificationResult routeResult3 = verify(sessionMockApi + .simulateLocationsAlongNewRouteWithRoutingAndSimulationOptions( + captureAny, captureAny, captureAny)); + + final List waypoints3 = + routeResult3.captured[0] as List; + final NavigationWaypointDto? waypointOut3 = waypoints3[0]; + expect(waypointIn.title, waypointOut3?.title); + expect(waypointIn.target?.latitude, waypointOut3?.target?.latitude); + expect(waypointIn.target?.longitude, waypointOut3?.target?.longitude); + + final RoutingOptionsDto routingOptionsOut2 = + routeResult3.captured[1] as RoutingOptionsDto; + expect(routingOptionsOut2.alternateRoutesStrategy, + AlternateRoutesStrategyDto.none); + expect(routingOptionsOut2.routingStrategy, + RoutingStrategyDto.deltaToTargetDistance); + expect(routingOptionsOut2.targetDistanceMeters, [1, 1, 1]); + expect(routingOptionsOut2.travelMode, TravelModeDto.taxi); + expect(routingOptionsOut2.avoidTolls, true); + expect(routingOptionsOut2.avoidFerries, true); + expect(routingOptionsOut2.avoidHighways, true); + expect(routingOptionsOut2.locationTimeoutMs, 10000); + + final SimulationOptionsDto simOptionsOut = + routeResult3.captured[2] as SimulationOptionsDto; + expect(simOptionsIn.speedMultiplier, simOptionsOut.speedMultiplier); + }); + }); - // Verify correct message sent from view api - final VerificationResult result = - verify(viewMockApi.clearPolygons(captureAny)); - final int viewId = result.captured[0] as int; + group('Markers', () { + test('get markers', () async { + // Create marker + const Marker marker = + Marker(markerId: 'Marker_0', options: MarkerOptions()); + + // Mock api response + final MarkerDto messageMarker = + MarkerDto(markerId: 'Marker_0', options: marker.options.toDto()); + when(viewMockApi.getMarkers(any)) + .thenAnswer((Invocation _) => [messageMarker]); + + // Get markers + final List markersOut = + await GoogleMapsNavigationPlatform.instance.getMarkers(viewId: 0); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.getMarkers(captureAny)); + final int viewId = result.captured[0] as int; + + // Verify response polygon options + expect(viewId, 0); + expect(marker.options, markersOut[0]!.options); + }); + + test('add marker', () async { + // Create options + const MarkerOptions optionsIn = MarkerOptions( + alpha: 0.5, + anchor: MarkerAnchor(u: 0.1, v: 0.2), + draggable: true, + flat: true, + consumeTapEvents: true, + position: LatLng(latitude: 50, longitude: 60), + rotation: 70, + infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), + zIndex: 2); + + // Mock api response + final MarkerDto markerIn = + MarkerDto(markerId: 'Marker_0', options: optionsIn.toDto()); + when(viewMockApi.addMarkers(any, any)) + .thenAnswer((Invocation _) => [markerIn]); + + // Add marker + final List markersOut = await GoogleMapsNavigationPlatform + .instance + .addMarkers(viewId: 0, markerOptions: [optionsIn]); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.addMarkers(captureAny, captureAny)); + final List markersInMessage = + result.captured[1] as List; + + // Verify message and response marker options + expect(markerIn.markerId, markersInMessage[0]?.markerId); + expect(optionsIn, markersInMessage[0]?.options.toMarkerOptions()); + expect(optionsIn, markersOut[0]!.options); + }); + + test('update marker', () async { + // Create marker + const Marker marker = + Marker(markerId: 'Marker_0', options: MarkerOptions()); + + // Mock api response + final MarkerDto messageMarker = + MarkerDto(markerId: 'Marker_0', options: marker.options.toDto()); + when(viewMockApi.updateMarkers(any, any)) + .thenAnswer((Invocation _) => [messageMarker]); + + // Edit marker + final List markersOut = await GoogleMapsNavigationPlatform + .instance + .updateMarkers(viewId: 0, markers: [marker]); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.updateMarkers(captureAny, captureAny)); + final List markersInMessage = + result.captured[1] as List; + + // Verify message and response marker options + expect(marker.markerId, markersInMessage[0]?.markerId); + expect( + marker.options, markersInMessage[0]?.options.toMarkerOptions()); + expect(marker.options, markersOut[0]!.options); + }); + + test('remove marker', () async { + // Create marker + const Marker marker = + Marker(markerId: 'Marker_0', options: MarkerOptions()); + + // Mock api response + when(viewMockApi.removeMarkers(any, any)) + .thenAnswer((Invocation _) => ()); + + // Remove marker + await GoogleMapsNavigationPlatform.instance + .removeMarkers(viewId: 0, markers: [marker]); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.removeMarkers(captureAny, captureAny)); + final List markersInMessage = + result.captured[1] as List; + + // Verify message + expect(marker.markerId, markersInMessage[0]?.markerId); + }); + + test('clear markers', () async { + // Mock api response + when(viewMockApi.clearMarkers(any)) + .thenAnswer((Invocation _) async => ()); + + // Clear map + await GoogleMapsNavigationPlatform.instance.clearMarkers(viewId: 0); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.clearMarkers(captureAny)); + final int viewId = result.captured[0] as int; + + // Verify message + expect(0, viewId); + }); + + test('clear map', () async { + // Mock api response + when(viewMockApi.clear(any)).thenAnswer((Invocation _) async => ()); + + // Clear map + await GoogleMapsNavigationPlatform.instance.clear(viewId: 0); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.clear(captureAny)); + final int viewId = result.captured[0] as int; + + // Verify message + expect(0, viewId); + }); + }); - // Verify message - expect(viewId, 0); + group('Polygons', () { + test('get polygons', () async { + // Create polygon + const Polygon polygon = + Polygon(polygonId: 'Polygon_0', options: PolygonOptions()); + + // Mock api response + final PolygonDto messagePolygon = PolygonDto( + polygonId: 'Polygon_0', options: polygon.options.toDto()); + when(viewMockApi.getPolygons(any)) + .thenAnswer((Invocation _) => [messagePolygon]); + + // Get polygons + final List polygonsOut = await GoogleMapsNavigationPlatform + .instance + .getPolygons(viewId: 0); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.getPolygons(captureAny)); + final int viewId = result.captured[0] as int; + + // Verify response polygon options + expect(viewId, 0); + expect(polygon.options, polygonsOut[0]!.options); + }); + + test('add polygon', () async { + // Create options + const PolygonOptions optionsIn = PolygonOptions( + points: [ + LatLng(latitude: 40.0, longitude: 50.0) + ], + holes: >[ + [LatLng(latitude: 60.0, longitude: 70.0)] + ], + clickable: true, + fillColor: Colors.amber, + geodesic: true, + strokeColor: Colors.cyan, + strokeWidth: 4, + zIndex: 3); + + // Mock api response + final PolygonDto polygonIn = + PolygonDto(polygonId: 'Polygon_0', options: optionsIn.toDto()); + when(viewMockApi.addPolygons(any, any)) + .thenAnswer((Invocation _) => [polygonIn]); + + // Add polygon + final List polygonsOut = + await GoogleMapsNavigationPlatform.instance.addPolygons( + viewId: 0, polygonOptions: [optionsIn]); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.addPolygons(captureAny, captureAny)); + final List polygonsInMessage = + result.captured[1] as List; + + // Verify message and response polygon options + expect(polygonIn.polygonId, polygonsInMessage[0]?.polygonId); + expect(optionsIn, polygonsInMessage[0]?.options.toPolygonOptions()); + expect(optionsIn, polygonsOut[0]!.options); + }); + + test('update polygon', () async { + // Create polygon + const Polygon polygon = + Polygon(polygonId: 'Polygon_0', options: PolygonOptions()); + + // Mock api response + final PolygonDto messagePolygon = PolygonDto( + polygonId: 'Polygon_0', options: polygon.options.toDto()); + when(viewMockApi.updatePolygons(any, any)) + .thenAnswer((Invocation _) => [messagePolygon]); + + // Edit polygon + final List polygonsOut = await GoogleMapsNavigationPlatform + .instance + .updatePolygons(viewId: 0, polygons: [polygon]); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.updatePolygons(captureAny, captureAny)); + final List polygonsInMessage = + result.captured[1] as List; + + // Verify message and response polygon options + expect(polygon.polygonId, polygonsInMessage[0]?.polygonId); + expect(polygon.options, + polygonsInMessage[0]?.options.toPolygonOptions()); + expect(polygon.options, polygonsOut[0]!.options); + }); + + test('remove polygon', () async { + // Create polygon + const Polygon polygon = + Polygon(polygonId: 'Polygon_0', options: PolygonOptions()); + + // Mock api response + when(viewMockApi.removePolygons(any, any)) + .thenAnswer((Invocation _) async => ()); + + // Remove polygon + await GoogleMapsNavigationPlatform.instance + .removePolygons(viewId: 0, polygons: [polygon]); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.removePolygons(captureAny, captureAny)); + final List polygonsInMessage = + result.captured[1] as List; + + // Verify message + expect(polygon.polygonId, polygonsInMessage[0]?.polygonId); + }); + + test('clear polygons', () async { + // Mock api response + when(viewMockApi.clearPolygons(any)) + .thenAnswer((Invocation _) async => ()); + + // Clear map + await GoogleMapsNavigationPlatform.instance.clearPolygons(viewId: 0); + + // Verify correct message sent from view api + final VerificationResult result = + verify(viewMockApi.clearPolygons(captureAny)); + final int viewId = result.captured[0] as int; + + // Verify message + expect(viewId, 0); + }); + }); }); - }); + } } diff --git a/test/google_maps_navigation_test.mocks.dart b/test/google_maps_navigation_test.mocks.dart index 5ec5493..00140b8 100644 --- a/test/google_maps_navigation_test.mocks.dart +++ b/test/google_maps_navigation_test.mocks.dart @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,10 +18,12 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; +import 'dart:typed_data' as _i6; import 'package:google_maps_navigation/src/method_channel/messages.g.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i5; import 'messages_test.g.dart' as _i3; @@ -71,6 +73,17 @@ class _FakeLatLngBoundsDto_2 extends _i1.SmartFake ); } +class _FakeImageDescriptorDto_3 extends _i1.SmartFake + implements _i2.ImageDescriptorDto { + _FakeImageDescriptorDto_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [TestNavigationSessionApi]. /// /// See the documentation for Mockito's code generation for more information. @@ -81,10 +94,12 @@ class MockTestNavigationSessionApi extends _i1.Mock } @override - _i4.Future createNavigationSession() => (super.noSuchMethod( + _i4.Future createNavigationSession( + bool? abnormalTerminationReportingEnabled) => + (super.noSuchMethod( Invocation.method( #createNavigationSession, - [], + [abnormalTerminationReportingEnabled], ), returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), @@ -144,6 +159,21 @@ class MockTestNavigationSessionApi extends _i1.Mock returnValueForMissingStub: null, ); + @override + String getNavSDKVersion() => (super.noSuchMethod( + Invocation.method( + #getNavSDKVersion, + [], + ), + returnValue: _i5.dummyValue( + this, + Invocation.method( + #getNavSDKVersion, + [], + ), + ), + ) as String); + @override bool isGuidanceRunning() => (super.noSuchMethod( Invocation.method( @@ -172,11 +202,12 @@ class MockTestNavigationSessionApi extends _i1.Mock ); @override - _i4.Future<_i2.RouteStatusDto> setDestinations(_i2.DestinationsDto? msg) => + _i4.Future<_i2.RouteStatusDto> setDestinations( + _i2.DestinationsDto? destinations) => (super.noSuchMethod( Invocation.method( #setDestinations, - [msg], + [destinations], ), returnValue: _i4.Future<_i2.RouteStatusDto>.value( _i2.RouteStatusDto.internalError), @@ -424,13 +455,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableMyLocation( + void setMyLocationEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableMyLocation, + #setMyLocationEnabled, [ viewId, enabled, @@ -497,13 +528,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableNavigationTripProgressBar( + void setNavigationTripProgressBarEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableNavigationTripProgressBar, + #setNavigationTripProgressBarEnabled, [ viewId, enabled, @@ -522,13 +553,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableNavigationHeader( + void setNavigationHeaderEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableNavigationHeader, + #setNavigationHeaderEnabled, [ viewId, enabled, @@ -547,13 +578,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableNavigationFooter( + void setNavigationFooterEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableNavigationFooter, + #setNavigationFooterEnabled, [ viewId, enabled, @@ -572,13 +603,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableRecenterButton( + void setRecenterButtonEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableRecenterButton, + #setRecenterButtonEnabled, [ viewId, enabled, @@ -597,13 +628,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableSpeedLimitIcon( + void setSpeedLimitIconEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableSpeedLimitIcon, + #setSpeedLimitIconEnabled, [ viewId, enabled, @@ -622,13 +653,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableSpeedometer( + void setSpeedometerEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableSpeedometer, + #setSpeedometerEnabled, [ viewId, enabled, @@ -638,22 +669,22 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - bool isIncidentCardsEnabled(int? viewId) => (super.noSuchMethod( + bool isTrafficIncidentCardsEnabled(int? viewId) => (super.noSuchMethod( Invocation.method( - #isIncidentCardsEnabled, + #isTrafficIncidentCardsEnabled, [viewId], ), returnValue: false, ) as bool); @override - void enableIncidentCards( + void setTrafficIncidentCardsEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableIncidentCards, + #setTrafficIncidentCardsEnabled, [ viewId, enabled, @@ -672,13 +703,13 @@ class MockTestNavigationViewApi extends _i1.Mock ) as bool); @override - void enableNavigationUI( + void setNavigationUIEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableNavigationUI, + #setNavigationUIEnabled, [ viewId, enabled, @@ -1003,13 +1034,72 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableMyLocationButton( + double getMinZoomPreference(int? viewId) => (super.noSuchMethod( + Invocation.method( + #getMinZoomPreference, + [viewId], + ), + returnValue: 0.0, + ) as double); + + @override + double getMaxZoomPreference(int? viewId) => (super.noSuchMethod( + Invocation.method( + #getMaxZoomPreference, + [viewId], + ), + returnValue: 0.0, + ) as double); + + @override + void resetMinMaxZoomPreference(int? viewId) => super.noSuchMethod( + Invocation.method( + #resetMinMaxZoomPreference, + [viewId], + ), + returnValueForMissingStub: null, + ); + + @override + void setMinZoomPreference( + int? viewId, + double? minZoomPreference, + ) => + super.noSuchMethod( + Invocation.method( + #setMinZoomPreference, + [ + viewId, + minZoomPreference, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void setMaxZoomPreference( + int? viewId, + double? maxZoomPreference, + ) => + super.noSuchMethod( + Invocation.method( + #setMaxZoomPreference, + [ + viewId, + maxZoomPreference, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void setMyLocationButtonEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableMyLocationButton, + #setMyLocationButtonEnabled, [ viewId, enabled, @@ -1019,13 +1109,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableZoomGestures( + void setConsumeMyLocationButtonClickEventsEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableZoomGestures, + #setConsumeMyLocationButtonClickEventsEnabled, [ viewId, enabled, @@ -1035,13 +1125,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableZoomControls( + void setZoomGesturesEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableZoomControls, + #setZoomGesturesEnabled, [ viewId, enabled, @@ -1051,13 +1141,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableCompass( + void setZoomControlsEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableCompass, + #setZoomControlsEnabled, [ viewId, enabled, @@ -1067,13 +1157,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableRotateGestures( + void setCompassEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableRotateGestures, + #setCompassEnabled, [ viewId, enabled, @@ -1083,13 +1173,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableScrollGestures( + void setRotateGesturesEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableScrollGestures, + #setRotateGesturesEnabled, [ viewId, enabled, @@ -1099,13 +1189,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableScrollGesturesDuringRotateOrZoom( + void setScrollGesturesEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableScrollGesturesDuringRotateOrZoom, + #setScrollGesturesEnabled, [ viewId, enabled, @@ -1115,13 +1205,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableTiltGestures( + void setScrollGesturesDuringRotateOrZoomEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableTiltGestures, + #setScrollGesturesDuringRotateOrZoomEnabled, [ viewId, enabled, @@ -1131,13 +1221,13 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableMapToolbar( + void setTiltGesturesEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableMapToolbar, + #setTiltGesturesEnabled, [ viewId, enabled, @@ -1147,13 +1237,29 @@ class MockTestNavigationViewApi extends _i1.Mock ); @override - void enableTraffic( + void setMapToolbarEnabled( int? viewId, bool? enabled, ) => super.noSuchMethod( Invocation.method( - #enableTraffic, + #setMapToolbarEnabled, + [ + viewId, + enabled, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void setTrafficEnabled( + int? viewId, + bool? enabled, + ) => + super.noSuchMethod( + Invocation.method( + #setTrafficEnabled, [ viewId, enabled, @@ -1171,6 +1277,16 @@ class MockTestNavigationViewApi extends _i1.Mock returnValue: false, ) as bool); + @override + bool isConsumeMyLocationButtonClickEventsEnabled(int? viewId) => + (super.noSuchMethod( + Invocation.method( + #isConsumeMyLocationButtonClickEventsEnabled, + [viewId], + ), + returnValue: false, + ) as bool); + @override bool isZoomGesturesEnabled(int? viewId) => (super.noSuchMethod( Invocation.method( @@ -1525,4 +1641,85 @@ class MockTestNavigationViewApi extends _i1.Mock ), returnValueForMissingStub: null, ); + + @override + void registerOnCameraChangedListener(int? viewId) => super.noSuchMethod( + Invocation.method( + #registerOnCameraChangedListener, + [viewId], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [TestImageRegistryApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestImageRegistryApi extends _i1.Mock + implements _i3.TestImageRegistryApi { + MockTestImageRegistryApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.ImageDescriptorDto registerBitmapImage( + String? imageId, + _i6.Uint8List? bytes, + double? imagePixelRatio, + double? width, + double? height, + ) => + (super.noSuchMethod( + Invocation.method( + #registerBitmapImage, + [ + imageId, + bytes, + imagePixelRatio, + width, + height, + ], + ), + returnValue: _FakeImageDescriptorDto_3( + this, + Invocation.method( + #registerBitmapImage, + [ + imageId, + bytes, + imagePixelRatio, + width, + height, + ], + ), + ), + ) as _i2.ImageDescriptorDto); + + @override + void unregisterImage(_i2.ImageDescriptorDto? imageDescriptor) => + super.noSuchMethod( + Invocation.method( + #unregisterImage, + [imageDescriptor], + ), + returnValueForMissingStub: null, + ); + + @override + List<_i2.ImageDescriptorDto?> getRegisteredImages() => (super.noSuchMethod( + Invocation.method( + #getRegisteredImages, + [], + ), + returnValue: <_i2.ImageDescriptorDto?>[], + ) as List<_i2.ImageDescriptorDto?>); + + @override + void clearRegisteredImages() => super.noSuchMethod( + Invocation.method( + #clearRegisteredImages, + [], + ), + returnValueForMissingStub: null, + ); } diff --git a/test/messages_test.g.dart b/test/messages_test.g.dart index c663311..4dffb03 100644 --- a/test/messages_test.g.dart +++ b/test/messages_test.g.dart @@ -37,51 +37,54 @@ class _TestNavigationViewApiCodec extends StandardMessageCodec { } else if (value is CircleOptionsDto) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is InfoWindowDto) { + } else if (value is ImageDescriptorDto) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is LatLngBoundsDto) { + } else if (value is InfoWindowDto) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is LatLngDto) { + } else if (value is LatLngBoundsDto) { buffer.putUint8(133); writeValue(buffer, value.encode()); } else if (value is LatLngDto) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is MarkerAnchorDto) { + } else if (value is LatLngDto) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is MarkerDto) { + } else if (value is MarkerAnchorDto) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is MarkerOptionsDto) { + } else if (value is MarkerDto) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PatternItemDto) { + } else if (value is MarkerOptionsDto) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PolygonDto) { + } else if (value is PatternItemDto) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PolygonHoleDto) { + } else if (value is PolygonDto) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is PolygonOptionsDto) { + } else if (value is PolygonHoleDto) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is PolylineDto) { + } else if (value is PolygonOptionsDto) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is PolylineOptionsDto) { + } else if (value is PolylineDto) { buffer.putUint8(143); writeValue(buffer, value.encode()); - } else if (value is StyleSpanDto) { + } else if (value is PolylineOptionsDto) { buffer.putUint8(144); writeValue(buffer, value.encode()); - } else if (value is StyleSpanStrokeStyleDto) { + } else if (value is StyleSpanDto) { buffer.putUint8(145); writeValue(buffer, value.encode()); + } else if (value is StyleSpanStrokeStyleDto) { + buffer.putUint8(146); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -97,34 +100,36 @@ class _TestNavigationViewApiCodec extends StandardMessageCodec { case 130: return CircleOptionsDto.decode(readValue(buffer)!); case 131: - return InfoWindowDto.decode(readValue(buffer)!); + return ImageDescriptorDto.decode(readValue(buffer)!); case 132: - return LatLngBoundsDto.decode(readValue(buffer)!); + return InfoWindowDto.decode(readValue(buffer)!); case 133: - return LatLngDto.decode(readValue(buffer)!); + return LatLngBoundsDto.decode(readValue(buffer)!); case 134: return LatLngDto.decode(readValue(buffer)!); case 135: - return MarkerAnchorDto.decode(readValue(buffer)!); + return LatLngDto.decode(readValue(buffer)!); case 136: - return MarkerDto.decode(readValue(buffer)!); + return MarkerAnchorDto.decode(readValue(buffer)!); case 137: - return MarkerOptionsDto.decode(readValue(buffer)!); + return MarkerDto.decode(readValue(buffer)!); case 138: - return PatternItemDto.decode(readValue(buffer)!); + return MarkerOptionsDto.decode(readValue(buffer)!); case 139: - return PolygonDto.decode(readValue(buffer)!); + return PatternItemDto.decode(readValue(buffer)!); case 140: - return PolygonHoleDto.decode(readValue(buffer)!); + return PolygonDto.decode(readValue(buffer)!); case 141: - return PolygonOptionsDto.decode(readValue(buffer)!); + return PolygonHoleDto.decode(readValue(buffer)!); case 142: - return PolylineDto.decode(readValue(buffer)!); + return PolygonOptionsDto.decode(readValue(buffer)!); case 143: - return PolylineOptionsDto.decode(readValue(buffer)!); + return PolylineDto.decode(readValue(buffer)!); case 144: - return StyleSpanDto.decode(readValue(buffer)!); + return PolylineOptionsDto.decode(readValue(buffer)!); case 145: + return StyleSpanDto.decode(readValue(buffer)!); + case 146: return StyleSpanStrokeStyleDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -142,7 +147,7 @@ abstract class TestNavigationViewApi { bool isMyLocationEnabled(int viewId); - void enableMyLocation(int viewId, bool enabled); + void setMyLocationEnabled(int viewId, bool enabled); LatLngDto? getMyLocation(int viewId); @@ -154,35 +159,35 @@ abstract class TestNavigationViewApi { bool isNavigationTripProgressBarEnabled(int viewId); - void enableNavigationTripProgressBar(int viewId, bool enabled); + void setNavigationTripProgressBarEnabled(int viewId, bool enabled); bool isNavigationHeaderEnabled(int viewId); - void enableNavigationHeader(int viewId, bool enabled); + void setNavigationHeaderEnabled(int viewId, bool enabled); bool isNavigationFooterEnabled(int viewId); - void enableNavigationFooter(int viewId, bool enabled); + void setNavigationFooterEnabled(int viewId, bool enabled); bool isRecenterButtonEnabled(int viewId); - void enableRecenterButton(int viewId, bool enabled); + void setRecenterButtonEnabled(int viewId, bool enabled); bool isSpeedLimitIconEnabled(int viewId); - void enableSpeedLimitIcon(int viewId, bool enabled); + void setSpeedLimitIconEnabled(int viewId, bool enabled); bool isSpeedometerEnabled(int viewId); - void enableSpeedometer(int viewId, bool enabled); + void setSpeedometerEnabled(int viewId, bool enabled); - bool isIncidentCardsEnabled(int viewId); + bool isTrafficIncidentCardsEnabled(int viewId); - void enableIncidentCards(int viewId, bool enabled); + void setTrafficIncidentCardsEnabled(int viewId, bool enabled); bool isNavigationUIEnabled(int viewId); - void enableNavigationUI(int viewId, bool enabled); + void setNavigationUIEnabled(int viewId, bool enabled); CameraPositionDto getCameraPosition(int viewId); @@ -229,28 +234,42 @@ abstract class TestNavigationViewApi { void showRouteOverview(int viewId); - void enableMyLocationButton(int viewId, bool enabled); + double getMinZoomPreference(int viewId); + + double getMaxZoomPreference(int viewId); + + void resetMinMaxZoomPreference(int viewId); - void enableZoomGestures(int viewId, bool enabled); + void setMinZoomPreference(int viewId, double minZoomPreference); - void enableZoomControls(int viewId, bool enabled); + void setMaxZoomPreference(int viewId, double maxZoomPreference); - void enableCompass(int viewId, bool enabled); + void setMyLocationButtonEnabled(int viewId, bool enabled); - void enableRotateGestures(int viewId, bool enabled); + void setConsumeMyLocationButtonClickEventsEnabled(int viewId, bool enabled); - void enableScrollGestures(int viewId, bool enabled); + void setZoomGesturesEnabled(int viewId, bool enabled); - void enableScrollGesturesDuringRotateOrZoom(int viewId, bool enabled); + void setZoomControlsEnabled(int viewId, bool enabled); - void enableTiltGestures(int viewId, bool enabled); + void setCompassEnabled(int viewId, bool enabled); - void enableMapToolbar(int viewId, bool enabled); + void setRotateGesturesEnabled(int viewId, bool enabled); - void enableTraffic(int viewId, bool enabled); + void setScrollGesturesEnabled(int viewId, bool enabled); + + void setScrollGesturesDuringRotateOrZoomEnabled(int viewId, bool enabled); + + void setTiltGesturesEnabled(int viewId, bool enabled); + + void setMapToolbarEnabled(int viewId, bool enabled); + + void setTrafficEnabled(int viewId, bool enabled); bool isMyLocationButtonEnabled(int viewId); + bool isConsumeMyLocationButtonClickEventsEnabled(int viewId); + bool isZoomGesturesEnabled(int viewId); bool isZoomControlsEnabled(int viewId); @@ -311,6 +330,8 @@ abstract class TestNavigationViewApi { void clearCircles(int viewId); + void registerOnCameraChangedListener(int viewId); + static void setup(TestNavigationViewApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -378,7 +399,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocation', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -389,16 +410,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocation was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocation was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocation was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationEnabled was null, expected non-null bool.'); try { - api.enableMyLocation(arg_viewId!, arg_enabled!); + api.setMyLocationEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -575,7 +596,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationTripProgressBar', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationTripProgressBarEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -586,16 +607,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationTripProgressBar was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationTripProgressBarEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationTripProgressBar was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationTripProgressBarEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationTripProgressBar was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationTripProgressBarEnabled was null, expected non-null bool.'); try { - api.enableNavigationTripProgressBar(arg_viewId!, arg_enabled!); + api.setNavigationTripProgressBarEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -640,7 +661,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationHeader', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationHeaderEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -651,16 +672,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationHeader was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationHeaderEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationHeader was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationHeaderEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationHeader was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationHeaderEnabled was null, expected non-null bool.'); try { - api.enableNavigationHeader(arg_viewId!, arg_enabled!); + api.setNavigationHeaderEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -705,7 +726,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationFooter', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationFooterEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -716,16 +737,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationFooter was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationFooterEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationFooter was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationFooterEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationFooter was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationFooterEnabled was null, expected non-null bool.'); try { - api.enableNavigationFooter(arg_viewId!, arg_enabled!); + api.setNavigationFooterEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -770,7 +791,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRecenterButton', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRecenterButtonEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -781,16 +802,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRecenterButton was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRecenterButtonEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRecenterButton was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRecenterButtonEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRecenterButton was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRecenterButtonEnabled was null, expected non-null bool.'); try { - api.enableRecenterButton(arg_viewId!, arg_enabled!); + api.setRecenterButtonEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -835,7 +856,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedLimitIcon', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedLimitIconEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -846,16 +867,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedLimitIcon was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedLimitIconEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedLimitIcon was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedLimitIconEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedLimitIcon was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedLimitIconEnabled was null, expected non-null bool.'); try { - api.enableSpeedLimitIcon(arg_viewId!, arg_enabled!); + api.setSpeedLimitIconEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -900,7 +921,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedometer', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedometerEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -911,16 +932,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedometer was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedometerEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedometer was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedometerEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableSpeedometer was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setSpeedometerEnabled was null, expected non-null bool.'); try { - api.enableSpeedometer(arg_viewId!, arg_enabled!); + api.setSpeedometerEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -934,7 +955,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isIncidentCardsEnabled', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isTrafficIncidentCardsEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -945,13 +966,13 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isIncidentCardsEnabled was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isTrafficIncidentCardsEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isIncidentCardsEnabled was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isTrafficIncidentCardsEnabled was null, expected non-null int.'); try { - final bool output = api.isIncidentCardsEnabled(arg_viewId!); + final bool output = api.isTrafficIncidentCardsEnabled(arg_viewId!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -965,7 +986,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableIncidentCards', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficIncidentCardsEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -976,16 +997,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableIncidentCards was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficIncidentCardsEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableIncidentCards was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficIncidentCardsEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableIncidentCards was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficIncidentCardsEnabled was null, expected non-null bool.'); try { - api.enableIncidentCards(arg_viewId!, arg_enabled!); + api.setTrafficIncidentCardsEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1030,7 +1051,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationUI', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationUIEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1041,16 +1062,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationUI was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationUIEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationUI was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationUIEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableNavigationUI was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setNavigationUIEnabled was null, expected non-null bool.'); try { - api.enableNavigationUI(arg_viewId!, arg_enabled!); + api.setNavigationUIEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1711,7 +1732,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocationButton', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMinZoomPreference', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1722,16 +1743,177 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocationButton was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMinZoomPreference was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocationButton was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMinZoomPreference was null, expected non-null int.'); + try { + final double output = api.getMinZoomPreference(arg_viewId!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMaxZoomPreference', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMaxZoomPreference was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.getMaxZoomPreference was null, expected non-null int.'); + try { + final double output = api.getMaxZoomPreference(arg_viewId!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.resetMinMaxZoomPreference', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.resetMinMaxZoomPreference was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.resetMinMaxZoomPreference was null, expected non-null int.'); + try { + api.resetMinMaxZoomPreference(arg_viewId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMinZoomPreference', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMinZoomPreference was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMinZoomPreference was null, expected non-null int.'); + final double? arg_minZoomPreference = (args[1] as double?); + assert(arg_minZoomPreference != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMinZoomPreference was null, expected non-null double.'); + try { + api.setMinZoomPreference(arg_viewId!, arg_minZoomPreference!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMaxZoomPreference', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMaxZoomPreference was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMaxZoomPreference was null, expected non-null int.'); + final double? arg_maxZoomPreference = (args[1] as double?); + assert(arg_maxZoomPreference != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMaxZoomPreference was null, expected non-null double.'); + try { + api.setMaxZoomPreference(arg_viewId!, arg_maxZoomPreference!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationButtonEnabled', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationButtonEnabled was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationButtonEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMyLocationButton was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMyLocationButtonEnabled was null, expected non-null bool.'); try { - api.enableMyLocationButton(arg_viewId!, arg_enabled!); + api.setMyLocationButtonEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1745,7 +1927,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomGestures', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setConsumeMyLocationButtonClickEventsEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1756,16 +1938,17 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomGestures was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setConsumeMyLocationButtonClickEventsEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomGestures was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setConsumeMyLocationButtonClickEventsEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomGestures was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setConsumeMyLocationButtonClickEventsEnabled was null, expected non-null bool.'); try { - api.enableZoomGestures(arg_viewId!, arg_enabled!); + api.setConsumeMyLocationButtonClickEventsEnabled( + arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1779,7 +1962,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomControls', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomGesturesEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1790,16 +1973,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomControls was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomGesturesEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomControls was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomGesturesEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableZoomControls was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomGesturesEnabled was null, expected non-null bool.'); try { - api.enableZoomControls(arg_viewId!, arg_enabled!); + api.setZoomGesturesEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1813,7 +1996,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableCompass', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomControlsEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1824,16 +2007,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableCompass was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomControlsEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableCompass was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomControlsEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableCompass was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setZoomControlsEnabled was null, expected non-null bool.'); try { - api.enableCompass(arg_viewId!, arg_enabled!); + api.setZoomControlsEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1847,7 +2030,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRotateGestures', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setCompassEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1858,16 +2041,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRotateGestures was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setCompassEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRotateGestures was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setCompassEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableRotateGestures was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setCompassEnabled was null, expected non-null bool.'); try { - api.enableRotateGestures(arg_viewId!, arg_enabled!); + api.setCompassEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1881,7 +2064,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGestures', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRotateGesturesEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1892,16 +2075,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGestures was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRotateGesturesEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGestures was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRotateGesturesEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGestures was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setRotateGesturesEnabled was null, expected non-null bool.'); try { - api.enableScrollGestures(arg_viewId!, arg_enabled!); + api.setRotateGesturesEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1915,7 +2098,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGesturesDuringRotateOrZoom', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1926,16 +2109,50 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGesturesDuringRotateOrZoom was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGesturesDuringRotateOrZoom was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableScrollGesturesDuringRotateOrZoom was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesEnabled was null, expected non-null bool.'); try { - api.enableScrollGesturesDuringRotateOrZoom( + api.setScrollGesturesEnabled(arg_viewId!, arg_enabled!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesDuringRotateOrZoomEnabled', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesDuringRotateOrZoomEnabled was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesDuringRotateOrZoomEnabled was null, expected non-null int.'); + final bool? arg_enabled = (args[1] as bool?); + assert(arg_enabled != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setScrollGesturesDuringRotateOrZoomEnabled was null, expected non-null bool.'); + try { + api.setScrollGesturesDuringRotateOrZoomEnabled( arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { @@ -1950,7 +2167,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTiltGestures', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTiltGesturesEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1961,16 +2178,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTiltGestures was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTiltGesturesEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTiltGestures was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTiltGesturesEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTiltGestures was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTiltGesturesEnabled was null, expected non-null bool.'); try { - api.enableTiltGestures(arg_viewId!, arg_enabled!); + api.setTiltGesturesEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1984,7 +2201,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMapToolbar', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMapToolbarEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -1995,16 +2212,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMapToolbar was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMapToolbarEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMapToolbar was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMapToolbarEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableMapToolbar was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setMapToolbarEnabled was null, expected non-null bool.'); try { - api.enableMapToolbar(arg_viewId!, arg_enabled!); + api.setMapToolbarEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -2018,7 +2235,7 @@ abstract class TestNavigationViewApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTraffic', + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficEnabled', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -2029,16 +2246,16 @@ abstract class TestNavigationViewApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTraffic was null.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficEnabled was null.'); final List args = (message as List?)!; final int? arg_viewId = (args[0] as int?); assert(arg_viewId != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTraffic was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.enableTraffic was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.setTrafficEnabled was null, expected non-null bool.'); try { - api.enableTraffic(arg_viewId!, arg_enabled!); + api.setTrafficEnabled(arg_viewId!, arg_enabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -2080,6 +2297,38 @@ abstract class TestNavigationViewApi { }); } } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isConsumeMyLocationButtonClickEventsEnabled', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isConsumeMyLocationButtonClickEventsEnabled was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.isConsumeMyLocationButtonClickEventsEnabled was null, expected non-null int.'); + try { + final bool output = + api.isConsumeMyLocationButtonClickEventsEnabled(arg_viewId!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( @@ -3067,6 +3316,211 @@ abstract class TestNavigationViewApi { }); } } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.registerOnCameraChangedListener', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.registerOnCameraChangedListener was null.'); + final List args = (message as List?)!; + final int? arg_viewId = (args[0] as int?); + assert(arg_viewId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationViewApi.registerOnCameraChangedListener was null, expected non-null int.'); + try { + api.registerOnCameraChangedListener(arg_viewId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} + +class _TestImageRegistryApiCodec extends StandardMessageCodec { + const _TestImageRegistryApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is ImageDescriptorDto) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is ImageDescriptorDto) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return ImageDescriptorDto.decode(readValue(buffer)!); + case 129: + return ImageDescriptorDto.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestImageRegistryApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = + _TestImageRegistryApiCodec(); + + ImageDescriptorDto registerBitmapImage(String imageId, Uint8List bytes, + double imagePixelRatio, double? width, double? height); + + void unregisterImage(ImageDescriptorDto imageDescriptor); + + List getRegisteredImages(); + + void clearRegisteredImages(); + + static void setup(TestImageRegistryApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage was null.'); + final List args = (message as List?)!; + final String? arg_imageId = (args[0] as String?); + assert(arg_imageId != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage was null, expected non-null String.'); + final Uint8List? arg_bytes = (args[1] as Uint8List?); + assert(arg_bytes != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage was null, expected non-null Uint8List.'); + final double? arg_imagePixelRatio = (args[2] as double?); + assert(arg_imagePixelRatio != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.registerBitmapImage was null, expected non-null double.'); + final double? arg_width = (args[3] as double?); + final double? arg_height = (args[4] as double?); + try { + final ImageDescriptorDto output = api.registerBitmapImage( + arg_imageId!, + arg_bytes!, + arg_imagePixelRatio!, + arg_width, + arg_height); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.unregisterImage', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.unregisterImage was null.'); + final List args = (message as List?)!; + final ImageDescriptorDto? arg_imageDescriptor = + (args[0] as ImageDescriptorDto?); + assert(arg_imageDescriptor != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.unregisterImage was null, expected non-null ImageDescriptorDto.'); + try { + api.unregisterImage(arg_imageDescriptor!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.getRegisteredImages', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + final List output = api.getRegisteredImages(); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.ImageRegistryApi.clearRegisteredImages', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + api.clearRegisteredImages(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } } } @@ -3110,15 +3564,18 @@ class _TestNavigationSessionApiCodec extends StandardMessageCodec { } else if (value is RouteSegmentTrafficDataRoadStretchRenderingDataDto) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is RoutingOptionsDto) { + } else if (value is RouteTokenOptionsDto) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is SimulationOptionsDto) { + } else if (value is RoutingOptionsDto) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is SpeedAlertOptionsDto) { + } else if (value is SimulationOptionsDto) { buffer.putUint8(142); writeValue(buffer, value.encode()); + } else if (value is SpeedAlertOptionsDto) { + buffer.putUint8(143); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -3153,10 +3610,12 @@ class _TestNavigationSessionApiCodec extends StandardMessageCodec { return RouteSegmentTrafficDataRoadStretchRenderingDataDto.decode( readValue(buffer)!); case 140: - return RoutingOptionsDto.decode(readValue(buffer)!); + return RouteTokenOptionsDto.decode(readValue(buffer)!); case 141: - return SimulationOptionsDto.decode(readValue(buffer)!); + return RoutingOptionsDto.decode(readValue(buffer)!); case 142: + return SimulationOptionsDto.decode(readValue(buffer)!); + case 143: return SpeedAlertOptionsDto.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -3171,7 +3630,8 @@ abstract class TestNavigationSessionApi { _TestNavigationSessionApiCodec(); /// General. - Future createNavigationSession(); + Future createNavigationSession( + bool abnormalTerminationReportingEnabled); bool isInitialized(); @@ -3184,6 +3644,8 @@ abstract class TestNavigationSessionApi { void resetTermsAccepted(); + String getNavSDKVersion(); + /// Navigation. bool isGuidanceRunning(); @@ -3191,7 +3653,7 @@ abstract class TestNavigationSessionApi { void stopGuidance(); - Future setDestinations(DestinationsDto msg); + Future setDestinations(DestinationsDto destinations); void clearDestinations(); @@ -3261,8 +3723,16 @@ abstract class TestNavigationSessionApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.createNavigationSession was null.'); + final List args = (message as List?)!; + final bool? arg_abnormalTerminationReportingEnabled = + (args[0] as bool?); + assert(arg_abnormalTerminationReportingEnabled != null, + 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.createNavigationSession was null, expected non-null bool.'); try { - await api.createNavigationSession(); + await api.createNavigationSession( + arg_abnormalTerminationReportingEnabled!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -3414,6 +3884,31 @@ abstract class TestNavigationSessionApi { }); } } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.getNavSDKVersion', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + final String output = api.getNavSDKVersion(); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( @@ -3505,11 +4000,13 @@ abstract class TestNavigationSessionApi { assert(message != null, 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.setDestinations was null.'); final List args = (message as List?)!; - final DestinationsDto? arg_msg = (args[0] as DestinationsDto?); - assert(arg_msg != null, + final DestinationsDto? arg_destinations = + (args[0] as DestinationsDto?); + assert(arg_destinations != null, 'Argument for dev.flutter.pigeon.google_maps_navigation.NavigationSessionApi.setDestinations was null, expected non-null DestinationsDto.'); try { - final RouteStatusDto output = await api.setDestinations(arg_msg!); + final RouteStatusDto output = + await api.setDestinations(arg_destinations!); return [output.index]; } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/test/navigation_polygons_test.dart b/test/navigation_polygons_test.dart index 5ac44f1..898a05b 100644 --- a/test/navigation_polygons_test.dart +++ b/test/navigation_polygons_test.dart @@ -15,7 +15,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_navigation/google_maps_navigation.dart'; -import 'package:google_maps_navigation/src/types/util/polygon_conversion.dart'; +import 'package:google_maps_navigation/src/method_channel/method_channel.dart'; void main() { late Polygon polygon; diff --git a/test/navigation_types_test.dart b/test/navigation_types_test.dart index 074ae6c..d2433e7 100644 --- a/test/navigation_types_test.dart +++ b/test/navigation_types_test.dart @@ -14,12 +14,13 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_navigation/google_maps_navigation.dart'; +import 'package:google_maps_navigation/src/method_channel/convert/navigation_waypoint.dart'; +import 'package:google_maps_navigation/src/method_channel/method_channel.dart'; void main() { late NavigationWaypointDto waypointDto; late NavigationWaypoint waypoint; late Destinations destinations; - late NavigationSessionEventDto sessionEventDto; late NavigationDisplayOptions displayOptions; late RoutingOptions routingOptions; @@ -47,10 +48,6 @@ void main() { showDestinationMarkers: false, ), ); - sessionEventDto = NavigationSessionEventDto( - type: NavigationSessionEventTypeDto.arrivalEvent, - message: 'testMessage', - ); displayOptions = NavigationDisplayOptions( showDestinationMarkers: false, showStopSigns: true, @@ -68,8 +65,7 @@ void main() { group('NavigationWaypoint tests', () { test('tests Navigation Waypoint conversion from DTO', () { - final NavigationWaypoint gmsWaypoint = - navigationWaypointFromDto(waypointDto); + final NavigationWaypoint gmsWaypoint = waypointDto.toNavigationWaypoint(); expect(gmsWaypoint.title, waypointDto.title); expect(gmsWaypoint.target?.latitude, waypointDto.target?.latitude); expect(gmsWaypoint.target?.longitude, waypointDto.target?.longitude); @@ -81,8 +77,7 @@ void main() { }); test('tests Navigation Waypoint conversion to DTO', () { - final NavigationWaypointDto waypointDto2 = - navigationWaypointToDto(waypoint); + final NavigationWaypointDto waypointDto2 = waypoint.toDto(); expect(waypoint.title, waypointDto2.title); expect(waypoint.target?.latitude, waypointDto2.target?.latitude); expect(waypoint.target?.longitude, waypointDto2.target?.longitude); @@ -95,8 +90,7 @@ void main() { group('NavigationDestinationEventMessage tests', () { test('tests Navigation Destination Message conversion to DTO', () { - final DestinationsDto pidgeonDestinationMessage = - navigationDestinationToDto(destinations); + final DestinationsDto pidgeonDestinationMessage = destinations.toDto(); expect(destinations.displayOptions.showDestinationMarkers, pidgeonDestinationMessage.displayOptions.showDestinationMarkers); @@ -120,28 +114,10 @@ void main() { }); }); - group('Navigation Event Message tests', () { - test('tests Navigation Session Event Message conversion from Pigeon DTO', - () { - final NavigationSessionEvent gmsNavigationSessionEventDto = - navigationSessionEventFromDto(sessionEventDto); - - expect( - gmsNavigationSessionEventDto.message, - sessionEventDto.message, - ); - - expect( - gmsNavigationSessionEventDto.type.toString().split('.').last, - sessionEventDto.type.toString().split('.').last, - ); - }); - }); - group('Navigation Options tests', () { test('tests Navigation Display options conversion to Pigeon DTO', () { final NavigationDisplayOptionsDto pigeonDtoDisplayOptions = - navigationDisplayOptionsToDto(displayOptions); + displayOptions.toDto(); expect( pigeonDtoDisplayOptions.showDestinationMarkers, @@ -158,8 +134,7 @@ void main() { }); test('tests Navigation Routing options conversion to Pigeon DTO', () { - final RoutingOptionsDto pigeonDtoRoutingOptions = - routingOptionsToDto(routingOptions); + final RoutingOptionsDto pigeonDtoRoutingOptions = routingOptions.toDto(); expect( pigeonDtoRoutingOptions.targetDistanceMeters, @@ -199,10 +174,8 @@ void main() { }); test('tests Navigation Routing strategy conversion to Pigeon DTO', () { - final RoutingStrategyDto? pigeonDtoStrategy = - navigationRoutingStrategyToPigeonFormat( - NavigationRoutingStrategy.defaultBest, - ); + final RoutingStrategyDto pigeonDtoStrategy = + NavigationRoutingStrategy.defaultBest.toDto(); expect( pigeonDtoStrategy.toString().split('.').last, @@ -212,10 +185,8 @@ void main() { test('tests Navigation AlternativeRoutes strategy conversion to Pigeon DTO', () { - final AlternateRoutesStrategyDto? pigeonDtoStrategy = - navigationAlternateRoutesStrategyToDto( - NavigationAlternateRoutesStrategy.all, - ); + final AlternateRoutesStrategyDto pigeonDtoStrategy = + NavigationAlternateRoutesStrategy.all.toDto(); expect( pigeonDtoStrategy.toString().split('.').last, @@ -227,7 +198,7 @@ void main() { group('Navigation tests', () { test('Navigation RouteStatus conversion from Pigeon DTO', () { final NavigationRouteStatus status = - navigationRouteStatusFromDto(RouteStatusDto.apiKeyNotAuthorized); + RouteStatusDto.apiKeyNotAuthorized.toNavigationRouteStatus(); expect( status.toString().split('.').last, @@ -236,11 +207,10 @@ void main() { }); test('Navigation time and distance conversion from Pigeon DTO', () { - final NavigationTimeAndDistance td = - navigationTimeAndDistanceFromDto(NavigationTimeAndDistanceDto( + final NavigationTimeAndDistance td = NavigationTimeAndDistanceDto( time: 5.0, distance: 6.0, - )); + ).toNavigationTimeAndDistance(); expect( td.time, @@ -254,12 +224,11 @@ void main() { test('Navigation audio guidance conversion to Pigeon DTO', () { final NavigationAudioGuidanceSettingsDto settings = - navigationAudioGuidanceSettingsToDto( - NavigationAudioGuidanceSettings( - isBluetoothAudioEnabled: true, - isVibrationEnabled: true, - guidanceType: NavigationAudioGuidanceType.alertsAndGuidance), - ); + NavigationAudioGuidanceSettings( + isBluetoothAudioEnabled: true, + isVibrationEnabled: true, + guidanceType: NavigationAudioGuidanceType.alertsAndGuidance) + .toDto(); expect( settings.isBluetoothAudioEnabled, @@ -281,19 +250,19 @@ void main() { test('Navigation speed alert severity conversion from Pigeon DTO', () { expect( SpeedAlertSeverity.major, - navigationSpeedAlertSeverityFromDto(SpeedAlertSeverityDto.major), + SpeedAlertSeverityDto.major.toSpeedAlertSeverity(), ); expect( SpeedAlertSeverity.minor, - navigationSpeedAlertSeverityFromDto(SpeedAlertSeverityDto.minor), + SpeedAlertSeverityDto.minor.toSpeedAlertSeverity(), ); expect( SpeedAlertSeverity.notSpeeding, - navigationSpeedAlertSeverityFromDto(SpeedAlertSeverityDto.notSpeeding), + SpeedAlertSeverityDto.notSpeeding.toSpeedAlertSeverity(), ); expect( SpeedAlertSeverity.unknown, - navigationSpeedAlertSeverityFromDto(SpeedAlertSeverityDto.unknown), + SpeedAlertSeverityDto.unknown.toSpeedAlertSeverity(), ); }); @@ -308,11 +277,11 @@ void main() { test('Navigation lat lng point conversion to Pigeon DTO', () { expect( LatLngDto(latitude: 5.0, longitude: 6.0).latitude, - latLngToDto(const LatLng(latitude: 5.0, longitude: 6.0)).latitude, + const LatLng(latitude: 5.0, longitude: 6.0).toDto().latitude, ); expect( LatLngDto(latitude: 5.0, longitude: 6.0).longitude, - latLngToDto(const LatLng(latitude: 5.0, longitude: 6.0)).longitude, + const LatLng(latitude: 5.0, longitude: 6.0).toDto().longitude, ); }); @@ -321,67 +290,19 @@ void main() { SpeedingUpdatedEvent( percentageAboveLimit: 5.0, severity: SpeedAlertSeverity.major) .severity, - speedingUpdatedEventFromDto(SpeedingUpdatedEventDto( + SpeedingUpdatedEventDto( percentageAboveLimit: 5.0, severity: SpeedAlertSeverityDto.major, - )).severity, + ).toSpeedingUpdatedEvent().severity, ); expect( SpeedingUpdatedEvent( percentageAboveLimit: 5.0, severity: SpeedAlertSeverity.major) .percentageAboveLimit, - speedingUpdatedEventFromDto(SpeedingUpdatedEventDto( + SpeedingUpdatedEventDto( percentageAboveLimit: 5.0, severity: SpeedAlertSeverityDto.major, - )).percentageAboveLimit, - ); - }); - - test( - 'Road snapped location updated event message conversion from Pigeon DTO', - () { - expect( - RoadSnappedLocationUpdatedEvent( - location: const LatLng( - latitude: 5.0, - longitude: 9.0, - ), - ).location.latitude, - roadSnappedLocationUpdatedEventFromDto( - RoadSnappedLocationUpdatedEventDto( - location: LatLngDto( - latitude: 5.0, - longitude: 9.0, - ), - )).location.latitude, - ); - expect( - RoadSnappedLocationUpdatedEvent( - location: const LatLng( - latitude: 5.0, - longitude: 9.0, - ), - ).location.longitude, - roadSnappedLocationUpdatedEventFromDto( - RoadSnappedLocationUpdatedEventDto( - location: LatLngDto( - latitude: 5.0, - longitude: 9.0, - ), - )).location.longitude, - ); - }); - - test( - 'Navigation remaining time or distance changed event message conversion from Pigeon DTO', - () { - final RemainingTimeOrDistanceChangedEventDto event = - RemainingTimeOrDistanceChangedEventDto( - remainingDistance: 5000, remainingTime: 100); - - expect( - remainingTimeOrDistanceChangedEventFromDto(event).remainingDistance, - event.remainingDistance, + ).toSpeedingUpdatedEvent().percentageAboveLimit, ); }); @@ -395,7 +316,7 @@ void main() { ); final RouteSegmentTrafficDataRoadStretchRenderingData gmsData = - roadStretchRenderingDataFromDto(data); + data.toRouteSegmentTrafficDataRoadStretchRenderingData(); expect(data.lengthMeters, gmsData.lengthMeters); expect(data.offsetMeters, gmsData.offsetMeters); @@ -417,8 +338,7 @@ void main() { renderingData ]); - final RouteSegmentTrafficData gmsData = - routeSegmentTrafficDataFromDto(data); + final RouteSegmentTrafficData gmsData = data.toRouteSegmentTrafficData(); expect(data.status.toString().split('.').last, gmsData.status.toString().split('.').last); @@ -458,7 +378,7 @@ void main() { target: LatLngDto(latitude: 77.0, longitude: 88.0)), latLngs: [LatLngDto(latitude: 11.0, longitude: 22.0)]); - final RouteSegment gmsSegment = routeSegmentFromDto(segment); + final RouteSegment gmsSegment = segment.toRouteSegment(); expect(segment.destinationLatLng.latitude, gmsSegment.destinationLatLng.latitude);