diff --git a/.ci.yaml b/.ci.yaml index 94780f2513cb..0cc83481ae62 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -31,7 +31,7 @@ platform_properties: {"dependency": "android_sdk", "version": "version:33v6"}, {"dependency": "open_jdk", "version": "version:17"}, {"dependency": "curl", "version": "version:7.64.0"}, - {"dependency": "avd_cipd_version", "version": "build_id:8739520057779466577"} + {"dependency": "avd_cipd_version", "version": "build_id:8733065022087935185"} ] linux_android_legacy: properties: @@ -44,7 +44,7 @@ platform_properties: {"dependency": "open_jdk", "version": "version:17"}, {"dependency": "curl", "version": "version:7.64.0"}, {"dependency": "android_virtual_device", "version": "generic_android22.textpb"}, - {"dependency": "avd_cipd_version", "version": "build_id:8739520057779466577"} + {"dependency": "avd_cipd_version", "version": "build_id:8733065022087935185"} ] linux_desktop: properties: diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fb5dec2eaa02..56b0bf516de5 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ed553d10cf6370065c12d372027ff2d0cf621cf1 +ab5b20c16d56c8e608773ce65c3f7c39d515d4a5 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d81d08273008..04703cbc45e8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -47,7 +47,6 @@ updates: - /packages/palette_generator/example/android/app - /packages/path_provider/path_provider/example/android/app - /packages/path_provider/path_provider_android/example/android/app - - /packages/platform/example/android/app - /packages/pigeon/example/app/android/app - /packages/pigeon/platform_tests/test_plugin/example/android/app - /packages/pigeon/platform_tests/alternate_language_test_plugin/example/android/app @@ -70,6 +69,8 @@ updates: prefix: "[gradle]" schedule: interval: "weekly" + labels: + - "autosubmit" open-pull-requests-limit: 10 ignore: - dependency-name: "*" @@ -110,6 +111,8 @@ updates: - "junit:junit" - "org.mockito:*" - "org.robolectric:*" + labels: + - "autosubmit" ignore: - dependency-name: "com.android.tools.build:gradle" update-types: ["version-update:semver-minor", "version-update:semver-patch"] diff --git a/.github/labeler.yml b/.github/labeler.yml index c7d1a1d531e6..c21910d88b5a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -89,6 +89,11 @@ - any-glob-to-any-file: - packages/go_router_builder/**/* +'p: google_adsense': + - changed-files: + - any-glob-to-any-file: + - packages/google_adsense/** + 'p: google_identity_services': - changed-files: - any-glob-to-any-file: diff --git a/CODEOWNERS b/CODEOWNERS index 5a5749661927..2b4ab3554019 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,6 +18,7 @@ packages/flutter_migrate/** @stuartmorgan packages/flutter_template_images/** @stuartmorgan packages/go_router/** @chunhtai packages/go_router_builder/** @chunhtai +packages/google_adsense/** @sokoloff06 @ditman packages/google_identity_services_web/** @ditman packages/google_maps_flutter/** @stuartmorgan packages/google_sign_in/** @stuartmorgan diff --git a/README.md b/README.md index 2d728c32ac11..64dce4761744 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ These are the packages hosted in this repository: | [flutter\_template\_images](./packages/flutter_template_images/) | [![pub package](https://img.shields.io/pub/v/flutter_template_images.svg)](https://pub.dev/packages/flutter_template_images) | [![pub points](https://img.shields.io/pub/points/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_template_images)](https://pub.dev/packages/flutter_template_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_template_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_template_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_template_images?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_template_images) | | [go\_router](./packages/go_router/) | [![pub package](https://img.shields.io/pub/v/go_router.svg)](https://pub.dev/packages/go_router) | [![pub points](https://img.shields.io/pub/points/go_router)](https://pub.dev/packages/go_router/score) | [![popularity](https://img.shields.io/pub/popularity/go_router)](https://pub.dev/packages/go_router/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router) | | [go\_router\_builder](./packages/go_router_builder/) | [![pub package](https://img.shields.io/pub/v/go_router_builder.svg)](https://pub.dev/packages/go_router_builder) | [![pub points](https://img.shields.io/pub/points/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![popularity](https://img.shields.io/pub/popularity/go_router_builder)](https://pub.dev/packages/go_router_builder/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20go_router_builder?label=)](https://github.com/flutter/flutter/labels/p%3A%20go_router_builder) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20go_router_builder?label=)](https://github.com/flutter/packages/labels/p%3A%20go_router_builder) | +| [google\_adsense](./packages/google_adsense/)| [![pub package](https://img.shields.io/pub/v/google_adsense.svg)](https://pub.dev/packages/google_adsense) | [![pub points](https://img.shields.io/pub/points/google_adsense)](https://pub.dev/packages/google_adsense/score) | [![popularity](https://img.shields.io/pub/popularity/google_adsense)](https://pub.dev/packages/google_adsense/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_adsense?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_adsense) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_adsense?label=)](https://github.com/flutter/packages/labels/p%3A%20google_adsense) | | [google\_maps\_flutter](./packages/google_maps_flutter/) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | | [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) | | [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) | diff --git a/packages/animations/example/ios/Runner/AppDelegate.swift b/packages/animations/example/ios/Runner/AppDelegate.swift index d83c0ff0beea..4580c8e76da4 100644 --- a/packages/animations/example/ios/Runner/AppDelegate.swift +++ b/packages/animations/example/ios/Runner/AppDelegate.swift @@ -5,7 +5,7 @@ import Flutter import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 26fd240254f2..8e9f664a66fe 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D82918721FABF772705DB0 /* libPods-Runner.a */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -58,6 +59,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -153,13 +155,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 8CEC7AD219FB134B511EBA9D /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -170,7 +174,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -187,6 +191,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -227,28 +234,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 8CEC7AD219FB134B511EBA9D /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/camera_avfoundation/camera_avfoundation_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation/path_provider_foundation_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/video_player_avfoundation/video_player_avfoundation_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/camera_avfoundation_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/path_provider_foundation_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/video_player_avfoundation_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -499,6 +484,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1ff4b573d761..ba0c5508103c 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index c844cc61abbf..d5686e096e72 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.9+17 + +* Updates annotations lib to 1.9.1. + ## 0.10.9+16 * Updates annotations lib to 1.9.0. diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index cbe399571dcd..3929a63d54b0 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -66,7 +66,7 @@ buildFeatures { } dependencies { - implementation 'androidx.annotation:annotation:1.9.0' + implementation 'androidx.annotation:annotation:1.9.1' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:5.0.0' testImplementation 'androidx.test:core:1.4.0' diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 6bc18c3c7c63..10922c7557a9 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.9+16 +version: 0.10.9+17 environment: sdk: ^3.5.0 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 3a1ce1ee25e2..041d918da5b9 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 408D7A792C3C9CD000B71F9A /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 408D7A782C3C9CD000B71F9A /* OCMock */; }; 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 7D5FCCD42AEF9D0200FB7108 /* CameraSettingsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D5FCCD32AEF9D0200FB7108 /* CameraSettingsTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -116,6 +117,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -261,13 +263,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - DC38DE83659461A2CFD30C81 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -301,6 +305,7 @@ ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, 408D7A772C3C9CD000B71F9A /* XCRemoteSwiftPackageReference "ocmock" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; @@ -410,28 +415,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - DC38DE83659461A2CFD30C81 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/camera_avfoundation/camera_avfoundation_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation/path_provider_foundation_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/video_player_avfoundation/video_player_avfoundation_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/camera_avfoundation_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/path_provider_foundation_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/video_player_avfoundation_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -742,6 +725,13 @@ }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCRemoteSwiftPackageReference section */ 408D7A772C3C9CD000B71F9A /* XCRemoteSwiftPackageReference "ocmock" */ = { isa = XCRemoteSwiftPackageReference; @@ -759,6 +749,10 @@ package = 408D7A772C3C9CD000B71F9A /* XCRemoteSwiftPackageReference "ocmock" */; productName = OCMock; }; + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 5e1cf7e14ad7..ba0c5508103c 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index 9f54c535f0af..ccc8b201c2ed 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -60,7 +60,7 @@ dependencies { implementation 'com.google.guava:guava:33.3.1-android' implementation 'com.squareup.okhttp3:okhttp:4.11.0' implementation 'com.google.code.gson:gson:2.10.1' - androidTestImplementation 'org.hamcrest:hamcrest:2.2' + androidTestImplementation 'org.hamcrest:hamcrest:3.0' testImplementation 'junit:junit:4.13.2' testImplementation "com.google.truth:truth:1.1.3" diff --git a/packages/espresso/example/android/app/build.gradle b/packages/espresso/example/android/app/build.gradle index 84b682fdb760..3e13c573600d 100644 --- a/packages/espresso/example/android/app/build.gradle +++ b/packages/espresso/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdk flutter.compileSdkVersion // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). diff --git a/packages/espresso/example/android/build.gradle b/packages/espresso/example/android/build.gradle index 1365397dabbf..31e331a7ec51 100644 --- a/packages/espresso/example/android/build.gradle +++ b/packages/espresso/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.1' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/espresso/example/android/settings.gradle b/packages/espresso/example/android/settings.gradle index 32735a3cfd4b..e3b0c2297977 100644 --- a/packages/espresso/example/android/settings.gradle +++ b/packages/espresso/example/android/settings.gradle @@ -1,28 +1,28 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.1" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" + } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle b/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle index 03fe48c2210b..20d6cf2df8ab 100644 --- a/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle +++ b/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle @@ -1,3 +1,10 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" + id "com.google.gms.google-services" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +13,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +23,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'com.google.gms.google-services' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdk flutter.compileSdkVersion @@ -37,7 +35,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "io.flutter.plugins.googlesigninexample" minSdkVersion flutter.minSdkVersion - targetSdkVersion 30 + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml b/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml index b537750e63d0..c1040e04aa95 100644 --- a/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml +++ b/packages/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize"> + + + + + + + + + + + + + + + + + + + + google_adsense_example + + + + + + diff --git a/packages/google_adsense/example/web/manifest.json b/packages/google_adsense/example/web/manifest.json new file mode 100644 index 000000000000..79d612a06590 --- /dev/null +++ b/packages/google_adsense/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "google_adsense_example", + "short_name": "google_adsense_example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/packages/google_adsense/lib/experimental/ad_unit_widget.dart b/packages/google_adsense/lib/experimental/ad_unit_widget.dart new file mode 100644 index 000000000000..9d165298d40d --- /dev/null +++ b/packages/google_adsense/lib/experimental/ad_unit_widget.dart @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export '../src/adsense/adsense.dart'; diff --git a/packages/google_adsense/lib/google_adsense.dart b/packages/google_adsense/lib/google_adsense.dart new file mode 100644 index 000000000000..ac0e7ae01c56 --- /dev/null +++ b/packages/google_adsense/lib/google_adsense.dart @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/core/google_adsense.dart'; diff --git a/packages/google_adsense/lib/src/adsense/ad_unit_configuration.dart b/packages/google_adsense/lib/src/adsense/ad_unit_configuration.dart new file mode 100644 index 000000000000..740ce728a4a2 --- /dev/null +++ b/packages/google_adsense/lib/src/adsense/ad_unit_configuration.dart @@ -0,0 +1,185 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import 'ad_unit_params.dart'; + +/// Configuration to customize the [AdUnitWidget]. +/// +/// Contains named constructors for the following supported ad unit types: +/// +/// * Display (see: [AdUnitConfiguration.displayAdUnit]) +/// * In-feed (see: [AdUnitConfiguration.inFeedAdUnit]) +/// * In-article (see: [AdUnitConfiguration.inArticleAdUnit]) +/// * Multiplex (see: [AdUnitConfiguration.multiplexAdUnit]) +/// +/// Each constructor will use one or more of the following arguments: +/// +/// {@template pkg_google_adsense_parameter_adSlot} +/// * [adSlot]: Identifies a specific ad unit from the AdSense console. +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_adFormat} +/// * [adFormat]: (Desktop only) Specifies a general shape (horizontal, vertical, +/// and/or rectangle) that this ad unit should conform to. To learn more, check: +/// [How to use responsive ad tag parameters](https://support.google.com/adsense/answer/9183460). +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_adLayout} +/// * [adLayout]: Customizes the layout of this ad unit. See: +/// [Customize your in-feed ad](https://support.google.com/adsense/answer/9189957). +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_adLayoutKey} +/// * [adLayoutKey]: The key identifying the layout for this ad unit. +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_matchedContentUiType} +/// * [matchedContentUiType]: Controls the arrangement of the text and images in +/// this Multiplex ad unit. For example, you can choose to have the image and +/// text side by side, the image above the text, etc. More information: +/// [How to customize your responsive Multiplex ad unit](https://support.google.com/adsense/answer/7533385) +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_rowsNum} +/// * [rowsNum]: Specifies how many rows to show within the Multiplex ad unit grid. +/// Requires [matchedContentUiType] to be set. +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_columnsNum} +/// * [columnsNum]: Specifies how many columns to show within the Multiplex ad unit grid. +/// Requires [matchedContentUiType] to be set. +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_isFullWidthResponsive} +/// * [isFullWidthResponsive]: Determines whether this responsive ad unit expands +/// to use the full width of a visitor's mobile device screen. See: +/// [How to use responsive ad tag parameters](https://support.google.com/adsense/answer/9183460). +/// {@endtemplate} +/// {@template pkg_google_adsense_parameter_isAdTest} +/// * [isAdTest]: Whether this ad will be shown in a test environment. Defaults to `true` in debug mode. +/// {@endtemplate} +/// +/// For more information about ad units, check the +/// [Ad formats FAQ](https://support.google.com/adsense/answer/10734935) +/// in the Google AdSense Help. +class AdUnitConfiguration { + AdUnitConfiguration._internal({ + required String adSlot, + AdFormat? adFormat, + AdLayout? adLayout, + String? adLayoutKey, + MatchedContentUiType? matchedContentUiType, + int? rowsNum, + int? columnsNum, + bool? isFullWidthResponsive = true, + bool? isAdTest, + }) : _adUnitParams = { + AdUnitParams.AD_SLOT: adSlot, + if (adFormat != null) AdUnitParams.AD_FORMAT: adFormat.toString(), + if (adLayout != null) AdUnitParams.AD_LAYOUT: adLayout.toString(), + if (adLayoutKey != null) AdUnitParams.AD_LAYOUT_KEY: adLayoutKey, + if (isFullWidthResponsive != null) + AdUnitParams.FULL_WIDTH_RESPONSIVE: + isFullWidthResponsive.toString(), + if (matchedContentUiType != null) + AdUnitParams.MATCHED_CONTENT_UI_TYPE: + matchedContentUiType.toString(), + if (columnsNum != null) + AdUnitParams.MATCHED_CONTENT_COLUMNS_NUM: columnsNum.toString(), + if (rowsNum != null) + AdUnitParams.MATCHED_CONTENT_ROWS_NUM: rowsNum.toString(), + if (isAdTest != null && isAdTest) AdUnitParams.AD_TEST: 'on', + }; + + /// Creates a configuration object for a Multiplex ad. + /// + /// Arguments: + /// + /// {@macro pkg_google_adsense_parameter_adSlot} + /// {@macro pkg_google_adsense_parameter_adFormat} + /// {@macro pkg_google_adsense_parameter_matchedContentUiType} + /// {@macro pkg_google_adsense_parameter_rowsNum} + /// {@macro pkg_google_adsense_parameter_columnsNum} + /// {@macro pkg_google_adsense_parameter_isFullWidthResponsive} + /// {@macro pkg_google_adsense_parameter_isAdTest} + AdUnitConfiguration.multiplexAdUnit({ + required String adSlot, + required AdFormat adFormat, + MatchedContentUiType? matchedContentUiType, + int? rowsNum, + int? columnsNum, + bool isFullWidthResponsive = true, + bool isAdTest = kDebugMode, + }) : this._internal( + adSlot: adSlot, + adFormat: adFormat, + matchedContentUiType: matchedContentUiType, + rowsNum: rowsNum, + columnsNum: columnsNum, + isFullWidthResponsive: isFullWidthResponsive, + isAdTest: isAdTest); + + /// Creates a configuration object for an In-feed ad. + /// + /// Arguments: + /// + /// {@macro pkg_google_adsense_parameter_adSlot} + /// {@macro pkg_google_adsense_parameter_adLayoutKey} + /// {@macro pkg_google_adsense_parameter_adFormat} + /// {@macro pkg_google_adsense_parameter_isFullWidthResponsive} + /// {@macro pkg_google_adsense_parameter_isAdTest} + AdUnitConfiguration.inFeedAdUnit({ + required String adSlot, + required String adLayoutKey, + AdFormat? adFormat, + bool isFullWidthResponsive = true, + bool isAdTest = kDebugMode, + }) : this._internal( + adSlot: adSlot, + adFormat: adFormat, + adLayoutKey: adLayoutKey, + isFullWidthResponsive: isFullWidthResponsive, + isAdTest: isAdTest); + + /// Creates a configuration object for an In-article ad. + /// + /// Arguments: + /// + /// {@macro pkg_google_adsense_parameter_adSlot} + /// {@macro pkg_google_adsense_parameter_adFormat} + /// {@macro pkg_google_adsense_parameter_adLayout} + /// {@macro pkg_google_adsense_parameter_isFullWidthResponsive} + /// {@macro pkg_google_adsense_parameter_isAdTest} + AdUnitConfiguration.inArticleAdUnit({ + required String adSlot, + AdFormat? adFormat, + AdLayout adLayout = AdLayout.IN_ARTICLE, + bool isFullWidthResponsive = true, + bool isAdTest = kDebugMode, + }) : this._internal( + adSlot: adSlot, + adFormat: adFormat, + adLayout: adLayout, + isFullWidthResponsive: isFullWidthResponsive, + isAdTest: isAdTest); + + /// Creates a configuration object for a Display ad. + /// + /// Arguments: + /// + /// {@macro pkg_google_adsense_parameter_adSlot} + /// {@macro pkg_google_adsense_parameter_adFormat} + /// {@macro pkg_google_adsense_parameter_isFullWidthResponsive} + /// {@macro pkg_google_adsense_parameter_isAdTest} + AdUnitConfiguration.displayAdUnit({ + required String adSlot, + AdFormat? adFormat, + bool isFullWidthResponsive = true, + bool isAdTest = kDebugMode, + }) : this._internal( + adSlot: adSlot, + adFormat: adFormat, + isFullWidthResponsive: isFullWidthResponsive, + isAdTest: isAdTest); + + Map _adUnitParams; + + /// `Map` representation of this configuration object. + Map get params => _adUnitParams; +} diff --git a/packages/google_adsense/lib/src/adsense/ad_unit_params.dart b/packages/google_adsense/lib/src/adsense/ad_unit_params.dart new file mode 100644 index 000000000000..d31a7c2c5fa7 --- /dev/null +++ b/packages/google_adsense/lib/src/adsense/ad_unit_params.dart @@ -0,0 +1,146 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Contains some of the possible adUnitParams keys constants for convenience and documentation +class AdUnitParams { + /// Identifies AdSense publisher account. Should be passed on initialization + static const String AD_CLIENT = 'adClient'; + + /// Identifies a specific ad unit from AdSense console. Can be taken from the ad unit HTML snippet under `data-ad-slot` parameter + static const String AD_SLOT = 'adSlot'; + + /// (Optional) Specify a general shape (desktop only) (horizontal, vertical, and/or rectangle) that your ad unit should conform to + /// See [docs](https://support.google.com/adsense/answer/9183460?hl=en&ref_topic=9183242&sjid=2004567335727763076-EU#:~:text=Specify%20a%20general%20shape%20(desktop%20only)) for details + static const String AD_FORMAT = 'adFormat'; + + /// (Optional) The data-full-width-responsive parameter determines whether your responsive ad unit expands to use the full width of your visitor's mobile device screen. + /// See [docs](https://support.google.com/adsense/answer/9183460?hl=en&ref_topic=9183242&sjid=2004567335727763076-EU#:~:text=Set%20the%20behavior%20of%20full%2Dwidth%20responsive%20ads%20on%20mobile%20devices) for details + static const String FULL_WIDTH_RESPONSIVE = 'fullWidthResponsive'; + + /// (Optional) Use value provided in the AdSense code generated in AdSense Console + static const String AD_LAYOUT_KEY = 'adLayoutKey'; + + /// (Optional) Use value provided in the AdSense code generated in AdSense Console + static const String AD_LAYOUT = 'adLayout'; + + /// (Optional) This parameter lets you control the arrangement of the text and images in your Multiplex ad units. For example, you can choose to have the image and text side by side, the image above the text, etc. + /// See [MultiplexLayout] + /// See [docs](https://support.google.com/adsense/answer/7533385?hl=en#:~:text=Change%20the%20layout%20of%20your%20Multiplex%20ad%20unit) + static const String MATCHED_CONTENT_UI_TYPE = 'matchedContentUiType'; + + /// The ads inside a Multiplex ad unit are arranged in a grid. You can specify how many rows and columns you want to show within that grid
+ /// Sets the number of rows
+ /// Requires setting [AdUnitParams.MATCHED_CONTENT_UI_TYPE] + static const String MATCHED_CONTENT_ROWS_NUM = 'macthedContentRowsNum'; + + /// The ads inside a Multiplex ad unit are arranged in a grid. You can specify how many rows and columns you want to show within that grid
+ /// Sets the number of columns
+ /// Requires setting [AdUnitParams.MATCHED_CONTENT_UI_TYPE] + static const String MATCHED_CONTENT_COLUMNS_NUM = 'macthedContentColumnsNum'; + + /// testing environment flag, defaults to kIsDebug + static const String AD_TEST = 'adtest'; +} + +/// Specifies the general shape that the ad unit should conform to. +/// +/// See [docs](https://support.google.com/adsense/answer/9183460?hl=en&ref_topic=9183242&sjid=2004567335727763076-EU#:~:text=Specify%20a%20general%20shape%20(desktop%20only)) for details. +enum AdFormat { + /// Default which enables the auto-sizing behavior for the responsive ad unit + AUTO('auto'), + + /// Use horizontal shape + HORIZONTAL('horizontal'), + + /// Use rectangle shape + RECTANGLE('rectangle'), + + /// Use vertical shape + VERTICAL('vertical'), + + /// Use horizontal and rectangle shape + HORIZONTAL_RECTANGLE('horizontal,rectangle'), + + /// Use horizontal and vertical shape + HORIZONTAL_VERTICAL('horizontal,vertical'), + + /// Use rectangle and vertical shape + RECTANGLE_VERTICAL('rectangle,vertical'), + + /// Use horizontal, rectangle and vertical shape + HORIZONTAL_RECTANGLE_VERTICAL('horizontal,rectangle,vertical'), + + /// Fluid ads have no fixed size, but rather adapt to fit the creative content they display + FLUID('fluid'); + + const AdFormat(this._adFormat); + final String _adFormat; + + @override + String toString() => _adFormat; +} + +/// Controls the general layout of an in-feed/in-article ad unit. +// TODO(sokoloff06): find docs link! +enum AdLayout { + /// + IMAGE_TOP('image-top'), + + /// + IMAGE_MIDDLE('image-middle'), + + /// + IMAGE_SIDE('image-side'), + + /// + TEXT_ONLY('text-only'), + + /// + IN_ARTICLE('in-article'); + + const AdLayout(this._adLayout); + final String _adLayout; + + @override + String toString() => _adLayout; +} + +/// Controls the arrangement of the text and images in a Multiplex ad unit. +/// +/// See [docs](https://support.google.com/adsense/answer/7533385?hl=en#:~:text=Change%20the%20layout%20of%20your%20Multiplex%20ad%20unit). +enum MatchedContentUiType { + /// In this layout, the image and text appear alongside each other. + IMAGE_CARD_SIDEBYSIDE('image_card_sidebyside'), + + /// In this layout, the image and text appear alongside each other within a card. + IMAGE_SIDEBYSIDE('image_sidebyside'), + + /// In this layout, the image and text are arranged one on top of the other. + IMAGE_STACKED('image_stacked'), + + /// In this layout, the image and text are arranged one on top of the other within a card. + IMAGE_CARD_STACKED('image_card_stacked'), + + /// A text-only layout with no image. + TEXT('text'), + + /// A text-only layout within a card. + TEXT_CARD('text_card'); + + const MatchedContentUiType(this._uiType); + final String _uiType; + + @override + String toString() => _uiType; +} + +/// After an ad unit has finished requesting an ad, AdSense adds a parameter to the element called data-ad-status. Note: data-ad-status should not be confused with data-adsbygoogle-status, which is used by our ad code for ads processing purposes. +/// See [docs](https://support.google.com/adsense/answer/10762946?hl=en) for more information +class AdStatus { + /// Indicates ad slot was filled + static const String FILLED = 'filled'; + + /// Indicates ad slot was not filled + static const String UNFILLED = 'unfilled'; +} diff --git a/packages/google_adsense/lib/src/adsense/ad_unit_widget.dart b/packages/google_adsense/lib/src/adsense/ad_unit_widget.dart new file mode 100644 index 000000000000..6771c468cc1c --- /dev/null +++ b/packages/google_adsense/lib/src/adsense/ad_unit_widget.dart @@ -0,0 +1,183 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; + +import 'package:flutter/widgets.dart'; +import 'package:web/web.dart' as web; + +import '../core/google_adsense.dart'; +import '../js_interop/adsbygoogle.dart'; +import '../utils/logging.dart'; +import 'ad_unit_configuration.dart'; +import 'ad_unit_params.dart'; +import 'adsense_js_interop.dart'; + +/// Widget displaying an ad unit. +/// +/// See the [AdUnitConfiguration] object for a complete reference of the available +/// parameters. +/// +/// When specifying an [AdFormat], the widget will behave like a "responsive" +/// ad unit. Responsive ad units might attempt to overflow the container in which +/// they're rendered. +/// +/// To create a fully constrained widget (specific width/height), do *not* pass +/// an `AdFormat` value, and wrap the `AdUnitWidget` in a constrained box from +/// Flutter, like a [Container] or [SizedBox]. +class AdUnitWidget extends StatefulWidget { + /// Constructs [AdUnitWidget] + AdUnitWidget({ + super.key, + required AdUnitConfiguration configuration, + @visibleForTesting String? adClient, + }) : _adClient = adClient ?? adSense.adClient, + _adUnitConfiguration = configuration { + assert(_adClient != null, + 'Attempted to render an AdUnitWidget before calling adSense.initialize'); + } + + final String? _adClient; + + final AdUnitConfiguration _adUnitConfiguration; + + @override + State createState() => _AdUnitWidgetWebState(); +} + +class _AdUnitWidgetWebState extends State + with AutomaticKeepAliveClientMixin { + static int _adUnitCounter = 0; + static final JSString _adStatusKey = 'adStatus'.toJS; + + // Limit height to minimize layout shift in case ad is not loaded + Size _adSize = const Size(double.infinity, 1.0); + + @override + bool get wantKeepAlive => true; + + static final web.ResizeObserver _adSenseResizeObserver = web.ResizeObserver( + (JSArray entries, web.ResizeObserver observer) { + for (final web.ResizeObserverEntry entry in entries.toDart) { + final web.Element target = entry.target; + if (target.isConnected) { + // First time resized since attached to DOM -> attachment callback from Flutter docs by David + _onElementAttached(target as web.HTMLElement); + observer.disconnect(); + } + } + }.toJS); + + @override + Widget build(BuildContext context) { + super.build(context); + // If the ad is collapsed (0x0), return an empty widget + if (_adSize.isEmpty) { + return const SizedBox.shrink(); + } + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + if (!widget._adUnitConfiguration.params + .containsKey(AdUnitParams.AD_FORMAT)) { + _adSize = Size(_adSize.width, constraints.maxHeight); + } + return SizedBox( + height: _adSize.height, + width: _adSize.width, + child: HtmlElementView.fromTagName( + tagName: 'div', + onElementCreated: _onElementCreated, + ), + ); + }); + } + + void _onElementCreated(Object element) { + // Create the `ins` element that is going to contain the actual ad. + final web.HTMLElement insElement = + (web.document.createElement('ins') as web.HTMLElement) + ..className = 'adsbygoogle' + ..style.width = '100%' + ..style.height = '100%' + ..style.display = 'block'; + + // Apply the widget configuration to insElement + { + AdUnitParams.AD_CLIENT: 'ca-pub-${widget._adClient}', + ...widget._adUnitConfiguration.params, + }.forEach((String key, String value) { + insElement.dataset.setProperty(key.toJS, value.toJS); + }); + + // Adding ins inside of the adUnit + final web.HTMLDivElement adUnitDiv = element as web.HTMLDivElement + ..id = 'adUnit${_adUnitCounter++}' + ..append(insElement); + + // Using Resize observer to detect element attached to DOM + _adSenseResizeObserver.observe(adUnitDiv); + + // Using Mutation Observer to detect when adslot is being loaded based on https://support.google.com/adsense/answer/10762946?hl=en + web.MutationObserver( + (JSArray entries, web.MutationObserver observer) { + for (final JSObject entry in entries.toDart) { + final web.HTMLElement target = + (entry as web.MutationRecord).target as web.HTMLElement; + if (_isLoaded(target)) { + if (_isFilled(target)) { + debugLog( + 'Resizing widget based on target $target size of ${target.offsetWidth} x ${target.offsetHeight}'); + _updateWidgetSize(Size( + target.offsetWidth.toDouble(), + // This is always the width of the platform view! + target.offsetHeight.toDouble(), + )); + } else { + // This removes the platform view. + _updateWidgetSize(Size.zero); + } + } + } + }.toJS) + .observe( + insElement, + web.MutationObserverInit( + attributes: true, + attributeFilter: ['data-ad-status'.toJS].toJS, + )); + } + + static void _onElementAttached(web.HTMLElement element) { + debugLog( + '$element attached with w=${element.offsetWidth} and h=${element.offsetHeight}'); + debugLog( + '${element.firstChild} size is ${(element.firstChild! as web.HTMLElement).offsetWidth}x${(element.firstChild! as web.HTMLElement).offsetHeight} '); + adsbygoogle.requestAd(); + } + + bool _isLoaded(web.HTMLElement target) { + final bool isLoaded = + target.dataset.getProperty(_adStatusKey).isDefinedAndNotNull; + debugLog('Ad isLoaded: $isLoaded'); + return isLoaded; + } + + bool _isFilled(web.HTMLElement target) { + final String? adStatus = + target.dataset.getProperty(_adStatusKey)?.toDart; + debugLog('Ad isFilled? $adStatus'); + if (adStatus == AdStatus.FILLED) { + return true; + } + return false; + } + + void _updateWidgetSize(Size newSize) { + debugLog('Resizing AdUnitWidget to $newSize'); + setState(() { + _adSize = newSize; + }); + } +} diff --git a/packages/google_adsense/lib/src/adsense/adsense.dart b/packages/google_adsense/lib/src/adsense/adsense.dart new file mode 100644 index 000000000000..07781299c901 --- /dev/null +++ b/packages/google_adsense/lib/src/adsense/adsense.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'ad_unit_configuration.dart'; +export 'ad_unit_params.dart' hide AdStatus, AdUnitParams; +export 'ad_unit_widget.dart'; diff --git a/packages/google_adsense/lib/src/adsense/adsense_js_interop.dart b/packages/google_adsense/lib/src/adsense/adsense_js_interop.dart new file mode 100644 index 000000000000..d3b6fa53bb21 --- /dev/null +++ b/packages/google_adsense/lib/src/adsense/adsense_js_interop.dart @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:js_interop'; + +import '../js_interop/adsbygoogle.dart'; + +/// Adds a `requestAd` method to request an AdSense ad. +extension AdsByGoogleExtension on AdsByGoogle { + /// Convenience method for invoking push() with an empty object + void requestAd() { + push(JSObject()); + } +} diff --git a/packages/google_adsense/lib/src/core/google_adsense.dart b/packages/google_adsense/lib/src/core/google_adsense.dart new file mode 100644 index 000000000000..3a56b5af92dc --- /dev/null +++ b/packages/google_adsense/lib/src/core/google_adsense.dart @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:js_interop'; + +import 'package:flutter/widgets.dart'; +import 'package:web/web.dart' as web; + +import '../js_interop/adsbygoogle.dart' show adsbygooglePresent; +import '../js_interop/package_web_tweaks.dart'; + +import '../utils/logging.dart'; + +/// The web implementation of the AdSense API. +class AdSense { + bool _isInitialized = false; + + /// The [Publisher ID](https://support.google.com/adsense/answer/2923881). + late String adClient; + static const String _url = + 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-'; + + /// Initializes the AdSense SDK with your [adClient]. + /// + /// The [adClient] parameter is your AdSense [Publisher ID](https://support.google.com/adsense/answer/2923881). + /// + /// Should be called ASAP, ideally in the `main` method. + Future initialize( + String adClient, { + @visibleForTesting bool skipJsLoader = false, + @visibleForTesting web.HTMLElement? jsLoaderTarget, + }) async { + if (_isInitialized) { + debugLog('adSense.initialize called multiple times. Skipping init.'); + return; + } + this.adClient = adClient; + if (!(skipJsLoader || _sdkAlreadyLoaded(testingTarget: jsLoaderTarget))) { + _loadJsSdk(adClient, jsLoaderTarget); + } else { + debugLog('SDK already on page. Skipping init.'); + } + _isInitialized = true; + } + + bool _sdkAlreadyLoaded({ + web.HTMLElement? testingTarget, + }) { + final String selector = 'script[src*=ca-pub-$adClient]'; + return adsbygooglePresent || + web.document.querySelector(selector) != null || + testingTarget?.querySelector(selector) != null; + } + + void _loadJsSdk(String adClient, web.HTMLElement? testingTarget) { + final String finalUrl = _url + adClient; + + final web.HTMLScriptElement script = web.HTMLScriptElement() + ..async = true + ..crossOrigin = 'anonymous'; + + if (web.window.nullableTrustedTypes != null) { + final String trustedTypePolicyName = 'adsense-dart-$adClient'; + try { + final web.TrustedTypePolicy policy = + web.window.trustedTypes.createPolicy( + trustedTypePolicyName, + web.TrustedTypePolicyOptions( + createScriptURL: ((JSString url) => url).toJS, + )); + script.trustedSrc = policy.createScriptURLNoArgs(finalUrl); + } catch (e) { + throw TrustedTypesException(e.toString()); + } + } else { + debugLog('TrustedTypes not available.'); + script.src = finalUrl; + } + + (testingTarget ?? web.document.head)!.appendChild(script); + } +} + +/// The singleton instance of the AdSense SDK. +final AdSense adSense = AdSense(); diff --git a/packages/google_adsense/lib/src/js_interop/adsbygoogle.dart b/packages/google_adsense/lib/src/js_interop/adsbygoogle.dart new file mode 100644 index 000000000000..bf97d23e978c --- /dev/null +++ b/packages/google_adsense/lib/src/js_interop/adsbygoogle.dart @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@JS() +library; + +import 'dart:js_interop'; + +/// JS-interop mappings to the window.adsbygoogle object. +extension type AdsByGoogle._(JSObject _) implements JSObject { + /// Pushes some `params` to the `window.adsbygoogle` object. + external void push(JSObject params); +} + +// window.adsbygoogle may be null if this package runs before the JS SDK loads. +@JS('adsbygoogle') +external AdsByGoogle? get _adsbygoogle; + +// window.adsbygoogle uses "duck typing", so let us set anything to it. +@JS('adsbygoogle') +external set _adsbygoogle(JSAny? value); + +/// Whether or not the `window.adsbygoogle` object is defined and not null. +bool get adsbygooglePresent => _adsbygoogle.isDefinedAndNotNull; + +/// Binding to the `adsbygoogle` JS global. +/// +/// See: https://support.google.com/adsense/answer/9274516?hl=en&ref_topic=28893&sjid=11495822575537499409-EU +AdsByGoogle get adsbygoogle { + if (!adsbygooglePresent) { + // Initialize _adsbygoole to "something that has a push method". + _adsbygoogle = JSArray(); + } + return _adsbygoogle!; +} diff --git a/packages/google_adsense/lib/src/js_interop/package_web_tweaks.dart b/packages/google_adsense/lib/src/js_interop/package_web_tweaks.dart new file mode 100644 index 000000000000..7f819dc6b9c5 --- /dev/null +++ b/packages/google_adsense/lib/src/js_interop/package_web_tweaks.dart @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:js_interop'; + +import 'package:web/web.dart' as web; + +// Re-use of https://github.com/flutter/packages/blob/main/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart + +/// This extension gives web.window a nullable getter to the `trustedTypes` +/// property, which needs to be used to check for feature support. +extension NullableTrustedTypesGetter on web.Window { + /// (Nullable) Bindings to window.trustedTypes. + /// + /// This may be null if the browser doesn't support the Trusted Types API. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API + @JS('trustedTypes') + external web.TrustedTypePolicyFactory? get nullableTrustedTypes; +} + +/// Allows setting a TrustedScriptURL as the src of a script element. +extension TrustedTypeSrcAttribute on web.HTMLScriptElement { + @JS('src') + external set trustedSrc(web.TrustedScriptURL value); +} + +/// Allows creating a script URL only from a string, with no arguments. +extension CreateScriptUrlNoArgs on web.TrustedTypePolicy { + /// Allows calling `createScriptURL` with only the `input` argument. + @JS('createScriptURL') + external web.TrustedScriptURL createScriptURLNoArgs(String input); +} + +/// Exception thrown if the Trusted Types feature is supported, enabled, and it +/// has prevented this loader from injecting the JS SDK. +class TrustedTypesException implements Exception { + /// + TrustedTypesException(this.message); + + /// The message of the exception + final String message; + + @override + String toString() => 'TrustedTypesException: $message'; +} diff --git a/packages/google_adsense/lib/src/utils/logging.dart b/packages/google_adsense/lib/src/utils/logging.dart new file mode 100644 index 000000000000..5b717bd0ed3c --- /dev/null +++ b/packages/google_adsense/lib/src/utils/logging.dart @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:js_interop'; + +import 'package:flutter/foundation.dart' show kDebugMode; +import 'package:web/web.dart' as web; + +/// Logs [log] to the JS console with debug level, if [kDebugMode] is `true`. +void debugLog(String log) { + if (kDebugMode) { + web.console.debug('[google_adsense] $log'.toJS); + } +} diff --git a/packages/google_adsense/pubspec.yaml b/packages/google_adsense/pubspec.yaml new file mode 100644 index 000000000000..5739583d2689 --- /dev/null +++ b/packages/google_adsense/pubspec.yaml @@ -0,0 +1,28 @@ +name: google_adsense +description: A wrapper plugin with convenience APIs allowing easier inserting Google Adsense HTML snippets withing a Flutter UI Web application +repository: https://github.com/flutter/packages/tree/main/packages/google_adsense +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_adsense%22 +version: 0.0.2 + +environment: + sdk: ^3.4.0 + flutter: ">=3.22.0" + +dependencies: + flutter: + sdk: flutter + web: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +topics: + - monetization + - ads + +screenshots: + - description: 'Ad displayed in the example app on the desktop browser' + path: example/images/desktop_screenshot.jpg + - description: 'Ad displayed in the example app on the mobile browser' + path: example/images/mobile_screenshot.png \ No newline at end of file diff --git a/packages/google_adsense/test/adsense_stub_test.dart b/packages/google_adsense/test/adsense_stub_test.dart new file mode 100644 index 000000000000..24e450e7ca78 --- /dev/null +++ b/packages/google_adsense/test/adsense_stub_test.dart @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: avoid_print + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Tell the user where to find the real tests', () { + print('---'); + print('This package uses integration_test for its main tests.'); + print('See `example/README.md` for more info.'); + print('---'); + }); +} diff --git a/packages/google_identity_services_web/CHANGELOG.md b/packages/google_identity_services_web/CHANGELOG.md index f6d8de6fa0b9..74323f88c30e 100644 --- a/packages/google_identity_services_web/CHANGELOG.md +++ b/packages/google_identity_services_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.3 + +* Moves all the JavaScript types to extend `JSObject`. + ## 0.3.2 * Adds the `nonce` parameter to `loadWebSdk`. diff --git a/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart b/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart index 1d1c5c29ea81..6c5da4bcd149 100644 --- a/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart +++ b/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart @@ -61,25 +61,24 @@ void main() async { utils.createExpectConfigValue(config as JSObject); expectConfigValue('client_id', 'testing_1-2-3'); - expectConfigValue('auto_select', isFalse); + expectConfigValue('auto_select', false); expectConfigValue('callback', utils.isAJs('function')); expectConfigValue('login_uri', 'https://www.example.com/login'); expectConfigValue('native_callback', utils.isAJs('function')); - expectConfigValue('cancel_on_tap_outside', isFalse); - expectConfigValue('allowed_parent_origin', isA>()); + expectConfigValue('cancel_on_tap_outside', false); + expectConfigValue( + 'allowed_parent_origin', ['allowed', 'another']); expectConfigValue('prompt_parent_id', 'some_dom_id'); expectConfigValue('nonce', 's0m3_r4ndOM_vALu3'); expectConfigValue('context', 'signin'); expectConfigValue('state_cookie_domain', 'subdomain.example.com'); expectConfigValue('ux_mode', 'popup'); - expectConfigValue( - 'allowed_parent_origin', ['allowed', 'another']); expectConfigValue( 'intermediate_iframe_close_callback', utils.isAJs('function')); - expectConfigValue('itp_support', isTrue); + expectConfigValue('itp_support', true); expectConfigValue('login_hint', 'login-hint@example.com'); expectConfigValue('hd', 'hd_value'); - expectConfigValue('use_fedcm_for_prompt', isTrue); + expectConfigValue('use_fedcm_for_prompt', true); }); }); diff --git a/packages/google_identity_services_web/example/integration_test/js_interop_oauth_test.dart b/packages/google_identity_services_web/example/integration_test/js_interop_oauth_test.dart index cf243b0859e7..b65b5d8a8e33 100644 --- a/packages/google_identity_services_web/example/integration_test/js_interop_oauth_test.dart +++ b/packages/google_identity_services_web/example/integration_test/js_interop_oauth_test.dart @@ -43,9 +43,9 @@ void main() async { expectConfigValue('client_id', 'testing_1-2-3'); expectConfigValue('callback', utils.isAJs('function')); expectConfigValue('scope', 'one two three'); - expectConfigValue('include_granted_scopes', isTrue); + expectConfigValue('include_granted_scopes', true); expectConfigValue('prompt', 'some-prompt'); - expectConfigValue('enable_granular_consent', isTrue); + expectConfigValue('enable_granular_consent', true); expectConfigValue('login_hint', 'login-hint@example.com'); expectConfigValue('hd', 'hd_value'); expectConfigValue('state', 'some-state'); @@ -66,9 +66,9 @@ void main() async { utils.createExpectConfigValue(config as JSObject); expectConfigValue('scope', 'one two three'); - expectConfigValue('include_granted_scopes', isTrue); + expectConfigValue('include_granted_scopes', true); expectConfigValue('prompt', 'some-prompt'); - expectConfigValue('enable_granular_consent', isTrue); + expectConfigValue('enable_granular_consent', true); expectConfigValue('login_hint', 'login-hint@example.com'); expectConfigValue('state', 'some-state'); }); @@ -93,15 +93,15 @@ void main() async { utils.createExpectConfigValue(config as JSObject); expectConfigValue('scope', 'one two three'); - expectConfigValue('include_granted_scopes', isTrue); + expectConfigValue('include_granted_scopes', true); expectConfigValue('redirect_uri', 'https://www.example.com/login'); expectConfigValue('callback', utils.isAJs('function')); expectConfigValue('state', 'some-state'); - expectConfigValue('enable_granular_consent', isTrue); + expectConfigValue('enable_granular_consent', true); expectConfigValue('login_hint', 'login-hint@example.com'); expectConfigValue('hd', 'hd_value'); expectConfigValue('ux_mode', 'popup'); - expectConfigValue('select_account', isTrue); + expectConfigValue('select_account', true); expectConfigValue('error_callback', utils.isAJs('function')); }); }); diff --git a/packages/google_identity_services_web/example/integration_test/utils.dart b/packages/google_identity_services_web/example/integration_test/utils.dart index 2d5c3f3fab8c..a75d2d9e9ee8 100644 --- a/packages/google_identity_services_web/example/integration_test/utils.dart +++ b/packages/google_identity_services_web/example/integration_test/utils.dart @@ -23,6 +23,20 @@ typedef ExpectConfigValueFn = void Function(String name, Object? matcher); /// Creates a [ExpectConfigValueFn] for the `config` [JSObject]. ExpectConfigValueFn createExpectConfigValue(JSObject config) { return (String name, Object? matcher) { + if (matcher is String) { + matcher = matcher.toJS; + } else if (matcher is bool) { + matcher = matcher.toJS; + } else if (matcher is List) { + final List old = matcher; + matcher = isA().having( + (JSAny? p0) => (p0 as JSArray?) + ?.toDart + .map((JSAny? e) => e.dartify()) + .toList(), + 'Array with matching values', + old); + } expect(config[name], matcher, reason: name); }; } diff --git a/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart b/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart index 99bc70690a4a..7679f8fad1d6 100644 --- a/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart +++ b/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart @@ -20,12 +20,7 @@ import 'shared.dart'; external GoogleAccountsId get id; /// The Dart definition of the `google.accounts.id` global. -@JS() -@staticInterop -abstract class GoogleAccountsId {} - -/// The `google.accounts.id` methods -extension GoogleAccountsIdExtension on GoogleAccountsId { +extension type GoogleAccountsId._(JSObject _) implements JSObject { /// An undocumented method. /// /// Try it with 'debug'. @@ -186,10 +181,7 @@ extension GoogleAccountsIdExtension on GoogleAccountsId { /// /// Data type: IdConfiguration /// https://developers.google.com/identity/gsi/web/reference/js-reference#IdConfiguration -@JS() -@anonymous -@staticInterop -abstract class IdConfiguration { +extension type IdConfiguration._(JSObject _) implements JSObject { /// Constructs a IdConfiguration object in JavaScript. factory IdConfiguration({ /// Your application's client ID, which is found and created in the Google @@ -355,12 +347,7 @@ typedef PromptMomentListenerFn = void Function(PromptMomentNotification moment); /// /// Data type: PromptMomentNotification /// https://developers.google.com/identity/gsi/web/reference/js-reference#PromptMomentNotification -@JS() -@staticInterop -abstract class PromptMomentNotification {} - -/// The methods of the [PromptMomentNotification] data type: -extension PromptMomentNotificationExtension on PromptMomentNotification { +extension type PromptMomentNotification._(JSObject _) implements JSObject { /// Is this notification for a display moment? bool isDisplayMoment() => _isDisplayMoment().toDart; @JS('isDisplayMoment') @@ -415,12 +402,7 @@ extension PromptMomentNotificationExtension on PromptMomentNotification { /// /// Data type: CredentialResponse /// https://developers.google.com/identity/gsi/web/reference/js-reference#CredentialResponse -@JS() -@staticInterop -abstract class CredentialResponse {} - -/// The fields that are contained in the credential response object. -extension CredentialResponseExtension on CredentialResponse { +extension type CredentialResponse._(JSObject _) implements JSObject { /// The ClientID for this Credential. String? get client_id => _client_id?.toDart; @JS('client_id') @@ -469,10 +451,7 @@ typedef CallbackFn = void Function(CredentialResponse credentialResponse); /// /// Data type: GsiButtonConfiguration /// https://developers.google.com/identity/gsi/web/reference/js-reference#GsiButtonConfiguration -@JS() -@anonymous -@staticInterop -abstract class GsiButtonConfiguration { +extension type GsiButtonConfiguration._(JSObject _) implements JSObject { /// Constructs an options object for the [renderButton] method. factory GsiButtonConfiguration({ /// The button type. @@ -535,12 +514,7 @@ abstract class GsiButtonConfiguration { } /// The object passed as an optional parameter to `click_listener` function. -@JS() -@staticInterop -abstract class GsiButtonData {} - -/// The fields that are contained in the button data. -extension GsiButtonDataExtension on GsiButtonData { +extension type GsiButtonData._(JSObject _) implements JSObject { /// Nonce String? get nonce => _nonce?.toDart; @JS('nonce') @@ -565,10 +539,7 @@ typedef GsiButtonClickListenerFn = void Function(GsiButtonData? gsiButtonData); /// /// Data type: Credential /// https://developers.google.com/identity/gsi/web/reference/js-reference#type-Credential -@JS() -@anonymous -@staticInterop -abstract class Credential { +extension type Credential._(JSObject _) implements JSObject { /// factory Credential({ required String id, @@ -620,12 +591,7 @@ typedef RevocationResponseHandlerFn = void Function( /// /// Data type: RevocationResponse /// https://developers.google.com/identity/gsi/web/reference/js-reference#RevocationResponse -@JS() -@staticInterop -abstract class RevocationResponse {} - -/// The fields that are contained in the [RevocationResponse] object. -extension RevocationResponseExtension on RevocationResponse { +extension type RevocationResponse._(JSObject _) implements JSObject { /// This field is a boolean value set to true if the revoke method call /// succeeded or false on failure. bool get successful => _successful.toDart; diff --git a/packages/google_identity_services_web/lib/src/js_interop/google_accounts_oauth2.dart b/packages/google_identity_services_web/lib/src/js_interop/google_accounts_oauth2.dart index 30a843b07e05..64402069004f 100644 --- a/packages/google_identity_services_web/lib/src/js_interop/google_accounts_oauth2.dart +++ b/packages/google_identity_services_web/lib/src/js_interop/google_accounts_oauth2.dart @@ -9,9 +9,6 @@ // * non_constant_identifier_names required to be able to use the same parameter // names as the underlying library. -@JS() -library google_accounts_oauth2; - import 'dart:js_interop'; import 'shared.dart'; @@ -23,12 +20,7 @@ import 'shared.dart'; external GoogleAccountsOauth2 get oauth2; /// The Dart definition of the `google.accounts.oauth2` global. -@JS() -@staticInterop -abstract class GoogleAccountsOauth2 {} - -/// The `google.accounts.oauth2` methods -extension GoogleAccountsOauth2Extension on GoogleAccountsOauth2 { +extension type GoogleAccountsOauth2._(JSObject _) implements JSObject { /// Initializes and returns a code client, with the passed-in [config]. /// /// Method: google.accounts.oauth2.initCodeClient @@ -97,10 +89,7 @@ extension GoogleAccountsOauth2Extension on GoogleAccountsOauth2 { /// /// Data type: CodeClientConfig /// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClientConfig -@JS() -@anonymous -@staticInterop -abstract class CodeClientConfig { +extension type CodeClientConfig._(JSObject _) implements JSObject { /// Constructs a CodeClientConfig object in JavaScript. /// /// The [callback] property must be a Dart function and not a JS function. @@ -161,12 +150,7 @@ abstract class CodeClientConfig { /// /// Data type: CodeClient /// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeClient -@JS() -@staticInterop -abstract class CodeClient {} - -/// The methods available on the [CodeClient]. -extension CodeClientExtension on CodeClient { +extension type CodeClient._(JSObject _) implements JSObject { /// Starts the OAuth 2.0 Code UX flow. external void requestCode(); } @@ -175,12 +159,7 @@ extension CodeClientExtension on CodeClient { /// /// Data type: CodeResponse /// https://developers.google.com/identity/oauth2/web/reference/js-reference#CodeResponse -@JS() -@staticInterop -abstract class CodeResponse {} - -/// The fields that are contained in the code response object. -extension CodeResponseExtension on CodeResponse { +extension type CodeResponse._(JSObject _) implements JSObject { /// The authorization code of a successful token response. String? get code => _code?.toDart; @JS('code') @@ -223,10 +202,7 @@ typedef CodeClientCallbackFn = void Function(CodeResponse response); /// /// Data type: TokenClientConfig /// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClientConfig -@JS() -@anonymous -@staticInterop -abstract class TokenClientConfig { +extension type TokenClientConfig._(JSObject _) implements JSObject { /// Constructs a TokenClientConfig object in JavaScript. /// /// The [callback] property must be a Dart function and not a JS function. @@ -281,12 +257,7 @@ abstract class TokenClientConfig { /// /// Data type: TokenClient /// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenClient -@JS() -@staticInterop -abstract class TokenClient {} - -/// The methods available on the [TokenClient]. -extension TokenClientExtension on TokenClient { +extension type TokenClient._(JSObject _) implements JSObject { /// Starts the OAuth 2.0 Code UX flow. void requestAccessToken([ OverridableTokenClientConfig? overrideConfig, @@ -308,10 +279,7 @@ extension TokenClientExtension on TokenClient { /// /// Data type: OverridableTokenClientConfig /// https://developers.google.com/identity/oauth2/web/reference/js-reference#OverridableTokenClientConfig -@JS() -@anonymous -@staticInterop -abstract class OverridableTokenClientConfig { +extension type OverridableTokenClientConfig._(JSObject _) implements JSObject { /// Constructs an OverridableTokenClientConfig object in JavaScript. factory OverridableTokenClientConfig({ /// A list of scopes that identify the resources that your application could @@ -396,12 +364,7 @@ abstract class OverridableTokenClientConfig { /// /// Data type: TokenResponse /// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse -@JS() -@staticInterop -abstract class TokenResponse {} - -/// The fields that are contained in the code response object. -extension TokenResponseExtension on TokenResponse { +extension type TokenResponse._(JSObject _) implements JSObject { /// The access token of a successful token response. String? get access_token => _access_token?.toDart; @JS('access_token') @@ -465,12 +428,7 @@ typedef TokenClientCallbackFn = void Function(TokenResponse response); typedef ErrorCallbackFn = void Function(GoogleIdentityServicesError? error); /// An error returned by `initTokenClient` or `initDataClient`. -@JS() -@staticInterop -abstract class GoogleIdentityServicesError {} - -/// Methods of the GoogleIdentityServicesError object. -extension GoogleIdentityServicesErrorExtension on GoogleIdentityServicesError { +extension type GoogleIdentityServicesError._(JSObject _) implements JSObject { /// The type of error GoogleIdentityServicesErrorType get type => GoogleIdentityServicesErrorType.values.byName(_type.toDart); @@ -492,12 +450,7 @@ typedef RevokeTokenDoneFn = void Function(TokenRevocationResponse response); /// /// Data type: RevocationResponse /// https://developers.google.com/identity/oauth2/web/reference/js-reference#TokenResponse -@JS() -@staticInterop -abstract class TokenRevocationResponse {} - -/// The fields that are contained in the [TokenRevocationResponse] object. -extension TokenRevocationResponseExtension on TokenRevocationResponse { +extension type TokenRevocationResponse._(JSObject _) implements JSObject { /// This field is a boolean value set to true if the revoke method call /// succeeded or false on failure. bool get successful => _successful.toDart; diff --git a/packages/google_identity_services_web/lib/src/js_interop/load_callback.dart b/packages/google_identity_services_web/lib/src/js_interop/load_callback.dart index 23b08447b8b6..f48f8aa0203b 100644 --- a/packages/google_identity_services_web/lib/src/js_interop/load_callback.dart +++ b/packages/google_identity_services_web/lib/src/js_interop/load_callback.dart @@ -5,8 +5,6 @@ // Methods here are documented in the Google Identity authentication website, // but they don't really belong to either the authentication nor authorization // libraries. -@JS() -library id_load_callback; import 'dart:js_interop'; @@ -18,7 +16,6 @@ import 'shared.dart'; */ @JS('onGoogleLibraryLoad') -@staticInterop external set _onGoogleLibraryLoad(JSFunction callback); /// Method called after the Sign In With Google JavaScript library is loaded. diff --git a/packages/google_identity_services_web/pubspec.yaml b/packages/google_identity_services_web/pubspec.yaml index 97af6ef049fe..cedb0e5b194b 100644 --- a/packages/google_identity_services_web/pubspec.yaml +++ b/packages/google_identity_services_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_identity_services_web description: A Dart JS-interop layer for Google Identity Services. Google's new sign-in SDK for Web that supports multiple types of credentials. repository: https://github.com/flutter/packages/tree/main/packages/google_identity_services_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_identiy_services_web%22 -version: 0.3.2 +version: 0.3.3 environment: sdk: ^3.4.0 diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index c809dfb10f86..75614efbf0db 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.10.0 -* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +* Fixes detection of WebAssembly support on package site. ## 2.9.0 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle index 1a7aa24663aa..c930bb46861f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdk flutter.compileSdkVersion @@ -31,7 +29,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.googlemapsexample" minSdkVersion flutter.minSdkVersion - targetSdkVersion 28 + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/main/AndroidManifest.xml b/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/main/AndroidManifest.xml index 3e9d1a8aac2d..67b551cc21a8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/main/AndroidManifest.xml +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize"> diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle index 0bed8906c094..b9db5700753a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.2' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/settings.gradle index 32735a3cfd4b..0667903d5724 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/settings.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.2" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart index 2ab5ffccccbc..536adc32f07d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart @@ -162,6 +162,8 @@ void runTests() { const String mapStyle = '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]'; + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use await controller.setMapStyle(mapStyle); }); @@ -184,6 +186,8 @@ void runTests() { final GoogleMapController controller = await controllerCompleter.future; try { + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use await controller.setMapStyle('invalid_value'); fail('expected MapStyleException'); } on MapStyleException catch (e) { @@ -208,6 +212,8 @@ void runTests() { ); final GoogleMapController controller = await controllerCompleter.future; + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use await controller.setMapStyle(null); }); @@ -471,6 +477,8 @@ void runTests() { final Set markers = { Marker( markerId: const MarkerId('1'), + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use icon: await BitmapDescriptor.fromAssetImage( imageConfiguration, 'assets/red_square.png', @@ -493,6 +501,8 @@ void runTests() { final Set markers = { Marker( markerId: const MarkerId('1'), + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use icon: BitmapDescriptor.fromBytes( bytes, size: const Size(100, 100), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 6271f61d4504..01c1ed66be12 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -55,6 +56,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -137,6 +139,9 @@ /* Begin PBXNativeTarget section */ 97C146ED1CF9000F007C117D /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -162,6 +167,9 @@ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastUpgradeCheck = 1510; @@ -487,6 +495,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index f8d70602adf7..cab978a8e3b6 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 5c3bb49133c8..66952bbe062d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -5,7 +5,6 @@ library google_maps_flutter; import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart index d93a9c9cc1af..888195ca36d2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart @@ -594,7 +594,6 @@ class _GoogleMapState extends State { /// Builds a [MapConfiguration] from the given [map]. MapConfiguration _configurationFromMapWidget(GoogleMap map) { - assert(!map.liteModeEnabled || Platform.isAndroid); return MapConfiguration( webGestureHandling: map.webGestureHandling, compassEnabled: map.compassEnabled, diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 00a533874875..661ed9b8d3c1 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.9.0 +version: 2.10.0 environment: sdk: ^3.4.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle index 8107f238eb47..c09c209772e8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.googlemapsexample' compileSdk flutter.compileSdkVersion diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle index c4687fc5d3bc..98976c044c96 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.1' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle index 32735a3cfd4b..078698fe752d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.1" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart index 277010adfeba..666b48555811 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart @@ -1405,6 +1405,8 @@ void googleMapsTests() { final Set markers = { Marker( markerId: const MarkerId('1'), + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use icon: await BitmapDescriptor.fromAssetImage( imageConfiguration, 'assets/red_square.png', @@ -1427,6 +1429,8 @@ void googleMapsTests() { final Set markers = { Marker( markerId: const MarkerId('1'), + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use icon: BitmapDescriptor.fromBytes( bytes, size: const Size(100, 100), diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart index 038d5fe16d41..dd19bed86468 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart @@ -885,6 +885,8 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { case final DefaultMarker marker: return PlatformBitmap( bitmap: PlatformBitmapDefaultMarker(hue: marker.hue?.toDouble())); + // Clients may still use this deprecated format, so it must be supported. + // ignore: deprecated_member_use case final BytesBitmap bytes: return PlatformBitmap( bitmap: PlatformBitmapBytes( @@ -895,6 +897,8 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { case final AssetBitmap asset: return PlatformBitmap( bitmap: PlatformBitmapAsset(name: asset.name, pkg: asset.package)); + // Clients may still use this deprecated format, so it must be supported. + // ignore: deprecated_member_use case final AssetImageBitmap asset: return PlatformBitmap( bitmap: PlatformBitmapAssetImage( diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart index 7104d01e163d..a12c7169780f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart @@ -310,43 +310,32 @@ void main() { // Object two should be changed. { expect(toChange.length, 1); - final List? encoded = toChange.first.encode() as List?; - expect(encoded?.getRange(0, 6), [ - object2new.consumeTapEvents, - object2new.fillColor.value, - object2new.strokeColor.value, - object2new.visible, - object2new.strokeWidth, - object2new.zIndex.toDouble(), - ]); - final PlatformLatLng? latLng = encoded?[6] as PlatformLatLng?; - expect(latLng?.latitude, object2new.center.latitude); - expect(latLng?.longitude, object2new.center.longitude); - expect(encoded?.getRange(7, 9), [ - object2new.radius, - object2new.circleId.value, - ]); + final PlatformCircle firstChanged = toChange.first; + expect(firstChanged.consumeTapEvents, object2new.consumeTapEvents); + expect(firstChanged.fillColor, object2new.fillColor.value); + expect(firstChanged.strokeColor, object2new.strokeColor.value); + expect(firstChanged.visible, object2new.visible); + expect(firstChanged.strokeWidth, object2new.strokeWidth); + expect(firstChanged.zIndex, object2new.zIndex.toDouble()); + expect(firstChanged.center.latitude, object2new.center.latitude); + expect(firstChanged.center.longitude, object2new.center.longitude); + expect(firstChanged.radius, object2new.radius); + expect(firstChanged.circleId, object2new.circleId.value); } // Object 3 should be added. - expect(toAdd.length, 1); { expect(toAdd.length, 1); - final List? encoded = toAdd.first.encode() as List?; - expect(encoded?.getRange(0, 6), [ - object3.consumeTapEvents, - object3.fillColor.value, - object3.strokeColor.value, - object3.visible, - object3.strokeWidth, - object3.zIndex.toDouble(), - ]); - final PlatformLatLng? latLng = encoded?[6] as PlatformLatLng?; - expect(latLng?.latitude, object3.center.latitude); - expect(latLng?.longitude, object3.center.longitude); - expect(encoded?.getRange(7, 9), [ - object3.radius, - object3.circleId.value, - ]); + final PlatformCircle firstAdded = toAdd.first; + expect(firstAdded.consumeTapEvents, object3.consumeTapEvents); + expect(firstAdded.fillColor, object3.fillColor.value); + expect(firstAdded.strokeColor, object3.strokeColor.value); + expect(firstAdded.visible, object3.visible); + expect(firstAdded.strokeWidth, object3.strokeWidth); + expect(firstAdded.zIndex, object3.zIndex.toDouble()); + expect(firstAdded.center.latitude, object3.center.latitude); + expect(firstAdded.center.longitude, object3.center.longitude); + expect(firstAdded.radius, object3.radius); + expect(firstAdded.circleId, object3.circleId.value); } }); @@ -406,72 +395,58 @@ void main() { // Object two should be changed. { expect(toChange.length, 1); - final List? encoded = toChange.first.encode() as List?; - expect(encoded?[0], object2new.alpha); - final PlatformDoublePair? offset = encoded?[1] as PlatformDoublePair?; - expect(offset?.x, object2new.anchor.dx); - expect(offset?.y, object2new.anchor.dy); - expect(encoded?.getRange(2, 5).toList(), [ - object2new.consumeTapEvents, - object2new.draggable, - object2new.flat, - ]); + final PlatformMarker firstChanged = toChange.first; + expect(firstChanged.alpha, object2new.alpha); + expect(firstChanged.anchor.x, object2new.anchor.dx); + expect(firstChanged.anchor.y, object2new.anchor.dy); + expect(firstChanged.consumeTapEvents, object2new.consumeTapEvents); + expect(firstChanged.draggable, object2new.draggable); + expect(firstChanged.flat, object2new.flat); expect( - (encoded?[5] as PlatformBitmap?)?.bitmap.runtimeType, + firstChanged.icon.bitmap.runtimeType, GoogleMapsFlutterAndroid.platformBitmapFromBitmapDescriptor( object2new.icon) .bitmap .runtimeType); - final PlatformInfoWindow? window = encoded?[6] as PlatformInfoWindow?; - expect(window?.title, object2new.infoWindow.title); - expect(window?.snippet, object2new.infoWindow.snippet); - expect(window?.anchor.x, object2new.infoWindow.anchor.dx); - expect(window?.anchor.y, object2new.infoWindow.anchor.dy); - final PlatformLatLng? latLng = encoded?[7] as PlatformLatLng?; - expect(latLng?.latitude, object2new.position.latitude); - expect(latLng?.longitude, object2new.position.longitude); - expect(encoded?.getRange(8, 13), [ - object2new.rotation, - object2new.visible, - object2new.zIndex, - object2new.markerId.value, - object2new.clusterManagerId?.value, - ]); + expect(firstChanged.infoWindow.title, object2new.infoWindow.title); + expect(firstChanged.infoWindow.snippet, object2new.infoWindow.snippet); + expect(firstChanged.infoWindow.anchor.x, object2new.infoWindow.anchor.dx); + expect(firstChanged.infoWindow.anchor.y, object2new.infoWindow.anchor.dy); + expect(firstChanged.position.latitude, object2new.position.latitude); + expect(firstChanged.position.longitude, object2new.position.longitude); + expect(firstChanged.rotation, object2new.rotation); + expect(firstChanged.visible, object2new.visible); + expect(firstChanged.zIndex, object2new.zIndex); + expect(firstChanged.markerId, object2new.markerId.value); + expect(firstChanged.clusterManagerId, object2new.clusterManagerId?.value); } // Object 3 should be added. { expect(toAdd.length, 1); - final List? encoded = toAdd.first.encode() as List?; - expect(encoded?[0], object3.alpha); - final PlatformDoublePair? offset = encoded?[1] as PlatformDoublePair?; - expect(offset?.x, object3.anchor.dx); - expect(offset?.y, object3.anchor.dy); - expect(encoded?.getRange(2, 5).toList(), [ - object3.consumeTapEvents, - object3.draggable, - object3.flat, - ]); + final PlatformMarker firstAdded = toAdd.first; + expect(firstAdded.alpha, object3.alpha); + expect(firstAdded.anchor.x, object3.anchor.dx); + expect(firstAdded.anchor.y, object3.anchor.dy); + expect(firstAdded.consumeTapEvents, object3.consumeTapEvents); + expect(firstAdded.draggable, object3.draggable); + expect(firstAdded.flat, object3.flat); expect( - (encoded?[5] as PlatformBitmap?)?.bitmap.runtimeType, + firstAdded.icon.bitmap.runtimeType, GoogleMapsFlutterAndroid.platformBitmapFromBitmapDescriptor( object3.icon) .bitmap .runtimeType); - final PlatformInfoWindow? window = encoded?[6] as PlatformInfoWindow?; - expect(window?.title, object3.infoWindow.title); - expect(window?.snippet, object3.infoWindow.snippet); - expect(window?.anchor.x, object3.infoWindow.anchor.dx); - expect(window?.anchor.y, object3.infoWindow.anchor.dy); - final PlatformLatLng? latLng = encoded?[7] as PlatformLatLng?; - expect(latLng?.latitude, object3.position.latitude); - expect(latLng?.longitude, object3.position.longitude); - expect(encoded?.getRange(8, 13), [ - object3.rotation, - object3.visible, - object3.zIndex, - object3.markerId.value, - object3.clusterManagerId?.value, - ]); + expect(firstAdded.infoWindow.title, object3.infoWindow.title); + expect(firstAdded.infoWindow.snippet, object3.infoWindow.snippet); + expect(firstAdded.infoWindow.anchor.x, object3.infoWindow.anchor.dx); + expect(firstAdded.infoWindow.anchor.y, object3.infoWindow.anchor.dy); + expect(firstAdded.position.latitude, object3.position.latitude); + expect(firstAdded.position.longitude, object3.position.longitude); + expect(firstAdded.rotation, object3.rotation); + expect(firstAdded.visible, object3.visible); + expect(firstAdded.zIndex, object3.zIndex); + expect(firstAdded.markerId, object3.markerId.value); + expect(firstAdded.clusterManagerId, object3.clusterManagerId?.value); } }); @@ -500,17 +475,14 @@ void main() { expect(toRemove.length, 1); expect(toRemove.first, object1.polygonId.value); void expectPolygon(PlatformPolygon actual, Polygon expected) { - final List encoded = actual.encode() as List; - expect(encoded.sublist(0, 4), [ - expected.polygonId.value, - expected.consumeTapEvents, - expected.fillColor.value, - expected.geodesic, - ]); + expect(actual.polygonId, expected.polygonId.value); + expect(actual.consumesTapEvents, expected.consumeTapEvents); + expect(actual.fillColor, expected.fillColor.value); + expect(actual.geodesic, expected.geodesic); expect(actual.points.length, expected.points.length); for (final (int i, PlatformLatLng? point) in actual.points.indexed) { - expect(point?.latitude, actual.points[i].latitude); - expect(point?.longitude, actual.points[i].longitude); + expect(point?.latitude, expected.points[i].latitude); + expect(point?.longitude, expected.points[i].longitude); } expect(actual.holes.length, expected.holes.length); for (final (int i, List? hole) in actual.holes.indexed) { @@ -520,12 +492,10 @@ void main() { expect(point?.longitude, expectedHole[j].longitude); } } - expect(encoded.sublist(6), [ - expected.visible, - expected.strokeColor.value, - expected.strokeWidth, - expected.zIndex, - ]); + expect(actual.visible, expected.visible); + expect(actual.strokeColor, expected.strokeColor.value); + expect(actual.strokeWidth, expected.strokeWidth); + expect(actual.zIndex, expected.zIndex); } // Object two should be changed. @@ -564,19 +534,15 @@ void main() { verification.captured[1] as List; final List toRemove = verification.captured[2] as List; void expectPolyline(PlatformPolyline actual, Polyline expected) { - final List encoded = actual.encode() as List; - expect(encoded.sublist(0, 5), [ - expected.polylineId.value, - expected.consumeTapEvents, - expected.color.value, - expected.geodesic, - platformJointTypeFromJointType(expected.jointType), - ]); - expect(encoded.sublist(9), [ - expected.visible, - expected.width, - expected.zIndex, - ]); + expect(actual.polylineId, expected.polylineId.value); + expect(actual.consumesTapEvents, expected.consumeTapEvents); + expect(actual.color, expected.color.value); + expect(actual.geodesic, expected.geodesic); + expect( + actual.jointType, platformJointTypeFromJointType(expected.jointType)); + expect(actual.visible, expected.visible); + expect(actual.width, expected.width); + expect(actual.zIndex, expected.zIndex); expect(actual.points.length, expected.points.length); for (final (int i, PlatformLatLng? point) in actual.points.indexed) { expect(point?.latitude, actual.points[i].latitude); @@ -639,15 +605,13 @@ void main() { final List toChange = verification.captured[1] as List; final List toRemove = verification.captured[2] as List; - void expectTileOverlay(PlatformTileOverlay? actual, TileOverlay expected) { - expect(actual?.encode(), [ - expected.tileOverlayId.value, - expected.fadeIn, - expected.transparency, - expected.zIndex, - expected.visible, - expected.tileSize, - ]); + void expectTileOverlay(PlatformTileOverlay actual, TileOverlay expected) { + expect(actual.tileOverlayId, expected.tileOverlayId.value); + expect(actual.fadeIn, expected.fadeIn); + expect(actual.transparency, expected.transparency); + expect(actual.zIndex, expected.zIndex); + expect(actual.visible, expected.visible); + expect(actual.tileSize, expected.tileSize); } // Object one should be removed. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 5f962bb978d6..a42fa3d3076e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.13.2 + +* Updates most objects passed from Dart to native to use typed data. + ## 2.13.1 * Updates Pigeon for non-nullable collection type support. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart index d3ea025d99c9..e2b5941d2618 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart @@ -1245,6 +1245,8 @@ void main() { final Set markers = { Marker( markerId: const MarkerId('1'), + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use icon: await BitmapDescriptor.fromAssetImage( imageConfiguration, 'assets/red_square.png', @@ -1268,6 +1270,8 @@ void main() { testWidgets('markerWithLegacyBytes', (WidgetTester tester) async { tester.view.devicePixelRatio = 2.0; final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + // Intentionally testing the deprecated code path. + // ignore: deprecated_member_use final BitmapDescriptor icon = BitmapDescriptor.fromBytes( bytes, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj index 34e5636692e0..b59580322983 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 528F16832C62941000148160 /* FGMClusterManagersControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 528F16822C62941000148160 /* FGMClusterManagersControllerTests.m */; }; 528F16872C62952700148160 /* ExtractIconFromDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 528F16862C62952700148160 /* ExtractIconFromDataTests.m */; }; 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -99,6 +100,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, B3A7FA04ABB7B84780729949 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -240,13 +242,15 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */, - A7D3A643E249522B15BA2B1D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -322,6 +326,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -412,24 +419,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - A7D3A643E249522B15BA2B1D /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -830,6 +819,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index f8d70602adf7..cab978a8e3b6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/ExtractIconFromDataTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/ExtractIconFromDataTests.m index c3d6a363e0c4..811f09d49b45 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/ExtractIconFromDataTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/ExtractIconFromDataTests.m @@ -23,16 +23,18 @@ - (void)testExtractIconFromDataAssetAuto { OCMStub([mockRegistrar lookupKeyForAsset:@"fakeImageNameKey"]).andReturn(@"fakeAssetKey"); OCMStub(ClassMethod([mockImageClass imageNamed:@"fakeAssetKey"])).andReturn(testImage); - NSDictionary *assetData = - @{@"assetName" : @"fakeImageNameKey", @"bitmapScaling" : @"auto", @"imagePixelRatio" : @1}; - - NSArray *iconData = @[ @"asset", assetData ]; + FGMPlatformBitmapAssetMap *bitmap = + [FGMPlatformBitmapAssetMap makeWithAssetName:@"fakeImageNameKey" + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:1 + width:nil + height:nil]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, 1.0); XCTAssertEqual(resultImage.size.width, 1.0); @@ -49,16 +51,18 @@ - (void)testExtractIconFromDataAssetAutoWithScale { OCMStub([mockRegistrar lookupKeyForAsset:@"fakeImageNameKey"]).andReturn(@"fakeAssetKey"); OCMStub(ClassMethod([mockImageClass imageNamed:@"fakeAssetKey"])).andReturn(testImage); - NSDictionary *assetData = - @{@"assetName" : @"fakeImageNameKey", @"bitmapScaling" : @"auto", @"imagePixelRatio" : @10}; - - NSArray *iconData = @[ @"asset", assetData ]; + FGMPlatformBitmapAssetMap *bitmap = + [FGMPlatformBitmapAssetMap makeWithAssetName:@"fakeImageNameKey" + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:10 + width:nil + height:nil]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, 10); @@ -77,29 +81,29 @@ - (void)testExtractIconFromDataAssetAutoAndSizeWithSameAspectRatio { OCMStub([mockRegistrar lookupKeyForAsset:@"fakeImageNameKey"]).andReturn(@"fakeAssetKey"); OCMStub(ClassMethod([mockImageClass imageNamed:@"fakeAssetKey"])).andReturn(testImage); - NSDictionary *assetData = @{ - @"assetName" : @"fakeImageNameKey", - @"bitmapScaling" : @"auto", - @"imagePixelRatio" : @1, - @"width" : @15.0 - }; // Target height + const CGFloat width = 15.0; + FGMPlatformBitmapAssetMap *bitmap = + [FGMPlatformBitmapAssetMap makeWithAssetName:@"fakeImageNameKey" + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:1 + width:@(width) + height:nil]; - NSArray *iconData = @[ @"asset", assetData ]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(testImage.scale, 1.0); // As image has same aspect ratio as the original image, // only image scale has been changed to match the target size. - CGFloat targetScale = testImage.scale * (testImage.size.width / 15.0); + CGFloat targetScale = testImage.scale * (testImage.size.width / width); const CGFloat accuracy = 0.001; XCTAssertEqualWithAccuracy(resultImage.scale, targetScale, accuracy); - XCTAssertEqual(resultImage.size.width, 15.0); - XCTAssertEqual(resultImage.size.height, 15.0); + XCTAssertEqual(resultImage.size.width, width); + XCTAssertEqual(resultImage.size.height, width); } - (void)testExtractIconFromDataAssetAutoAndSizeWithDifferentAspectRatio { @@ -112,25 +116,24 @@ - (void)testExtractIconFromDataAssetAutoAndSizeWithDifferentAspectRatio { OCMStub([mockRegistrar lookupKeyForAsset:@"fakeImageNameKey"]).andReturn(@"fakeAssetKey"); OCMStub(ClassMethod([mockImageClass imageNamed:@"fakeAssetKey"])).andReturn(testImage); - NSDictionary *assetData = @{ - @"assetName" : @"fakeImageNameKey", - @"bitmapScaling" : @"auto", - @"imagePixelRatio" : @1, - @"width" : @15.0, - @"height" : @45.0 - }; - - NSArray *iconData = @[ @"asset", assetData ]; + const CGFloat width = 15.0; + const CGFloat height = 45.0; + FGMPlatformBitmapAssetMap *bitmap = + [FGMPlatformBitmapAssetMap makeWithAssetName:@"fakeImageNameKey" + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:1 + width:@(width) + height:@(height)]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, screenScale); - XCTAssertEqual(resultImage.size.width, 15.0); - XCTAssertEqual(resultImage.size.height, 45.0); + XCTAssertEqual(resultImage.size.width, width); + XCTAssertEqual(resultImage.size.height, height); } - (void)testExtractIconFromDataAssetNoScaling { @@ -143,16 +146,18 @@ - (void)testExtractIconFromDataAssetNoScaling { OCMStub([mockRegistrar lookupKeyForAsset:@"fakeImageNameKey"]).andReturn(@"fakeAssetKey"); OCMStub(ClassMethod([mockImageClass imageNamed:@"fakeAssetKey"])).andReturn(testImage); - NSDictionary *assetData = - @{@"assetName" : @"fakeImageNameKey", @"bitmapScaling" : @"none", @"imagePixelRatio" : @10}; - - NSArray *iconData = @[ @"asset", assetData ]; + FGMPlatformBitmapAssetMap *bitmap = + [FGMPlatformBitmapAssetMap makeWithAssetName:@"fakeImageNameKey" + bitmapScaling:FGMPlatformMapBitmapScalingNone + imagePixelRatio:1 + width:nil + height:nil]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, 1.0); @@ -169,16 +174,18 @@ - (void)testExtractIconFromDataBytesAuto { XCTAssertNotNil(pngData); FlutterStandardTypedData *typedData = [FlutterStandardTypedData typedDataWithBytes:pngData]; + FGMPlatformBitmapBytesMap *bitmap = + [FGMPlatformBitmapBytesMap makeWithByteData:typedData + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:1 + width:nil + height:nil]; - NSDictionary *bytesData = - @{@"byteData" : typedData, @"bitmapScaling" : @"auto", @"imagePixelRatio" : @1}; - - NSArray *iconData = @[ @"bytes", bytesData ]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, 1.0); @@ -195,17 +202,18 @@ - (void)testExtractIconFromDataBytesAutoWithScaling { XCTAssertNotNil(pngData); FlutterStandardTypedData *typedData = [FlutterStandardTypedData typedDataWithBytes:pngData]; - - NSDictionary *bytesData = - @{@"byteData" : typedData, @"bitmapScaling" : @"auto", @"imagePixelRatio" : @10}; - - NSArray *iconData = @[ @"bytes", bytesData ]; + FGMPlatformBitmapBytesMap *bitmap = + [FGMPlatformBitmapBytesMap makeWithByteData:typedData + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:10 + width:nil + height:nil]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, 10); XCTAssertEqual(resultImage.size.width, 0.1); @@ -220,34 +228,32 @@ - (void)testExtractIconFromDataBytesAutoAndSizeWithSameAspectRatio { NSData *pngData = UIImagePNGRepresentation(testImage); XCTAssertNotNil(pngData); + const CGFloat width = 15.0; + const CGFloat height = 15.0; FlutterStandardTypedData *typedData = [FlutterStandardTypedData typedDataWithBytes:pngData]; - - NSDictionary *bytesData = @{ - @"byteData" : typedData, - @"bitmapScaling" : @"auto", - @"imagePixelRatio" : @1, - @"width" : @15.0, - @"height" : @15.0 - }; - - NSArray *iconData = @[ @"bytes", bytesData ]; + FGMPlatformBitmapBytesMap *bitmap = + [FGMPlatformBitmapBytesMap makeWithByteData:typedData + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:1 + width:@(width) + height:@(height)]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(testImage.scale, 1.0); // As image has same aspect ratio as the original image, // only image scale has been changed to match the target size. - CGFloat targetScale = testImage.scale * (testImage.size.width / 15.0); + CGFloat targetScale = testImage.scale * (testImage.size.width / width); const CGFloat accuracy = 0.001; XCTAssertEqualWithAccuracy(resultImage.scale, targetScale, accuracy); - XCTAssertEqual(resultImage.size.width, 15.0); - XCTAssertEqual(resultImage.size.height, 15.0); + XCTAssertEqual(resultImage.size.width, width); + XCTAssertEqual(resultImage.size.height, height); } - (void)testExtractIconFromDataBytesAutoAndSizeWithDifferentAspectRatio { @@ -258,26 +264,25 @@ - (void)testExtractIconFromDataBytesAutoAndSizeWithDifferentAspectRatio { NSData *pngData = UIImagePNGRepresentation(testImage); XCTAssertNotNil(pngData); + const CGFloat width = 15.0; + const CGFloat height = 45.0; FlutterStandardTypedData *typedData = [FlutterStandardTypedData typedDataWithBytes:pngData]; + FGMPlatformBitmapBytesMap *bitmap = + [FGMPlatformBitmapBytesMap makeWithByteData:typedData + bitmapScaling:FGMPlatformMapBitmapScalingAuto + imagePixelRatio:1 + width:@(width) + height:@(height)]; - NSDictionary *bytesData = @{ - @"byteData" : typedData, - @"bitmapScaling" : @"auto", - @"imagePixelRatio" : @1, - @"width" : @15.0, - @"height" : @45.0 - }; - - NSArray *iconData = @[ @"bytes", bytesData ]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, screenScale); - XCTAssertEqual(resultImage.size.width, 15.0); - XCTAssertEqual(resultImage.size.height, 45.0); + XCTAssertEqual(resultImage.size.width, width); + XCTAssertEqual(resultImage.size.height, height); } - (void)testExtractIconFromDataBytesNoScaling { @@ -289,16 +294,18 @@ - (void)testExtractIconFromDataBytesNoScaling { XCTAssertNotNil(pngData); FlutterStandardTypedData *typedData = [FlutterStandardTypedData typedDataWithBytes:pngData]; + FGMPlatformBitmapBytesMap *bitmap = + [FGMPlatformBitmapBytesMap makeWithByteData:typedData + bitmapScaling:FGMPlatformMapBitmapScalingNone + imagePixelRatio:1 + width:nil + height:nil]; - NSDictionary *bytesData = - @{@"byteData" : typedData, @"bitmapScaling" : @"none", @"imagePixelRatio" : @1}; - - NSArray *iconData = @[ @"bytes", bytesData ]; CGFloat screenScale = 3.0; - UIImage *resultImage = [instance extractIconFromData:iconData - registrar:mockRegistrar - screenScale:screenScale]; + UIImage *resultImage = [instance iconFromBitmap:[FGMPlatformBitmap makeWithBitmap:bitmap] + registrar:mockRegistrar + screenScale:screenScale]; XCTAssertNotNil(resultImage); XCTAssertEqual(resultImage.scale, 1.0); XCTAssertEqual(resultImage.size.width, 1.0); @@ -362,7 +369,7 @@ - (UIImage *)createOnePixelImage { UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:format]; UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull context) { - [[UIColor whiteColor] setFill]; + [UIColor.whiteColor setFill]; [context fillRect:CGRectMake(0, 0, size.width, size.height)]; }]; return image; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FGMClusterManagersControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FGMClusterManagersControllerTests.m index 6fdcccf468dd..8d435eb4941c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FGMClusterManagersControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FGMClusterManagersControllerTests.m @@ -52,16 +52,39 @@ - (void)testClustering { NSString *markerId1 = @"m1"; NSString *markerId2 = @"m2"; - FGMPlatformMarker *marker1 = [FGMPlatformMarker makeWithJson:@{ - @"markerId" : markerId1, - @"position" : @[ @0, @0 ], - @"clusterManagerId" : clusterManagerId - }]; - FGMPlatformMarker *marker2 = [FGMPlatformMarker makeWithJson:@{ - @"markerId" : markerId2, - @"position" : @[ @0, @0 ], - @"clusterManagerId" : clusterManagerId - }]; + FGMPlatformPoint *zeroPoint = [FGMPlatformPoint makeWithX:0 y:0]; + FGMPlatformLatLng *zeroLatLng = [FGMPlatformLatLng makeWithLatitude:0 longitude:0]; + FGMPlatformBitmap *bitmap = + [FGMPlatformBitmap makeWithBitmap:[FGMPlatformBitmapDefaultMarker makeWithHue:0]]; + FGMPlatformInfoWindow *infoWindow = [FGMPlatformInfoWindow makeWithTitle:@"Info" + snippet:NULL + anchor:zeroPoint]; + FGMPlatformMarker *marker1 = [FGMPlatformMarker makeWithAlpha:1 + anchor:zeroPoint + consumeTapEvents:NO + draggable:NO + flat:NO + icon:bitmap + infoWindow:infoWindow + position:zeroLatLng + rotation:0 + visible:YES + zIndex:1 + markerId:markerId1 + clusterManagerId:clusterManagerId]; + FGMPlatformMarker *marker2 = [FGMPlatformMarker makeWithAlpha:1 + anchor:zeroPoint + consumeTapEvents:NO + draggable:NO + flat:NO + icon:bitmap + infoWindow:infoWindow + position:zeroLatLng + rotation:0 + visible:YES + zIndex:1 + markerId:markerId2 + clusterManagerId:clusterManagerId]; [markersController addMarkers:@[ marker1, marker2 ]]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m index c66698c6834e..cb7c63a72768 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m @@ -72,8 +72,11 @@ - (void)testColorFromRGBA { } - (void)testPointsFromLatLongs { - NSArray *latlongs = @[ @[ @1, @2 ], @[ @(3), @(4) ] ]; - NSArray *locations = [FLTGoogleMapJSONConversions pointsFromLatLongs:latlongs]; + NSArray *latlongs = @[ + [FGMPlatformLatLng makeWithLatitude:1 longitude:2], [FGMPlatformLatLng makeWithLatitude:3 + longitude:4] + ]; + NSArray *locations = FGMGetPointsForPigeonLatLngs(latlongs); XCTAssertEqual(locations.count, 2); XCTAssertEqual(locations[0].coordinate.latitude, 1); XCTAssertEqual(locations[0].coordinate.longitude, 2); @@ -82,10 +85,17 @@ - (void)testPointsFromLatLongs { } - (void)testHolesFromPointsArray { - NSArray *pointsArray = - @[ @[ @[ @1, @2 ], @[ @(3), @(4) ] ], @[ @[ @(5), @(6) ], @[ @(7), @(8) ] ] ]; - NSArray *> *holes = - [FLTGoogleMapJSONConversions holesFromPointsArray:pointsArray]; + NSArray *> *pointsArray = @[ + @[ + [FGMPlatformLatLng makeWithLatitude:1 longitude:2], [FGMPlatformLatLng makeWithLatitude:3 + longitude:4] + ], + @[ + [FGMPlatformLatLng makeWithLatitude:5 longitude:6], [FGMPlatformLatLng makeWithLatitude:7 + longitude:8] + ] + ]; + NSArray *> *holes = FGMGetHolesForPigeonLatLngArrays(pointsArray); XCTAssertEqual(holes.count, 2); XCTAssertEqual(holes[0][0].coordinate.latitude, 1); XCTAssertEqual(holes[0][0].coordinate.longitude, 2); @@ -134,23 +144,6 @@ - (void)testPigeonLatLngBoundsForCoordinateBounds { DBL_EPSILON); } -- (void)testCameraPostionFromDictionary { - XCTAssertNil([FLTGoogleMapJSONConversions cameraPostionFromDictionary:nil]); - - NSDictionary *channelValue = - @{@"target" : @[ @1, @2 ], @"zoom" : @3, @"bearing" : @4, @"tilt" : @5}; - - GMSCameraPosition *cameraPosition = - [FLTGoogleMapJSONConversions cameraPostionFromDictionary:channelValue]; - - const CGFloat accuracy = 0.001; - XCTAssertEqualWithAccuracy(cameraPosition.target.latitude, 1, accuracy); - XCTAssertEqualWithAccuracy(cameraPosition.target.longitude, 2, accuracy); - XCTAssertEqualWithAccuracy(cameraPosition.zoom, 3, accuracy); - XCTAssertEqualWithAccuracy(cameraPosition.bearing, 4, accuracy); - XCTAssertEqualWithAccuracy(cameraPosition.viewingAngle, 5, accuracy); -} - - (void)testGetCameraPostionForPigeonCameraPosition { FGMPlatformCameraPosition *pigeonCameraPosition = [FGMPlatformCameraPosition makeWithBearing:1.0 @@ -180,11 +173,11 @@ - (void)testCGPointForPigeonPoint { } - (void)testCoordinateBoundsFromLatLongs { - NSArray *latlong1 = @[ @1, @2 ]; - NSArray *latlong2 = @[ @(3), @(4) ]; + FGMPlatformLatLngBounds *pigeonBounds = [FGMPlatformLatLngBounds + makeWithNortheast:[FGMPlatformLatLng makeWithLatitude:3 longitude:4] + southwest:[FGMPlatformLatLng makeWithLatitude:1 longitude:2]]; - GMSCoordinateBounds *bounds = - [FLTGoogleMapJSONConversions coordinateBoundsFromLatLongs:@[ latlong1, latlong2 ]]; + GMSCoordinateBounds *bounds = FGMGetCoordinateBoundsForPigeonLatLngBounds(pigeonBounds); const CGFloat accuracy = 0.001; XCTAssertEqualWithAccuracy(bounds.southWest.latitude, 1, accuracy); @@ -201,14 +194,20 @@ - (void)testMapViewTypeFromPigeonType { XCTAssertEqual(kGMSTypeNone, FGMGetMapViewTypeForPigeonMapType(FGMPlatformMapTypeNone)); } -- (void)testCameraUpdateFromArrayNewCameraPosition { - NSArray *channelValue = @[ - @"newCameraPosition", @{@"target" : @[ @1, @2 ], @"zoom" : @3, @"bearing" : @4, @"tilt" : @5} - ]; +- (void)testCameraUpdateFromNewCameraPosition { id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; + FGMPlatformCameraUpdateNewCameraPosition *newPositionUpdate = + [FGMPlatformCameraUpdateNewCameraPosition + makeWithCameraPosition:[FGMPlatformCameraPosition + makeWithBearing:4 + target:[FGMPlatformLatLng makeWithLatitude:1 + longitude:2] + tilt:5 + zoom:3]]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:newPositionUpdate]); [[classMockCameraUpdate expect] - setCamera:[FLTGoogleMapJSONConversions cameraPostionFromDictionary:channelValue[1]]]; + setCamera:FGMGetCameraPositionForPigeonCameraPosition(newPositionUpdate.cameraPosition)]; [classMockCameraUpdate stopMocking]; } @@ -226,10 +225,14 @@ - (void)testCameraUpdateFromArrayNewCameraPosition { // verified. // // The code in below test uses the 2nd approach. -- (void)skip_testCameraUpdateFromArrayNewLatLong { - NSArray *channelValue = @[ @"newLatLng", @[ @1, @2 ] ]; +- (void)skip_testCameraUpdateFromNewLatLong { + const CGFloat lat = 1; + const CGFloat lng = 2; + FGMPlatformCameraUpdateNewLatLng *platformUpdate = [FGMPlatformCameraUpdateNewLatLng + makeWithLatLng:[FGMPlatformLatLng makeWithLatitude:lat longitude:lng]]; - GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; + GMSCameraUpdate *update = FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); GMSMapViewOptions *options = [[GMSMapViewOptions alloc] init]; options.frame = CGRectZero; @@ -237,104 +240,150 @@ - (void)skip_testCameraUpdateFromArrayNewLatLong { GMSMapView *mapView = [[GMSMapView alloc] initWithOptions:options]; [mapView moveCamera:update]; const CGFloat accuracy = 0.001; - XCTAssertEqualWithAccuracy(mapView.camera.target.latitude, 1, + XCTAssertEqualWithAccuracy(mapView.camera.target.latitude, lat, accuracy); // mapView.camera.target.latitude is still 5. - XCTAssertEqualWithAccuracy(mapView.camera.target.longitude, 2, + XCTAssertEqualWithAccuracy(mapView.camera.target.longitude, lng, accuracy); // mapView.camera.target.longitude is still 6. } -- (void)testCameraUpdateFromArrayNewLatLngBounds { - NSArray *latlong1 = @[ @1, @2 ]; - NSArray *latlong2 = @[ @(3), @(4) ]; - GMSCoordinateBounds *bounds = - [FLTGoogleMapJSONConversions coordinateBoundsFromLatLongs:@[ latlong1, latlong2 ]]; +- (void)testCameraUpdateFromNewLatLngBounds { + FGMPlatformLatLngBounds *pigeonBounds = [FGMPlatformLatLngBounds + makeWithNortheast:[FGMPlatformLatLng makeWithLatitude:1 longitude:2] + southwest:[FGMPlatformLatLng makeWithLatitude:3 longitude:4]]; + GMSCoordinateBounds *bounds = FGMGetCoordinateBoundsForPigeonLatLngBounds(pigeonBounds); - NSArray *channelValue = @[ @"newLatLngBounds", @[ latlong1, latlong2 ], @20 ]; + const CGFloat padding = 20; + FGMPlatformCameraUpdateNewLatLngBounds *platformUpdate = [FGMPlatformCameraUpdateNewLatLngBounds + makeWithBounds:FGMGetPigeonLatLngBoundsForCoordinateBounds(bounds) + padding:padding]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); - [[classMockCameraUpdate expect] fitBounds:bounds withPadding:20]; + [[classMockCameraUpdate expect] fitBounds:bounds withPadding:padding]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromArrayNewLatLngZoom { - NSArray *channelValue = @[ @"newLatLngZoom", @[ @1, @2 ], @3 ]; +- (void)testCameraUpdateFromNewLatLngZoom { + const CGFloat lat = 1; + const CGFloat lng = 2; + const CGFloat zoom = 3; + FGMPlatformCameraUpdateNewLatLngZoom *platformUpdate = [FGMPlatformCameraUpdateNewLatLngZoom + makeWithLatLng:[FGMPlatformLatLng makeWithLatitude:lat longitude:lng] + zoom:zoom]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); - [[classMockCameraUpdate expect] setTarget:CLLocationCoordinate2DMake(1, 2) zoom:3]; + [[classMockCameraUpdate expect] setTarget:CLLocationCoordinate2DMake(lat, lng) zoom:zoom]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromArrayScrollBy { - NSArray *channelValue = @[ @"scrollBy", @1, @2 ]; +- (void)testCameraUpdateFromScrollBy { + const CGFloat x = 1; + const CGFloat y = 2; + FGMPlatformCameraUpdateScrollBy *platformUpdate = [FGMPlatformCameraUpdateScrollBy makeWithDx:x + dy:y]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); - [[classMockCameraUpdate expect] scrollByX:1 Y:2]; + [[classMockCameraUpdate expect] scrollByX:x Y:y]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromArrayZoomBy { - NSArray *channelValueNoPoint = @[ @"zoomBy", @1 ]; +- (void)testCameraUpdateFromZoomBy { + const CGFloat zoom = 1; + FGMPlatformCameraUpdateZoomBy *platformUpdateNoPoint = + [FGMPlatformCameraUpdateZoomBy makeWithAmount:zoom focus:nil]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdateNoPoint]); - [[classMockCameraUpdate expect] zoomBy:1]; + [[classMockCameraUpdate expect] zoomBy:zoom]; - NSArray *channelValueWithPoint = @[ @"zoomBy", @1, @[ @2, @3 ] ]; + const CGFloat x = 2; + const CGFloat y = 3; + FGMPlatformCameraUpdateZoomBy *platformUpdate = + [FGMPlatformCameraUpdateZoomBy makeWithAmount:zoom focus:[FGMPlatformPoint makeWithX:x y:y]]; - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueWithPoint]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); - [[classMockCameraUpdate expect] zoomBy:1 atPoint:CGPointMake(2, 3)]; + [[classMockCameraUpdate expect] zoomBy:zoom atPoint:CGPointMake(x, y)]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromArrayZoomIn { - NSArray *channelValueNoPoint = @[ @"zoomIn" ]; +- (void)testCameraUpdateFromZoomIn { + FGMPlatformCameraUpdateZoom *platformUpdate = [FGMPlatformCameraUpdateZoom makeWithOut:NO]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); [[classMockCameraUpdate expect] zoomIn]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromArrayZoomOut { - NSArray *channelValueNoPoint = @[ @"zoomOut" ]; +- (void)testCameraUpdateFromZoomOut { + FGMPlatformCameraUpdateZoom *platformUpdate = [FGMPlatformCameraUpdateZoom makeWithOut:YES]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); [[classMockCameraUpdate expect] zoomOut]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromArrayZoomTo { - NSArray *channelValueNoPoint = @[ @"zoomTo", @1 ]; +- (void)testCameraUpdateFromZoomTo { + const CGFloat zoom = 1; + FGMPlatformCameraUpdateZoomTo *platformUpdate = [FGMPlatformCameraUpdateZoomTo makeWithZoom:zoom]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; + FGMGetCameraUpdateForPigeonCameraUpdate( + [FGMPlatformCameraUpdate makeWithCameraUpdate:platformUpdate]); - [[classMockCameraUpdate expect] zoomTo:1]; + [[classMockCameraUpdate expect] zoomTo:zoom]; [classMockCameraUpdate stopMocking]; } +- (void)testStrokeStylesFromPatterns { + NSArray *patterns = @[ + [FGMPlatformPatternItem makeWithType:FGMPlatformPatternItemTypeGap length:@(1)], + [FGMPlatformPatternItem makeWithType:FGMPlatformPatternItemTypeDash length:@(1)] + ]; + UIColor *strokeColor = UIColor.redColor; + + NSArray *patternStrokeStyle = + FGMGetStrokeStylesFromPatterns(patterns, strokeColor); + + XCTAssertEqual(patternStrokeStyle.count, 2); + + // None of the parameters of `patternStrokeStyle` is observable, so we limit to testing + // the length of this output array. +} + - (void)testLengthsFromPatterns { - NSArray *> *patterns = @[ @[ @"gap", @10 ], @[ @"dash", @6.4 ] ]; + const CGFloat gapLength = 10; + const CGFloat dashLength = 6.4; + NSArray *patterns = @[ + [FGMPlatformPatternItem makeWithType:FGMPlatformPatternItemTypeGap length:@(gapLength)], + [FGMPlatformPatternItem makeWithType:FGMPlatformPatternItemTypeDash length:@(dashLength)] + ]; - NSArray *spanLengths = [FLTGoogleMapJSONConversions spanLengthsFromPatterns:patterns]; + NSArray *spanLengths = FGMGetSpanLengthsFromPatterns(patterns); - XCTAssertEqual([spanLengths count], 2); + XCTAssertEqual(spanLengths.count, 2); NSNumber *firstSpanLength = spanLengths[0]; NSNumber *secondSpanLength = spanLengths[1]; - XCTAssertEqual(firstSpanLength.doubleValue, 10); - XCTAssertEqual(secondSpanLength.doubleValue, 6.4); + XCTAssertEqual(firstSpanLength.doubleValue, gapLength); + XCTAssertEqual(secondSpanLength.doubleValue, dashLength); } - (void)testWeightedLatLngFromArray { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m index 0ce0f53e00c4..edb0ed03096d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m @@ -9,6 +9,7 @@ #import #import +#import #import "PartiallyMockedMapView.h" @interface GoogleMapsPolylinesControllerTests : XCTestCase @@ -20,13 +21,22 @@ @implementation GoogleMapsPolylinesControllerTests /// /// @return An object of FLTGoogleMapPolylineController - (FLTGoogleMapPolylineController *)polylineControllerWithMockedMap { - NSDictionary *polyline = @{ - @"points" : @[ - @[ @(52.4816), @(-3.1791) ], @[ @(54.043), @(-2.9925) ], @[ @(54.1396), @(-4.2739) ], - @[ @(53.4153), @(-4.0829) ] - ], - @"polylineId" : @"polyline_id_0", - }; + FGMPlatformPolyline *polyline = [FGMPlatformPolyline + makeWithPolylineId:@"polyline_id_0" + consumesTapEvents:NO + color:0 + geodesic:NO + jointType:FGMPlatformJointTypeRound + patterns:@[] + points:@[ + [FGMPlatformLatLng makeWithLatitude:52.4816 longitude:-3.1791], + [FGMPlatformLatLng makeWithLatitude:54.043 longitude:-2.9925], + [FGMPlatformLatLng makeWithLatitude:54.1396 longitude:-4.2739], + [FGMPlatformLatLng makeWithLatitude:53.4153 longitude:-4.0829], + ] + visible:NO + width:1 + zIndex:0]; CGRect frame = CGRectMake(0, 0, 100, 100); GMSCameraPosition *camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; @@ -37,12 +47,11 @@ - (FLTGoogleMapPolylineController *)polylineControllerWithMockedMap { PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; - GMSMutablePath *path = [FLTPolylinesController pathForPolyline:polyline]; - NSString *identifier = polyline[@"polylineId"]; + GMSMutablePath *path = FGMGetPathFromPoints(FGMGetPointsForPigeonLatLngs(polyline.points)); FLTGoogleMapPolylineController *polylineControllerWithMockedMap = [[FLTGoogleMapPolylineController alloc] initWithPath:path - identifier:identifier + identifier:polyline.polylineId mapView:mapView]; return polylineControllerWithMockedMap; @@ -50,7 +59,7 @@ - (FLTGoogleMapPolylineController *)polylineControllerWithMockedMap { - (void)testSetPatterns { NSArray *styles = @[ - [GMSStrokeStyle solidColor:[UIColor clearColor]], [GMSStrokeStyle solidColor:[UIColor redColor]] + [GMSStrokeStyle solidColor:UIColor.clearColor], [GMSStrokeStyle solidColor:UIColor.redColor] ]; NSArray *lengths = @[ @10, @10 ]; @@ -65,17 +74,4 @@ - (void)testSetPatterns { XCTAssertNotNil(polylineController.polyline.spans); } -- (void)testStrokeStylesFromPatterns { - NSArray *> *patterns = @[ @[ @"gap", @10 ], @[ @"dash", @10 ] ]; - UIColor *strokeColor = [UIColor redColor]; - - NSArray *patternStrokeStyle = - [FLTGoogleMapJSONConversions strokeStylesFromPatterns:patterns strokeColor:strokeColor]; - - XCTAssertEqual([patternStrokeStyle count], 2); - - // None of the parameters of `patternStrokeStyle` is observable, so we limit to testing - // the length of this output array. -} - @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj index 04a00376c7aa..cf3ec2ab9f0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 5B5EF9A6C72A03092BDA553E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28D4666EF03E57DB7D03E916 /* Pods_RunnerTests.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -78,6 +79,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, AF2E6994ED025F4BCE652A48 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -196,13 +198,15 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */, - 851CD952D11C6D91E216A90F /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -255,6 +259,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -340,24 +347,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 851CD952D11C6D91E216A90F /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -694,6 +683,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b9b0e81d05f8..cab978a8e3b6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -86,6 +86,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h index ba68ca7a5b5b..2d853e9bfe98 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h @@ -42,6 +42,15 @@ extern FGMPlatformCameraPosition *FGMGetPigeonCameraPositionForPosition( extern GMSCameraPosition *FGMGetCameraPositionForPigeonCameraPosition( FGMPlatformCameraPosition *position); +/// Creates a CLLocation array from its Pigeon equivalent. +extern NSArray *FGMGetPointsForPigeonLatLngs(NSArray *points); + +/// Creates a CLLocation arary array, representing a set of holes, from its Pigeon equivalent. +extern NSArray *> *FGMGetHolesForPigeonLatLngArrays( + NSArray *> *points); + +extern GMSMutablePath *FGMGetPathFromPoints(NSArray *points); + /// Creates a GMSMapViewType from its Pigeon representation. extern GMSMapViewType FGMGetMapViewTypeForPigeonMapType(FGMPlatformMapType type); @@ -49,6 +58,21 @@ extern GMSMapViewType FGMGetMapViewTypeForPigeonMapType(FGMPlatformMapType type) extern FGMPlatformCluster *FGMGetPigeonCluster(GMUStaticCluster *cluster, NSString *clusterManagerIdentifier); +/// Creates a GMSCameraUpdate from its Pigeon equivalent. +extern GMSCameraUpdate *_Nullable FGMGetCameraUpdateForPigeonCameraUpdate( + FGMPlatformCameraUpdate *update); + +/// Creates a UIColor from its RGBA components, expressed as an integer. +extern UIColor *FGMGetColorForRGBA(NSInteger rgba); + +/// Creates an array of GMSStrokeStyles using the given patterns and stroke color. +extern NSArray *FGMGetStrokeStylesFromPatterns( + NSArray *patterns, UIColor *strokeColor); + +/// Creates an array of span lengths using the given patterns. +extern NSArray *FGMGetSpanLengthsFromPatterns( + NSArray *patterns); + @interface FLTGoogleMapJSONConversions : NSObject extern NSString *const kHeatmapsToAddKey; @@ -68,11 +92,6 @@ extern NSString *const kHeatmapGradientColorMapSizeKey; + (NSArray *)arrayFromLocation:(CLLocationCoordinate2D)location; + (UIColor *)colorFromRGBA:(NSNumber *)data; + (NSNumber *)RGBAFromColor:(UIColor *)color; -+ (NSArray *)pointsFromLatLongs:(NSArray *)data; -+ (NSArray *> *)holesFromPointsArray:(NSArray *)data; -+ (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)channelValue; -+ (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs; -+ (nullable GMSCameraUpdate *)cameraUpdateFromArray:(NSArray *)channelValue; + (nullable GMUWeightedLatLng *)weightedLatLngFromArray:(NSArray *)data; + (NSArray *)arrayFromWeightedLatLng:(GMUWeightedLatLng *)weightedLatLng; + (NSArray *)weightedDataFromArray:(NSArray *> *)data; @@ -80,21 +99,6 @@ extern NSString *const kHeatmapGradientColorMapSizeKey; + (GMUGradient *)gradientFromDictionary:(NSDictionary *)data; + (NSDictionary *)dictionaryFromGradient:(GMUGradient *)gradient; -/// Return GMS strokestyle object array populated using the patterns and stroke colors passed in. -/// -/// @param patterns An array of patterns for each stroke in the polyline. -/// @param strokeColor An array of color for each stroke in the polyline. -/// @return An array of GMSStrokeStyle. -+ (NSArray *)strokeStylesFromPatterns:(NSArray *> *)patterns - strokeColor:(UIColor *)strokeColor; - -/// Return GMS strokestyle object array populated using the patterns and stroke colors passed in. -/// Extracts the lengths of each stroke in the polyline from patterns input -/// -/// @param patterns An array of object representing the pattern params in the polyline. -/// @return Array of lengths. -+ (NSArray *)spanLengthsFromPatterns:(NSArray *> *)patterns; - @end NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index 99439c48dd2d..25504291192e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -54,7 +54,34 @@ CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng viewingAngle:position.tilt]; } -extern GMSMapViewType FGMGetMapViewTypeForPigeonMapType(FGMPlatformMapType type) { +NSArray *FGMGetPointsForPigeonLatLngs(NSArray *pigeonPoints) { + NSMutableArray *points = [[NSMutableArray alloc] initWithCapacity:pigeonPoints.count]; + for (FGMPlatformLatLng *point in pigeonPoints) { + [points addObject:[[CLLocation alloc] initWithLatitude:point.latitude + longitude:point.longitude]]; + } + return points; +} + +NSArray *> *FGMGetHolesForPigeonLatLngArrays( + NSArray *> *pigeonHolePoints) { + NSMutableArray *> *holes = + [[NSMutableArray alloc] initWithCapacity:pigeonHolePoints.count]; + for (NSArray *holePoints in pigeonHolePoints) { + [holes addObject:FGMGetPointsForPigeonLatLngs(holePoints)]; + } + return holes; +} + +GMSMutablePath *FGMGetPathFromPoints(NSArray *points) { + GMSMutablePath *path = [GMSMutablePath path]; + for (CLLocation *location in points) { + [path addCoordinate:location.coordinate]; + } + return path; +} + +GMSMapViewType FGMGetMapViewTypeForPigeonMapType(FGMPlatformMapType type) { switch (type) { case FGMPlatformMapTypeNone: return kGMSTypeNone; @@ -86,6 +113,77 @@ extern GMSMapViewType FGMGetMapViewTypeForPigeonMapType(FGMPlatformMapType type) markerIds:markerIDs]; } +GMSCameraUpdate *FGMGetCameraUpdateForPigeonCameraUpdate(FGMPlatformCameraUpdate *cameraUpdate) { + // See note in messages.dart for why this is so loosely typed. + id update = cameraUpdate.cameraUpdate; + if ([update isKindOfClass:[FGMPlatformCameraUpdateNewCameraPosition class]]) { + return [GMSCameraUpdate + setCamera:FGMGetCameraPositionForPigeonCameraPosition( + ((FGMPlatformCameraUpdateNewCameraPosition *)update).cameraPosition)]; + } else if ([update isKindOfClass:[FGMPlatformCameraUpdateNewLatLng class]]) { + return [GMSCameraUpdate setTarget:FGMGetCoordinateForPigeonLatLng( + ((FGMPlatformCameraUpdateNewLatLng *)update).latLng)]; + } else if ([update isKindOfClass:[FGMPlatformCameraUpdateNewLatLngBounds class]]) { + FGMPlatformCameraUpdateNewLatLngBounds *typedUpdate = + (FGMPlatformCameraUpdateNewLatLngBounds *)update; + return + [GMSCameraUpdate fitBounds:FGMGetCoordinateBoundsForPigeonLatLngBounds(typedUpdate.bounds) + withPadding:typedUpdate.padding]; + } else if ([update isKindOfClass:[FGMPlatformCameraUpdateNewLatLngZoom class]]) { + FGMPlatformCameraUpdateNewLatLngZoom *typedUpdate = + (FGMPlatformCameraUpdateNewLatLngZoom *)update; + return [GMSCameraUpdate setTarget:FGMGetCoordinateForPigeonLatLng(typedUpdate.latLng) + zoom:typedUpdate.zoom]; + } else if ([update isKindOfClass:[FGMPlatformCameraUpdateScrollBy class]]) { + FGMPlatformCameraUpdateScrollBy *typedUpdate = (FGMPlatformCameraUpdateScrollBy *)update; + return [GMSCameraUpdate scrollByX:typedUpdate.dx Y:typedUpdate.dy]; + } else if ([update isKindOfClass:[FGMPlatformCameraUpdateZoomBy class]]) { + FGMPlatformCameraUpdateZoomBy *typedUpdate = (FGMPlatformCameraUpdateZoomBy *)update; + if (typedUpdate.focus) { + return [GMSCameraUpdate zoomBy:typedUpdate.amount + atPoint:FGMGetCGPointForPigeonPoint(typedUpdate.focus)]; + } else { + return [GMSCameraUpdate zoomBy:typedUpdate.amount]; + } + } else if ([update isKindOfClass:[FGMPlatformCameraUpdateZoom class]]) { + if (((FGMPlatformCameraUpdateZoom *)update).out) { + return [GMSCameraUpdate zoomOut]; + } else { + return [GMSCameraUpdate zoomIn]; + } + } else if ([update isKindOfClass:[FGMPlatformCameraUpdateZoomTo class]]) { + return [GMSCameraUpdate zoomTo:((FGMPlatformCameraUpdateZoomTo *)update).zoom]; + } + return nil; +} + +UIColor *FGMGetColorForRGBA(NSInteger rgba) { + return [UIColor colorWithRed:((CGFloat)((rgba & 0xFF0000) >> 16)) / 255.0 + green:((CGFloat)((rgba & 0xFF00) >> 8)) / 255.0 + blue:((CGFloat)(rgba & 0xFF)) / 255.0 + alpha:((CGFloat)((rgba & 0xFF000000) >> 24)) / 255.0]; +} + +NSArray *FGMGetStrokeStylesFromPatterns( + NSArray *patterns, UIColor *strokeColor) { + NSMutableArray *strokeStyles = [[NSMutableArray alloc] initWithCapacity:[patterns count]]; + for (FGMPlatformPatternItem *pattern in patterns) { + UIColor *color = + pattern.type == FGMPlatformPatternItemTypeGap ? UIColor.clearColor : strokeColor; + [strokeStyles addObject:[GMSStrokeStyle solidColor:color]]; + } + return strokeStyles; +} + +NSArray *FGMGetSpanLengthsFromPatterns(NSArray *patterns) { + NSMutableArray *lengths = [[NSMutableArray alloc] initWithCapacity:[patterns count]]; + for (FGMPlatformPatternItem *pattern in patterns) { + NSNumber *length = pattern.length ?: @0; + [lengths addObject:length]; + } + return lengths; +} + @implementation FLTGoogleMapJSONConversions // These constants must match the corresponding constants in serialization.dart @@ -114,11 +212,7 @@ + (NSArray *)arrayFromLocation:(CLLocationCoordinate2D)location { } + (UIColor *)colorFromRGBA:(NSNumber *)numberColor { - unsigned long value = [numberColor unsignedLongValue]; - return [UIColor colorWithRed:((float)((value & 0xFF0000) >> 16)) / 255.0 - green:((float)((value & 0xFF00) >> 8)) / 255.0 - blue:((float)(value & 0xFF)) / 255.0 - alpha:((float)((value & 0xFF000000) >> 24)) / 255.0]; + return FGMGetColorForRGBA(numberColor.unsignedLongValue); } + (NSNumber *)RGBAFromColor:(UIColor *)color { @@ -129,104 +223,6 @@ + (NSNumber *)RGBAFromColor:(UIColor *)color { return @(value); } -+ (NSArray *)pointsFromLatLongs:(NSArray *)data { - NSMutableArray *points = [[NSMutableArray alloc] init]; - for (unsigned i = 0; i < [data count]; i++) { - NSNumber *latitude = data[i][0]; - NSNumber *longitude = data[i][1]; - CLLocation *point = [[CLLocation alloc] initWithLatitude:[latitude doubleValue] - longitude:[longitude doubleValue]]; - [points addObject:point]; - } - - return points; -} - -+ (NSArray *> *)holesFromPointsArray:(NSArray *)data { - NSMutableArray *> *holes = [[[NSMutableArray alloc] init] init]; - for (unsigned i = 0; i < [data count]; i++) { - NSArray *points = [FLTGoogleMapJSONConversions pointsFromLatLongs:data[i]]; - [holes addObject:points]; - } - - return holes; -} - -+ (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)data { - if (!data) { - return nil; - } - return [GMSCameraPosition - cameraWithTarget:[FLTGoogleMapJSONConversions locationFromLatLong:data[@"target"]] - zoom:[data[@"zoom"] floatValue] - bearing:[data[@"bearing"] doubleValue] - viewingAngle:[data[@"tilt"] doubleValue]]; -} - -+ (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs { - return [[GMSCoordinateBounds alloc] - initWithCoordinate:[FLTGoogleMapJSONConversions locationFromLatLong:latlongs[0]] - coordinate:[FLTGoogleMapJSONConversions locationFromLatLong:latlongs[1]]]; -} - -+ (nullable GMSCameraUpdate *)cameraUpdateFromArray:(NSArray *)channelValue { - NSString *update = channelValue[0]; - if ([update isEqualToString:@"newCameraPosition"]) { - return [GMSCameraUpdate - setCamera:[FLTGoogleMapJSONConversions cameraPostionFromDictionary:channelValue[1]]]; - } else if ([update isEqualToString:@"newLatLng"]) { - return [GMSCameraUpdate - setTarget:[FLTGoogleMapJSONConversions locationFromLatLong:channelValue[1]]]; - } else if ([update isEqualToString:@"newLatLngBounds"]) { - return [GMSCameraUpdate - fitBounds:[FLTGoogleMapJSONConversions coordinateBoundsFromLatLongs:channelValue[1]] - withPadding:[channelValue[2] doubleValue]]; - } else if ([update isEqualToString:@"newLatLngZoom"]) { - return - [GMSCameraUpdate setTarget:[FLTGoogleMapJSONConversions locationFromLatLong:channelValue[1]] - zoom:[channelValue[2] floatValue]]; - } else if ([update isEqualToString:@"scrollBy"]) { - return [GMSCameraUpdate scrollByX:[channelValue[1] doubleValue] - Y:[channelValue[2] doubleValue]]; - } else if ([update isEqualToString:@"zoomBy"]) { - if (channelValue.count == 2) { - return [GMSCameraUpdate zoomBy:[channelValue[1] floatValue]]; - } else { - return [GMSCameraUpdate zoomBy:[channelValue[1] floatValue] - atPoint:[FLTGoogleMapJSONConversions pointFromArray:channelValue[2]]]; - } - } else if ([update isEqualToString:@"zoomIn"]) { - return [GMSCameraUpdate zoomIn]; - } else if ([update isEqualToString:@"zoomOut"]) { - return [GMSCameraUpdate zoomOut]; - } else if ([update isEqualToString:@"zoomTo"]) { - return [GMSCameraUpdate zoomTo:[channelValue[1] floatValue]]; - } - return nil; -} - -+ (NSArray *)strokeStylesFromPatterns:(NSArray *> *)patterns - strokeColor:(UIColor *)strokeColor { - NSMutableArray *strokeStyles = [[NSMutableArray alloc] initWithCapacity:[patterns count]]; - for (NSArray *pattern in patterns) { - NSString *patternType = pattern[0]; - UIColor *color = [patternType isEqualToString:@"gap"] ? [UIColor clearColor] : strokeColor; - [strokeStyles addObject:[GMSStrokeStyle solidColor:color]]; - } - - return strokeStyles; -} - -+ (NSArray *)spanLengthsFromPatterns:(NSArray *> *)patterns { - NSMutableArray *lengths = [[NSMutableArray alloc] initWithCapacity:[patterns count]]; - for (NSArray *pattern in patterns) { - NSNumber *length = [pattern count] > 1 ? pattern[1] : @0; - [lengths addObject:length]; - } - - return lengths; -} - + (GMUWeightedLatLng *)weightedLatLngFromArray:(NSArray *)data { NSAssert(data.count == 2, @"WeightedLatLng data must have length of 2"); if (data.count != 2) { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h index 223de202bfc8..924c4475d493 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h @@ -13,9 +13,9 @@ NS_ASSUME_NONNULL_BEGIN /// The layer managed by this controller instance. @property(readonly, nonatomic) GMSTileLayer *layer; -- (instancetype)initWithTileLayer:(GMSTileLayer *)tileLayer - mapView:(GMSMapView *)mapView - options:(NSDictionary *)optionsData; +- (instancetype)initWithTileOverlay:(FGMPlatformTileOverlay *)tileOverlay + tileLayer:(GMSTileLayer *)tileLayer + mapView:(GMSMapView *)mapView; - (void)removeTileOverlay; - (void)clearTileCache; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m index 9897cd93da64..37b2f8b6311e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -14,14 +14,15 @@ @interface FLTGoogleMapTileOverlayController () @implementation FLTGoogleMapTileOverlayController -- (instancetype)initWithTileLayer:(GMSTileLayer *)tileLayer - mapView:(GMSMapView *)mapView - options:(NSDictionary *)optionsData { +- (instancetype)initWithTileOverlay:(FGMPlatformTileOverlay *)tileOverlay + tileLayer:(GMSTileLayer *)tileLayer + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _layer = tileLayer; _mapView = mapView; - [self interpretTileOverlayOptions:optionsData]; + // TODO(stuartmorgan: Refactor to avoid this call to an instance method in init. + [self updateFromPlatformTileOverlay:tileOverlay]; } return self; } @@ -55,34 +56,12 @@ - (void)setTileSize:(NSInteger)tileSize { self.layer.tileSize = tileSize; } -- (void)interpretTileOverlayOptions:(NSDictionary *)data { - if (!data) { - return; - } - NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); - if (visible) { - [self setVisible:visible.boolValue]; - } - - NSNumber *transparency = FGMGetValueOrNilFromDict(data, @"transparency"); - if (transparency) { - [self setTransparency:transparency.floatValue]; - } - - NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); - if (zIndex) { - [self setZIndex:zIndex.intValue]; - } - - NSNumber *fadeIn = FGMGetValueOrNilFromDict(data, @"fadeIn"); - if (fadeIn) { - [self setFadeIn:fadeIn.boolValue]; - } - - NSNumber *tileSize = FGMGetValueOrNilFromDict(data, @"tileSize"); - if (tileSize) { - [self setTileSize:tileSize.integerValue]; - } +- (void)updateFromPlatformTileOverlay:(FGMPlatformTileOverlay *)overlay { + [self setVisible:overlay.visible]; + [self setTransparency:overlay.transparency]; + [self setZIndex:(int)overlay.zIndex]; + [self setFadeIn:overlay.fadeIn]; + [self setTileSize:overlay.tileSize]; } @end @@ -182,24 +161,24 @@ - (instancetype)initWithMapView:(GMSMapView *)mapView - (void)addTileOverlays:(NSArray *)tileOverlaysToAdd { for (FGMPlatformTileOverlay *tileOverlay in tileOverlaysToAdd) { - NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay.json]; + NSString *identifier = tileOverlay.tileOverlayId; FLTTileProviderController *tileProvider = [[FLTTileProviderController alloc] initWithTileOverlayIdentifier:identifier callbackHandler:self.callbackHandler]; FLTGoogleMapTileOverlayController *controller = - [[FLTGoogleMapTileOverlayController alloc] initWithTileLayer:tileProvider - mapView:self.mapView - options:tileOverlay.json]; + [[FLTGoogleMapTileOverlayController alloc] initWithTileOverlay:tileOverlay + tileLayer:tileProvider + mapView:self.mapView]; self.tileOverlayIdentifierToController[identifier] = controller; } } - (void)changeTileOverlays:(NSArray *)tileOverlaysToChange { for (FGMPlatformTileOverlay *tileOverlay in tileOverlaysToChange) { - NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay.json]; + NSString *identifier = tileOverlay.tileOverlayId; FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdentifierToController[identifier]; - [controller interpretTileOverlayOptions:tileOverlay.json]; + [controller updateFromPlatformTileOverlay:tileOverlay]; } } - (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers { @@ -224,8 +203,4 @@ - (nullable FLTGoogleMapTileOverlayController *)tileOverlayWithIdentifier:(NSStr return self.tileOverlayIdentifierToController[identifier]; } -+ (NSString *)identifierForTileOverlay:(NSDictionary *)tileOverlay { - return tileOverlay[@"tileOverlayId"]; -} - @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h index c40340e08db4..3262b37317f2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h @@ -9,11 +9,8 @@ // Defines circle controllable by Flutter. @interface FLTGoogleMapCircleController : NSObject -- (instancetype)initCircleWithPosition:(CLLocationCoordinate2D)position - radius:(CLLocationDistance)radius - circleId:(NSString *)circleIdentifier - mapView:(GMSMapView *)mapView - options:(NSDictionary *)options; +- (instancetype)initCircleWithPlatformCircle:(FGMPlatformCircle *)circle + mapView:(GMSMapView *)mapView; - (void)removeCircle; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m index ca71d3150221..683538a67eea 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m @@ -14,17 +14,16 @@ @interface FLTGoogleMapCircleController () @implementation FLTGoogleMapCircleController -- (instancetype)initCircleWithPosition:(CLLocationCoordinate2D)position - radius:(CLLocationDistance)radius - circleId:(NSString *)circleIdentifier - mapView:(GMSMapView *)mapView - options:(NSDictionary *)options { +- (instancetype)initCircleWithPlatformCircle:(FGMPlatformCircle *)circle + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { - _circle = [GMSCircle circleWithPosition:position radius:radius]; + _circle = [GMSCircle circleWithPosition:FGMGetCoordinateForPigeonLatLng(circle.center) + radius:circle.radius]; _mapView = mapView; - _circle.userData = @[ circleIdentifier ]; - [self interpretCircleOptions:options]; + _circle.userData = @[ circle.circleId ]; + // TODO(stuartmorgan: Refactor to avoid this call to an instance method in init. + [self updateFromPlatformCircle:circle]; } return self; } @@ -59,46 +58,15 @@ - (void)setFillColor:(UIColor *)color { self.circle.fillColor = color; } -- (void)interpretCircleOptions:(NSDictionary *)data { - NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); - if (consumeTapEvents) { - [self setConsumeTapEvents:consumeTapEvents.boolValue]; - } - - NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); - if (visible) { - [self setVisible:[visible boolValue]]; - } - - NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); - if (zIndex) { - [self setZIndex:[zIndex intValue]]; - } - - NSArray *center = FGMGetValueOrNilFromDict(data, @"center"); - if (center) { - [self setCenter:[FLTGoogleMapJSONConversions locationFromLatLong:center]]; - } - - NSNumber *radius = FGMGetValueOrNilFromDict(data, @"radius"); - if (radius) { - [self setRadius:[radius floatValue]]; - } - - NSNumber *strokeColor = FGMGetValueOrNilFromDict(data, @"strokeColor"); - if (strokeColor) { - [self setStrokeColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; - } - - NSNumber *strokeWidth = FGMGetValueOrNilFromDict(data, @"strokeWidth"); - if (strokeWidth) { - [self setStrokeWidth:[strokeWidth intValue]]; - } - - NSNumber *fillColor = FGMGetValueOrNilFromDict(data, @"fillColor"); - if (fillColor) { - [self setFillColor:[FLTGoogleMapJSONConversions colorFromRGBA:fillColor]]; - } +- (void)updateFromPlatformCircle:(FGMPlatformCircle *)platformCircle { + [self setConsumeTapEvents:platformCircle.consumeTapEvents]; + [self setVisible:platformCircle.visible]; + [self setZIndex:platformCircle.zIndex]; + [self setCenter:FGMGetCoordinateForPigeonLatLng(platformCircle.center)]; + [self setRadius:platformCircle.radius]; + [self setStrokeColor:FGMGetColorForRGBA(platformCircle.strokeColor)]; + [self setStrokeWidth:platformCircle.strokeWidth]; + [self setFillColor:FGMGetColorForRGBA(platformCircle.fillColor)]; } @end @@ -127,24 +95,17 @@ - (instancetype)initWithMapView:(GMSMapView *)mapView - (void)addCircles:(NSArray *)circlesToAdd { for (FGMPlatformCircle *circle in circlesToAdd) { - CLLocationCoordinate2D position = [FLTCirclesController getPosition:circle.json]; - CLLocationDistance radius = [FLTCirclesController getRadius:circle.json]; - NSString *circleId = [FLTCirclesController getCircleId:circle.json]; FLTGoogleMapCircleController *controller = - [[FLTGoogleMapCircleController alloc] initCircleWithPosition:position - radius:radius - circleId:circleId - mapView:self.mapView - options:circle.json]; - self.circleIdToController[circleId] = controller; + [[FLTGoogleMapCircleController alloc] initCircleWithPlatformCircle:circle + mapView:self.mapView]; + self.circleIdToController[circle.circleId] = controller; } } - (void)changeCircles:(NSArray *)circlesToChange { for (FGMPlatformCircle *circle in circlesToChange) { - NSString *circleId = [FLTCirclesController getCircleId:circle.json]; - FLTGoogleMapCircleController *controller = self.circleIdToController[circleId]; - [controller interpretCircleOptions:circle.json]; + FLTGoogleMapCircleController *controller = self.circleIdToController[circle.circleId]; + [controller updateFromPlatformCircle:circle]; } } @@ -179,18 +140,4 @@ - (void)didTapCircleWithIdentifier:(NSString *)identifier { }]; } -+ (CLLocationCoordinate2D)getPosition:(NSDictionary *)circle { - NSArray *center = circle[@"center"]; - return [FLTGoogleMapJSONConversions locationFromLatLong:center]; -} - -+ (CLLocationDistance)getRadius:(NSDictionary *)circle { - NSNumber *radius = circle[@"radius"]; - return [radius floatValue]; -} - -+ (NSString *)getCircleId:(NSDictionary *)circle { - return circle[@"circleId"]; -} - @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 8f913c464219..0108a3f72b2a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -645,11 +645,11 @@ - (nullable FGMPlatformLatLngBounds *)visibleMapRegion: - (void)moveCameraWithUpdate:(nonnull FGMPlatformCameraUpdate *)cameraUpdate error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromArray:cameraUpdate.json]; + GMSCameraUpdate *update = FGMGetCameraUpdateForPigeonCameraUpdate(cameraUpdate); if (!update) { *error = [FlutterError errorWithCode:@"Invalid update" message:@"Unrecognized camera update" - details:cameraUpdate.json]; + details:nil]; return; } [self.controller.mapView moveCamera:update]; @@ -657,11 +657,11 @@ - (void)moveCameraWithUpdate:(nonnull FGMPlatformCameraUpdate *)cameraUpdate - (void)animateCameraWithUpdate:(nonnull FGMPlatformCameraUpdate *)cameraUpdate error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromArray:cameraUpdate.json]; + GMSCameraUpdate *update = FGMGetCameraUpdateForPigeonCameraUpdate(cameraUpdate); if (!update) { *error = [FlutterError errorWithCode:@"Invalid update" message:@"Unrecognized camera update" - details:cameraUpdate.json]; + details:nil]; return; } [self.controller.mapView animateWithCameraUpdate:update]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index 648ac9f37330..cfbc9392159f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -106,51 +106,22 @@ - (void)setZIndex:(int)zIndex { self.marker.zIndex = zIndex; } -- (void)interpretMarkerOptions:(NSDictionary *)data - registrar:(NSObject *)registrar - screenScale:(CGFloat)screenScale { - NSNumber *alpha = FGMGetValueOrNilFromDict(data, @"alpha"); - if (alpha) { - [self setAlpha:[alpha floatValue]]; - } - NSArray *anchor = FGMGetValueOrNilFromDict(data, @"anchor"); - if (anchor) { - [self setAnchor:[FLTGoogleMapJSONConversions pointFromArray:anchor]]; - } - NSNumber *draggable = FGMGetValueOrNilFromDict(data, @"draggable"); - if (draggable) { - [self setDraggable:[draggable boolValue]]; - } - NSArray *icon = FGMGetValueOrNilFromDict(data, @"icon"); - if (icon) { - UIImage *image = [self extractIconFromData:icon registrar:registrar screenScale:screenScale]; - [self setIcon:image]; - } - NSNumber *flat = FGMGetValueOrNilFromDict(data, @"flat"); - if (flat) { - [self setFlat:[flat boolValue]]; - } - NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); - if (consumeTapEvents) { - [self setConsumeTapEvents:[consumeTapEvents boolValue]]; - } - [self interpretInfoWindow:data]; - NSArray *position = FGMGetValueOrNilFromDict(data, @"position"); - if (position) { - [self setPosition:[FLTGoogleMapJSONConversions locationFromLatLong:position]]; - } - NSNumber *rotation = FGMGetValueOrNilFromDict(data, @"rotation"); - if (rotation) { - [self setRotation:[rotation doubleValue]]; - } - NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); - if (visible) { - [self setVisible:[visible boolValue]]; - } - NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); - if (zIndex) { - [self setZIndex:[zIndex intValue]]; - } +- (void)updateFromPlatformMarker:(FGMPlatformMarker *)platformMarker + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale { + [self setAlpha:platformMarker.alpha]; + [self setAnchor:FGMGetCGPointForPigeonPoint(platformMarker.anchor)]; + [self setDraggable:platformMarker.draggable]; + UIImage *image = [self iconFromBitmap:platformMarker.icon + registrar:registrar + screenScale:screenScale]; + [self setIcon:image]; + [self setFlat:platformMarker.flat]; + [self setConsumeTapEvents:platformMarker.consumeTapEvents]; + [self setPosition:FGMGetCoordinateForPigeonLatLng(platformMarker.position)]; + [self setRotation:platformMarker.rotation]; + [self setVisible:platformMarker.visible]; + [self setZIndex:platformMarker.zIndex]; } - (void)interpretInfoWindow:(NSDictionary *)data { @@ -168,86 +139,57 @@ - (void)interpretInfoWindow:(NSDictionary *)data { } } -- (UIImage *)extractIconFromData:(NSArray *)iconData - registrar:(NSObject *)registrar - screenScale:(CGFloat)screenScale { +- (UIImage *)iconFromBitmap:(FGMPlatformBitmap *)platformBitmap + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale { NSAssert(screenScale > 0, @"Screen scale must be greater than 0"); + // See comment in messages.dart for why this is so loosely typed. See also + // https://github.com/flutter/flutter/issues/117819. + id bitmap = platformBitmap.bitmap; UIImage *image; - if ([iconData.firstObject isEqualToString:@"defaultMarker"]) { - CGFloat hue = (iconData.count == 1) ? 0.0f : [iconData[1] doubleValue]; + if ([bitmap isKindOfClass:[FGMPlatformBitmapDefaultMarker class]]) { + FGMPlatformBitmapDefaultMarker *bitmapDefaultMarker = bitmap; + CGFloat hue = bitmapDefaultMarker.hue.doubleValue; image = [GMSMarker markerImageWithColor:[UIColor colorWithHue:hue / 360.0 saturation:1.0 brightness:0.7 alpha:1.0]]; - } else if ([iconData.firstObject isEqualToString:@"fromAsset"]) { + } else if ([bitmap isKindOfClass:[FGMPlatformBitmapAsset class]]) { // Deprecated: This message handling for 'fromAsset' has been replaced by 'asset'. // Refer to the flutter google_maps_flutter_platform_interface package for details. - if (iconData.count == 2) { - image = [UIImage imageNamed:[registrar lookupKeyForAsset:iconData[1]]]; + FGMPlatformBitmapAsset *bitmapAsset = bitmap; + if (bitmapAsset.pkg) { + image = [UIImage imageNamed:[registrar lookupKeyForAsset:bitmapAsset.name + fromPackage:bitmapAsset.pkg]]; } else { - image = [UIImage imageNamed:[registrar lookupKeyForAsset:iconData[1] - fromPackage:iconData[2]]]; + image = [UIImage imageNamed:[registrar lookupKeyForAsset:bitmapAsset.name]]; } - } else if ([iconData.firstObject isEqualToString:@"fromAssetImage"]) { + } else if ([bitmap isKindOfClass:[FGMPlatformBitmapAssetImage class]]) { // Deprecated: This message handling for 'fromAssetImage' has been replaced by 'asset'. // Refer to the flutter google_maps_flutter_platform_interface package for details. - if (iconData.count == 3) { - image = [UIImage imageNamed:[registrar lookupKeyForAsset:iconData[1]]]; - id scaleParam = iconData[2]; - image = [self scaleImage:image by:scaleParam]; - } else { - NSString *error = - [NSString stringWithFormat:@"'fromAssetImage' should have exactly 3 arguments. Got: %lu", - (unsigned long)iconData.count]; - NSException *exception = [NSException exceptionWithName:@"InvalidBitmapDescriptor" - reason:error - userInfo:nil]; - @throw exception; - } - } else if ([iconData[0] isEqualToString:@"fromBytes"]) { + FGMPlatformBitmapAssetImage *bitmapAssetImage = bitmap; + image = [UIImage imageNamed:[registrar lookupKeyForAsset:bitmapAssetImage.name]]; + image = [self scaleImage:image by:bitmapAssetImage.scale]; + } else if ([bitmap isKindOfClass:[FGMPlatformBitmapBytes class]]) { // Deprecated: This message handling for 'fromBytes' has been replaced by 'bytes'. // Refer to the flutter google_maps_flutter_platform_interface package for details. - if (iconData.count == 2) { - @try { - FlutterStandardTypedData *byteData = iconData[1]; - CGFloat mainScreenScale = [[UIScreen mainScreen] scale]; - image = [UIImage imageWithData:[byteData data] scale:mainScreenScale]; - } @catch (NSException *exception) { - @throw [NSException exceptionWithName:@"InvalidByteDescriptor" - reason:@"Unable to interpret bytes as a valid image." - userInfo:nil]; - } - } else { - NSString *error = [NSString - stringWithFormat:@"fromBytes should have exactly one argument, the bytes. Got: %lu", - (unsigned long)iconData.count]; - NSException *exception = [NSException exceptionWithName:@"InvalidByteDescriptor" - reason:error - userInfo:nil]; - @throw exception; - } - } else if ([iconData.firstObject isEqualToString:@"asset"]) { - NSDictionary *assetData = iconData[1]; - if (![assetData isKindOfClass:[NSDictionary class]]) { - NSException *exception = - [NSException exceptionWithName:@"InvalidByteDescriptor" - reason:@"Unable to interpret asset, expected a dictionary as the " - @"second parameter." - userInfo:nil]; - @throw exception; + FGMPlatformBitmapBytes *bitmapBytes = bitmap; + @try { + CGFloat mainScreenScale = [[UIScreen mainScreen] scale]; + image = [UIImage imageWithData:bitmapBytes.byteData.data scale:mainScreenScale]; + } @catch (NSException *exception) { + @throw [NSException exceptionWithName:@"InvalidByteDescriptor" + reason:@"Unable to interpret bytes as a valid image." + userInfo:nil]; } + } else if ([bitmap isKindOfClass:[FGMPlatformBitmapAssetMap class]]) { + FGMPlatformBitmapAssetMap *bitmapAssetMap = bitmap; - NSString *assetName = FGMGetValueOrNilFromDict(assetData, @"assetName"); - NSString *scalingMode = FGMGetValueOrNilFromDict(assetData, @"bitmapScaling"); - - image = [UIImage imageNamed:[registrar lookupKeyForAsset:assetName]]; - - if ([scalingMode isEqualToString:@"auto"]) { - NSNumber *width = FGMGetValueOrNilFromDict(assetData, @"width"); - NSNumber *height = FGMGetValueOrNilFromDict(assetData, @"height"); - CGFloat imagePixelRatio = - [FGMGetValueOrNilFromDict(assetData, @"imagePixelRatio") doubleValue]; + image = [UIImage imageNamed:[registrar lookupKeyForAsset:bitmapAssetMap.assetName]]; + if (bitmapAssetMap.bitmapScaling == FGMPlatformMapBitmapScalingAuto) { + NSNumber *width = bitmapAssetMap.width; + NSNumber *height = bitmapAssetMap.height; if (width || height) { image = [FLTGoogleMapMarkerController scaledImage:image withScale:screenScale]; image = [FLTGoogleMapMarkerController scaledImage:image @@ -255,44 +197,34 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData height:height screenScale:screenScale]; } else { - image = [FLTGoogleMapMarkerController scaledImage:image withScale:imagePixelRatio]; + image = [FLTGoogleMapMarkerController scaledImage:image + withScale:bitmapAssetMap.imagePixelRatio]; } } - } else if ([iconData[0] isEqualToString:@"bytes"]) { - NSDictionary *byteData = iconData[1]; - if (![byteData isKindOfClass:[NSDictionary class]]) { - NSException *exception = - [NSException exceptionWithName:@"InvalidByteDescriptor" - reason:@"Unable to interpret bytes, expected a dictionary as the " - @"second parameter." - userInfo:nil]; - @throw exception; - } - - FlutterStandardTypedData *bytes = FGMGetValueOrNilFromDict(byteData, @"byteData"); - NSString *scalingMode = FGMGetValueOrNilFromDict(byteData, @"bitmapScaling"); + } else if ([bitmap isKindOfClass:[FGMPlatformBitmapBytesMap class]]) { + FGMPlatformBitmapBytesMap *bitmapBytesMap = bitmap; + FlutterStandardTypedData *bytes = bitmapBytesMap.byteData; @try { - image = [UIImage imageWithData:[bytes data] scale:screenScale]; - if ([scalingMode isEqualToString:@"auto"]) { - NSNumber *width = FGMGetValueOrNilFromDict(byteData, @"width"); - NSNumber *height = FGMGetValueOrNilFromDict(byteData, @"height"); - CGFloat imagePixelRatio = - [FGMGetValueOrNilFromDict(byteData, @"imagePixelRatio") doubleValue]; + image = [UIImage imageWithData:bytes.data scale:screenScale]; + if (bitmapBytesMap.bitmapScaling == FGMPlatformMapBitmapScalingAuto) { + NSNumber *width = bitmapBytesMap.width; + NSNumber *height = bitmapBytesMap.height; if (width || height) { - // Before scaling the image, image must be in screenScale + // Before scaling the image, image must be in screenScale. image = [FLTGoogleMapMarkerController scaledImage:image withScale:screenScale]; image = [FLTGoogleMapMarkerController scaledImage:image withWidth:width height:height screenScale:screenScale]; } else { - image = [FLTGoogleMapMarkerController scaledImage:image withScale:imagePixelRatio]; + image = [FLTGoogleMapMarkerController scaledImage:image + withScale:bitmapBytesMap.imagePixelRatio]; } } else { // No scaling, load image from bytes without scale parameter. - image = [UIImage imageWithData:[bytes data]]; + image = [UIImage imageWithData:bytes.data]; } } @catch (NSException *exception) { @throw [NSException exceptionWithName:@"InvalidByteDescriptor" @@ -308,11 +240,7 @@ - (UIImage *)extractIconFromData:(NSArray *)iconData /// flutter google_maps_flutter_platform_interface package which has been replaced by 'bytes' /// message handling. It will be removed when the deprecated image bitmap description type /// 'fromBytes' is removed from the platform interface. -- (UIImage *)scaleImage:(UIImage *)image by:(id)scaleParam { - double scale = 1.0; - if ([scaleParam isKindOfClass:[NSNumber class]]) { - scale = [scaleParam doubleValue]; - } +- (UIImage *)scaleImage:(UIImage *)image by:(double)scale { if (fabs(scale - 1) > 1e-3) { return [UIImage imageWithCGImage:[image CGImage] scale:(image.scale * scale) @@ -474,23 +402,23 @@ - (instancetype)initWithMapView:(GMSMapView *)mapView - (void)addMarkers:(NSArray *)markersToAdd { for (FGMPlatformMarker *marker in markersToAdd) { - [self addJSONMarker:marker.json]; + [self addMarker:marker]; } } -- (void)addJSONMarker:(NSDictionary *)markerToAdd { - CLLocationCoordinate2D position = [FLTMarkersController getPosition:markerToAdd]; - NSString *markerIdentifier = markerToAdd[@"markerId"]; - NSString *clusterManagerIdentifier = markerToAdd[@"clusterManagerId"]; +- (void)addMarker:(FGMPlatformMarker *)markerToAdd { + CLLocationCoordinate2D position = FGMGetCoordinateForPigeonLatLng(markerToAdd.position); + NSString *markerIdentifier = markerToAdd.markerId; + NSString *clusterManagerIdentifier = markerToAdd.clusterManagerId; GMSMarker *marker = [GMSMarker markerWithPosition:position]; FLTGoogleMapMarkerController *controller = [[FLTGoogleMapMarkerController alloc] initWithMarker:marker markerIdentifier:markerIdentifier clusterManagerIdentifier:clusterManagerIdentifier mapView:self.mapView]; - [controller interpretMarkerOptions:markerToAdd - registrar:self.registrar - screenScale:[self getScreenScale]]; + [controller updateFromPlatformMarker:markerToAdd + registrar:self.registrar + screenScale:[self getScreenScale]]; if (clusterManagerIdentifier) { GMUClusterManager *clusterManager = [_clusterManagersController clusterManagerWithIdentifier:clusterManagerIdentifier]; @@ -508,8 +436,8 @@ - (void)changeMarkers:(NSArray *)markersToChange { } - (void)changeMarker:(FGMPlatformMarker *)markerToChange { - NSString *markerIdentifier = markerToChange.json[@"markerId"]; - NSString *clusterManagerIdentifier = markerToChange.json[@"clusterManagerId"]; + NSString *markerIdentifier = markerToChange.markerId; + NSString *clusterManagerIdentifier = markerToChange.clusterManagerId; FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[markerIdentifier]; if (!controller) { @@ -518,11 +446,11 @@ - (void)changeMarker:(FGMPlatformMarker *)markerToChange { NSString *previousClusterManagerIdentifier = [controller clusterManagerIdentifier]; if (![previousClusterManagerIdentifier isEqualToString:clusterManagerIdentifier]) { [self removeMarker:markerIdentifier]; - [self addJSONMarker:markerToChange.json]; + [self addMarker:markerToChange]; } else { - [controller interpretMarkerOptions:markerToChange.json - registrar:self.registrar - screenScale:[self getScreenScale]]; + [controller updateFromPlatformMarker:markerToChange + registrar:self.registrar + screenScale:[self getScreenScale]]; } } @@ -664,9 +592,4 @@ - (CGFloat)getScreenScale { return self.mapView.traitCollection.displayScale; } -+ (CLLocationCoordinate2D)getPosition:(NSDictionary *)marker { - NSArray *position = marker[@"position"]; - return [FLTGoogleMapJSONConversions locationFromLatLong:position]; -} - @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h index 84e6d0937a28..213f975c5e72 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h @@ -8,14 +8,14 @@ /// Extracts an icon image from the iconData array. /// -/// @param iconData An array containing the data for the icon image. +/// @param platformBitmap The Pigeon representation of the icon image. /// @param registrar A Flutter plugin registrar. /// @param screenScale Screen scale factor for scaling bitmaps. Must be greater than 0. /// @return A UIImage object created from the icon data. /// @note Assert unless screenScale is greater than 0. -- (UIImage *)extractIconFromData:(NSArray *)iconData - registrar:(NSObject *)registrar - screenScale:(CGFloat)screenScale; +- (UIImage *)iconFromBitmap:(FGMPlatformBitmap *)platformBitmap + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale; /// Checks if an image can be scaled from an original size to a target size using a scale factor /// while maintaining the aspect ratio. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m index a66b7f5d39d7..46d7234c085d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m @@ -40,12 +40,7 @@ - (void)setZIndex:(int)zIndex { self.polygon.zIndex = zIndex; } - (void)setPoints:(NSArray *)points { - GMSMutablePath *path = [GMSMutablePath path]; - - for (CLLocation *location in points) { - [path addCoordinate:location.coordinate]; - } - self.polygon.path = path; + self.polygon.path = FGMGetPathFromPoints(points); } - (void)setHoles:(NSArray *> *)rawHoles { NSMutableArray *holes = [[NSMutableArray alloc] init]; @@ -71,47 +66,16 @@ - (void)setStrokeWidth:(CGFloat)width { self.polygon.strokeWidth = width; } -- (void)interpretPolygonOptions:(NSDictionary *)data - registrar:(NSObject *)registrar { - NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); - if (consumeTapEvents) { - [self setConsumeTapEvents:[consumeTapEvents boolValue]]; - } - - NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); - if (visible) { - [self setVisible:[visible boolValue]]; - } - - NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); - if (zIndex) { - [self setZIndex:[zIndex intValue]]; - } - - NSArray *points = FGMGetValueOrNilFromDict(data, @"points"); - if (points) { - [self setPoints:[FLTGoogleMapJSONConversions pointsFromLatLongs:points]]; - } - - NSArray *holes = FGMGetValueOrNilFromDict(data, @"holes"); - if (holes) { - [self setHoles:[FLTGoogleMapJSONConversions holesFromPointsArray:holes]]; - } - - NSNumber *fillColor = FGMGetValueOrNilFromDict(data, @"fillColor"); - if (fillColor) { - [self setFillColor:[FLTGoogleMapJSONConversions colorFromRGBA:fillColor]]; - } - - NSNumber *strokeColor = FGMGetValueOrNilFromDict(data, @"strokeColor"); - if (strokeColor) { - [self setStrokeColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; - } - - NSNumber *strokeWidth = FGMGetValueOrNilFromDict(data, @"strokeWidth"); - if (strokeWidth) { - [self setStrokeWidth:[strokeWidth intValue]]; - } +- (void)updateFromPlatformPolygon:(FGMPlatformPolygon *)polygon + registrar:(NSObject *)registrar { + [self setConsumeTapEvents:polygon.consumesTapEvents]; + [self setVisible:polygon.visible]; + [self setZIndex:(int)polygon.zIndex]; + [self setPoints:FGMGetPointsForPigeonLatLngs(polygon.points)]; + [self setHoles:FGMGetHolesForPigeonLatLngArrays(polygon.holes)]; + [self setFillColor:FGMGetColorForRGBA(polygon.fillColor)]; + [self setStrokeColor:FGMGetColorForRGBA(polygon.strokeColor)]; + [self setStrokeWidth:polygon.strokeWidth]; } @end @@ -142,22 +106,22 @@ - (instancetype)initWithMapView:(GMSMapView *)mapView - (void)addPolygons:(NSArray *)polygonsToAdd { for (FGMPlatformPolygon *polygon in polygonsToAdd) { - GMSMutablePath *path = [FLTPolygonsController getPath:polygon.json]; - NSString *identifier = polygon.json[@"polygonId"]; + GMSMutablePath *path = FGMGetPathFromPoints(FGMGetPointsForPigeonLatLngs(polygon.points)); + NSString *identifier = polygon.polygonId; FLTGoogleMapPolygonController *controller = [[FLTGoogleMapPolygonController alloc] initWithPath:path identifier:identifier mapView:self.mapView]; - [controller interpretPolygonOptions:polygon.json registrar:self.registrar]; + [controller updateFromPlatformPolygon:polygon registrar:self.registrar]; self.polygonIdentifierToController[identifier] = controller; } } - (void)changePolygons:(NSArray *)polygonsToChange { for (FGMPlatformPolygon *polygon in polygonsToChange) { - NSString *identifier = polygon.json[@"polygonId"]; + NSString *identifier = polygon.polygonId; FLTGoogleMapPolygonController *controller = self.polygonIdentifierToController[identifier]; - [controller interpretPolygonOptions:polygon.json registrar:self.registrar]; + [controller updateFromPlatformPolygon:polygon registrar:self.registrar]; } } @@ -192,14 +156,4 @@ - (bool)hasPolygonWithIdentifier:(NSString *)identifier { return self.polygonIdentifierToController[identifier] != nil; } -+ (GMSMutablePath *)getPath:(NSDictionary *)polygon { - NSArray *pointArray = polygon[@"points"]; - NSArray *points = [FLTGoogleMapJSONConversions pointsFromLatLongs:pointArray]; - GMSMutablePath *path = [GMSMutablePath path]; - for (CLLocation *location in points) { - [path addCoordinate:location.coordinate]; - } - return path; -} - @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m index 44a31b18b531..16643b5e7e49 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m @@ -63,50 +63,17 @@ - (void)setPattern:(NSArray *)styles lengths:(NSArray *)registrar { - NSNumber *consumeTapEvents = FGMGetValueOrNilFromDict(data, @"consumeTapEvents"); - if (consumeTapEvents) { - [self setConsumeTapEvents:[consumeTapEvents boolValue]]; - } - - NSNumber *visible = FGMGetValueOrNilFromDict(data, @"visible"); - if (visible) { - [self setVisible:[visible boolValue]]; - } - - NSNumber *zIndex = FGMGetValueOrNilFromDict(data, @"zIndex"); - if (zIndex) { - [self setZIndex:[zIndex intValue]]; - } - - NSArray *points = FGMGetValueOrNilFromDict(data, @"points"); - if (points) { - [self setPoints:[FLTGoogleMapJSONConversions pointsFromLatLongs:points]]; - } - - NSNumber *strokeColor = FGMGetValueOrNilFromDict(data, @"color"); - if (strokeColor) { - [self setColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; - } - - NSNumber *strokeWidth = FGMGetValueOrNilFromDict(data, @"width"); - if (strokeWidth) { - [self setStrokeWidth:[strokeWidth intValue]]; - } - - NSNumber *geodesic = FGMGetValueOrNilFromDict(data, @"geodesic"); - if (geodesic) { - [self setGeodesic:geodesic.boolValue]; - } - - NSArray *patterns = FGMGetValueOrNilFromDict(data, @"pattern"); - if (patterns) { - [self - setPattern:[FLTGoogleMapJSONConversions strokeStylesFromPatterns:patterns - strokeColor:self.polyline.strokeColor] - lengths:[FLTGoogleMapJSONConversions spanLengthsFromPatterns:patterns]]; - } +- (void)updateFromPlatformPolyline:(FGMPlatformPolyline *)polyline + registrar:(NSObject *)registrar { + [self setConsumeTapEvents:polyline.consumesTapEvents]; + [self setVisible:polyline.visible]; + [self setZIndex:(int)polyline.zIndex]; + [self setPoints:FGMGetPointsForPigeonLatLngs(polyline.points)]; + [self setColor:FGMGetColorForRGBA(polyline.color)]; + [self setStrokeWidth:polyline.width]; + [self setGeodesic:polyline.geodesic]; + [self setPattern:FGMGetStrokeStylesFromPatterns(polyline.patterns, self.polyline.strokeColor) + lengths:FGMGetSpanLengthsFromPatterns(polyline.patterns)]; } @end @@ -138,22 +105,22 @@ - (instancetype)initWithMapView:(GMSMapView *)mapView - (void)addPolylines:(NSArray *)polylinesToAdd { for (FGMPlatformPolyline *polyline in polylinesToAdd) { - GMSMutablePath *path = [FLTPolylinesController pathForPolyline:polyline.json]; - NSString *identifier = polyline.json[@"polylineId"]; + GMSMutablePath *path = FGMGetPathFromPoints(FGMGetPointsForPigeonLatLngs(polyline.points)); + NSString *identifier = polyline.polylineId; FLTGoogleMapPolylineController *controller = [[FLTGoogleMapPolylineController alloc] initWithPath:path identifier:identifier mapView:self.mapView]; - [controller interpretPolylineOptions:polyline.json registrar:self.registrar]; + [controller updateFromPlatformPolyline:polyline registrar:self.registrar]; self.polylineIdentifierToController[identifier] = controller; } } - (void)changePolylines:(NSArray *)polylinesToChange { for (FGMPlatformPolyline *polyline in polylinesToChange) { - NSString *identifier = polyline.json[@"polylineId"]; + NSString *identifier = polyline.polylineId; FLTGoogleMapPolylineController *controller = self.polylineIdentifierToController[identifier]; - [controller interpretPolylineOptions:polyline.json registrar:self.registrar]; + [controller updateFromPlatformPolyline:polyline registrar:self.registrar]; } } @@ -188,14 +155,4 @@ - (bool)hasPolylineWithIdentifier:(NSString *)identifier { return self.polylineIdentifierToController[identifier] != nil; } -+ (GMSMutablePath *)pathForPolyline:(NSDictionary *)polyline { - NSArray *pointArray = polyline[@"points"]; - NSArray *points = [FLTGoogleMapJSONConversions pointsFromLatLongs:pointArray]; - GMSMutablePath *path = [GMSMutablePath path]; - for (CLLocation *location in points) { - [path addCoordinate:location.coordinate]; - } - return path; -} - @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h index 2b6e91e5d123..295c263beb01 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h @@ -11,14 +11,3 @@ @property(strong, nonatomic) GMSPolyline *polyline; @end - -/// Internal APIs explosed for unit testing -@interface FLTPolylinesController (Test) - -/// Returns the path for polyline based on the points(locations) the polyline has. -/// -/// @param polyline The polyline instance for which path is calculated. -/// @return An instance of GMSMutablePath. -+ (GMSMutablePath *)pathForPolyline:(NSDictionary *)polyline; - -@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h index 088b8a657147..b8c1bb5c35da 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -28,15 +28,63 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { - (instancetype)initWithValue:(FGMPlatformMapType)value; @end +/// Join types for polyline joints. +typedef NS_ENUM(NSUInteger, FGMPlatformJointType) { + FGMPlatformJointTypeMitered = 0, + FGMPlatformJointTypeBevel = 1, + FGMPlatformJointTypeRound = 2, +}; + +/// Wrapper for FGMPlatformJointType to allow for nullability. +@interface FGMPlatformJointTypeBox : NSObject +@property(nonatomic, assign) FGMPlatformJointType value; +- (instancetype)initWithValue:(FGMPlatformJointType)value; +@end + +/// Enumeration of possible types for PatternItem. +typedef NS_ENUM(NSUInteger, FGMPlatformPatternItemType) { + FGMPlatformPatternItemTypeDot = 0, + FGMPlatformPatternItemTypeDash = 1, + FGMPlatformPatternItemTypeGap = 2, +}; + +/// Wrapper for FGMPlatformPatternItemType to allow for nullability. +@interface FGMPlatformPatternItemTypeBox : NSObject +@property(nonatomic, assign) FGMPlatformPatternItemType value; +- (instancetype)initWithValue:(FGMPlatformPatternItemType)value; +@end + +/// Pigeon equivalent of [MapBitmapScaling]. +typedef NS_ENUM(NSUInteger, FGMPlatformMapBitmapScaling) { + FGMPlatformMapBitmapScalingAuto = 0, + FGMPlatformMapBitmapScalingNone = 1, +}; + +/// Wrapper for FGMPlatformMapBitmapScaling to allow for nullability. +@interface FGMPlatformMapBitmapScalingBox : NSObject +@property(nonatomic, assign) FGMPlatformMapBitmapScaling value; +- (instancetype)initWithValue:(FGMPlatformMapBitmapScaling)value; +@end + @class FGMPlatformCameraPosition; @class FGMPlatformCameraUpdate; +@class FGMPlatformCameraUpdateNewCameraPosition; +@class FGMPlatformCameraUpdateNewLatLng; +@class FGMPlatformCameraUpdateNewLatLngBounds; +@class FGMPlatformCameraUpdateNewLatLngZoom; +@class FGMPlatformCameraUpdateScrollBy; +@class FGMPlatformCameraUpdateZoomBy; +@class FGMPlatformCameraUpdateZoom; +@class FGMPlatformCameraUpdateZoomTo; @class FGMPlatformCircle; @class FGMPlatformHeatmap; +@class FGMPlatformInfoWindow; @class FGMPlatformCluster; @class FGMPlatformClusterManager; @class FGMPlatformMarker; @class FGMPlatformPolygon; @class FGMPlatformPolyline; +@class FGMPlatformPatternItem; @class FGMPlatformTile; @class FGMPlatformTileOverlay; @class FGMPlatformEdgeInsets; @@ -46,8 +94,16 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { @class FGMPlatformMapViewCreationParams; @class FGMPlatformMapConfiguration; @class FGMPlatformPoint; +@class FGMPlatformSize; @class FGMPlatformTileLayer; @class FGMPlatformZoomRange; +@class FGMPlatformBitmap; +@class FGMPlatformBitmapDefaultMarker; +@class FGMPlatformBitmapBytes; +@class FGMPlatformBitmapAsset; +@class FGMPlatformBitmapAssetImage; +@class FGMPlatformBitmapAssetMap; +@class FGMPlatformBitmapBytesMap; /// Pigeon representatation of a CameraPosition. @interface FGMPlatformCameraPosition : NSObject @@ -67,22 +123,104 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { @interface FGMPlatformCameraUpdate : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithJson:(id)json; -/// The update data, as JSON. This should only be set from -/// CameraUpdate.toJson, and the native code must interpret it according to the -/// internal implementation details of the CameraUpdate class. -@property(nonatomic, strong) id json; ++ (instancetype)makeWithCameraUpdate:(id)cameraUpdate; +/// This Object must be one of the classes below prefixed with +/// PlatformCameraUpdate. Each such class represents a different type of +/// camera update, and each holds a different set of data, preventing the +/// use of a single unified class. +@property(nonatomic, strong) id cameraUpdate; +@end + +/// Pigeon equivalent of NewCameraPosition +@interface FGMPlatformCameraUpdateNewCameraPosition : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithCameraPosition:(FGMPlatformCameraPosition *)cameraPosition; +@property(nonatomic, strong) FGMPlatformCameraPosition *cameraPosition; +@end + +/// Pigeon equivalent of NewLatLng +@interface FGMPlatformCameraUpdateNewLatLng : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithLatLng:(FGMPlatformLatLng *)latLng; +@property(nonatomic, strong) FGMPlatformLatLng *latLng; +@end + +/// Pigeon equivalent of NewLatLngBounds +@interface FGMPlatformCameraUpdateNewLatLngBounds : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithBounds:(FGMPlatformLatLngBounds *)bounds padding:(double)padding; +@property(nonatomic, strong) FGMPlatformLatLngBounds *bounds; +@property(nonatomic, assign) double padding; +@end + +/// Pigeon equivalent of NewLatLngZoom +@interface FGMPlatformCameraUpdateNewLatLngZoom : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithLatLng:(FGMPlatformLatLng *)latLng zoom:(double)zoom; +@property(nonatomic, strong) FGMPlatformLatLng *latLng; +@property(nonatomic, assign) double zoom; +@end + +/// Pigeon equivalent of ScrollBy +@interface FGMPlatformCameraUpdateScrollBy : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithDx:(double)dx dy:(double)dy; +@property(nonatomic, assign) double dx; +@property(nonatomic, assign) double dy; +@end + +/// Pigeon equivalent of ZoomBy +@interface FGMPlatformCameraUpdateZoomBy : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithAmount:(double)amount focus:(nullable FGMPlatformPoint *)focus; +@property(nonatomic, assign) double amount; +@property(nonatomic, strong, nullable) FGMPlatformPoint *focus; +@end + +/// Pigeon equivalent of ZoomIn/ZoomOut +@interface FGMPlatformCameraUpdateZoom : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithOut:(BOOL)out; +@property(nonatomic, assign) BOOL out; +@end + +/// Pigeon equivalent of ZoomTo +@interface FGMPlatformCameraUpdateZoomTo : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithZoom:(double)zoom; +@property(nonatomic, assign) double zoom; @end /// Pigeon equivalent of the Circle class. @interface FGMPlatformCircle : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithJson:(id)json; -/// The circle data, as JSON. This should only be set from -/// Circle.toJson, and the native code must interpret it according to the -/// internal implementation details of that method. -@property(nonatomic, strong) id json; ++ (instancetype)makeWithConsumeTapEvents:(BOOL)consumeTapEvents + fillColor:(NSInteger)fillColor + strokeColor:(NSInteger)strokeColor + visible:(BOOL)visible + strokeWidth:(NSInteger)strokeWidth + zIndex:(double)zIndex + center:(FGMPlatformLatLng *)center + radius:(double)radius + circleId:(NSString *)circleId; +@property(nonatomic, assign) BOOL consumeTapEvents; +@property(nonatomic, assign) NSInteger fillColor; +@property(nonatomic, assign) NSInteger strokeColor; +@property(nonatomic, assign) BOOL visible; +@property(nonatomic, assign) NSInteger strokeWidth; +@property(nonatomic, assign) double zIndex; +@property(nonatomic, strong) FGMPlatformLatLng *center; +@property(nonatomic, assign) double radius; +@property(nonatomic, copy) NSString *circleId; @end /// Pigeon equivalent of the Heatmap class. @@ -96,6 +234,18 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { @property(nonatomic, strong) id json; @end +/// Pigeon equivalent of the InfoWindow class. +@interface FGMPlatformInfoWindow : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithTitle:(nullable NSString *)title + snippet:(nullable NSString *)snippet + anchor:(FGMPlatformPoint *)anchor; +@property(nonatomic, copy, nullable) NSString *title; +@property(nonatomic, copy, nullable) NSString *snippet; +@property(nonatomic, strong) FGMPlatformPoint *anchor; +@end + /// Pigeon equivalent of Cluster. @interface FGMPlatformCluster : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -122,33 +272,95 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { @interface FGMPlatformMarker : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithJson:(id)json; -/// The marker data, as JSON. This should only be set from -/// Marker.toJson, and the native code must interpret it according to the -/// internal implementation details of that method. -@property(nonatomic, strong) id json; ++ (instancetype)makeWithAlpha:(double)alpha + anchor:(FGMPlatformPoint *)anchor + consumeTapEvents:(BOOL)consumeTapEvents + draggable:(BOOL)draggable + flat:(BOOL)flat + icon:(FGMPlatformBitmap *)icon + infoWindow:(FGMPlatformInfoWindow *)infoWindow + position:(FGMPlatformLatLng *)position + rotation:(double)rotation + visible:(BOOL)visible + zIndex:(double)zIndex + markerId:(NSString *)markerId + clusterManagerId:(nullable NSString *)clusterManagerId; +@property(nonatomic, assign) double alpha; +@property(nonatomic, strong) FGMPlatformPoint *anchor; +@property(nonatomic, assign) BOOL consumeTapEvents; +@property(nonatomic, assign) BOOL draggable; +@property(nonatomic, assign) BOOL flat; +@property(nonatomic, strong) FGMPlatformBitmap *icon; +@property(nonatomic, strong) FGMPlatformInfoWindow *infoWindow; +@property(nonatomic, strong) FGMPlatformLatLng *position; +@property(nonatomic, assign) double rotation; +@property(nonatomic, assign) BOOL visible; +@property(nonatomic, assign) double zIndex; +@property(nonatomic, copy) NSString *markerId; +@property(nonatomic, copy, nullable) NSString *clusterManagerId; @end /// Pigeon equivalent of the Polygon class. @interface FGMPlatformPolygon : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithJson:(id)json; -/// The polygon data, as JSON. This should only be set from -/// Polygon.toJson, and the native code must interpret it according to the -/// internal implementation details of that method. -@property(nonatomic, strong) id json; ++ (instancetype)makeWithPolygonId:(NSString *)polygonId + consumesTapEvents:(BOOL)consumesTapEvents + fillColor:(NSInteger)fillColor + geodesic:(BOOL)geodesic + points:(NSArray *)points + holes:(NSArray *> *)holes + visible:(BOOL)visible + strokeColor:(NSInteger)strokeColor + strokeWidth:(NSInteger)strokeWidth + zIndex:(NSInteger)zIndex; +@property(nonatomic, copy) NSString *polygonId; +@property(nonatomic, assign) BOOL consumesTapEvents; +@property(nonatomic, assign) NSInteger fillColor; +@property(nonatomic, assign) BOOL geodesic; +@property(nonatomic, copy) NSArray *points; +@property(nonatomic, copy) NSArray *> *holes; +@property(nonatomic, assign) BOOL visible; +@property(nonatomic, assign) NSInteger strokeColor; +@property(nonatomic, assign) NSInteger strokeWidth; +@property(nonatomic, assign) NSInteger zIndex; @end /// Pigeon equivalent of the Polyline class. @interface FGMPlatformPolyline : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithJson:(id)json; -/// The polyline data, as JSON. This should only be set from -/// Polyline.toJson, and the native code must interpret it according to the -/// internal implementation details of that method. -@property(nonatomic, strong) id json; ++ (instancetype)makeWithPolylineId:(NSString *)polylineId + consumesTapEvents:(BOOL)consumesTapEvents + color:(NSInteger)color + geodesic:(BOOL)geodesic + jointType:(FGMPlatformJointType)jointType + patterns:(NSArray *)patterns + points:(NSArray *)points + visible:(BOOL)visible + width:(NSInteger)width + zIndex:(NSInteger)zIndex; +@property(nonatomic, copy) NSString *polylineId; +@property(nonatomic, assign) BOOL consumesTapEvents; +@property(nonatomic, assign) NSInteger color; +@property(nonatomic, assign) BOOL geodesic; +/// The joint type. +@property(nonatomic, assign) FGMPlatformJointType jointType; +/// The pattern data, as a list of pattern items. +@property(nonatomic, copy) NSArray *patterns; +@property(nonatomic, copy) NSArray *points; +@property(nonatomic, assign) BOOL visible; +@property(nonatomic, assign) NSInteger width; +@property(nonatomic, assign) NSInteger zIndex; +@end + +/// Pigeon equivalent of the PatternItem class. +@interface FGMPlatformPatternItem : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithType:(FGMPlatformPatternItemType)type length:(nullable NSNumber *)length; +@property(nonatomic, assign) FGMPlatformPatternItemType type; +@property(nonatomic, strong, nullable) NSNumber *length; @end /// Pigeon equivalent of the Tile class. @@ -167,11 +379,18 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { @interface FGMPlatformTileOverlay : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithJson:(id)json; -/// The tile overlay data, as JSON. This should only be set from -/// TileOverlay.toJson, and the native code must interpret it according to the -/// internal implementation details of that method. -@property(nonatomic, strong) id json; ++ (instancetype)makeWithTileOverlayId:(NSString *)tileOverlayId + fadeIn:(BOOL)fadeIn + transparency:(double)transparency + zIndex:(NSInteger)zIndex + visible:(BOOL)visible + tileSize:(NSInteger)tileSize; +@property(nonatomic, copy) NSString *tileOverlayId; +@property(nonatomic, assign) BOOL fadeIn; +@property(nonatomic, assign) double transparency; +@property(nonatomic, assign) NSInteger zIndex; +@property(nonatomic, assign) BOOL visible; +@property(nonatomic, assign) NSInteger tileSize; @end /// Pigeon equivalent of Flutter's EdgeInsets. @@ -285,6 +504,15 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { @property(nonatomic, assign) double y; @end +/// Pigeon representation of a size. +@interface FGMPlatformSize : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithWidth:(double)width height:(double)height; +@property(nonatomic, assign) double width; +@property(nonatomic, assign) double height; +@end + /// Pigeon equivalent of GMSTileLayer properties. @interface FGMPlatformTileLayer : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -306,6 +534,98 @@ typedef NS_ENUM(NSUInteger, FGMPlatformMapType) { @property(nonatomic, strong, nullable) NSNumber *max; @end +/// Pigeon equivalent of [BitmapDescriptor]. As there are multiple disjoint +/// types of [BitmapDescriptor], [PlatformBitmap] contains a single field which +/// may hold the pigeon equivalent type of any of them. +@interface FGMPlatformBitmap : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithBitmap:(id)bitmap; +/// One of [PlatformBitmapAssetMap], [PlatformBitmapAsset], +/// [PlatformBitmapAssetImage], [PlatformBitmapBytesMap], +/// [PlatformBitmapBytes], or [PlatformBitmapDefaultMarker]. +/// As Pigeon does not currently support data class inheritance, this +/// approach allows for the different bitmap implementations to be valid +/// argument and return types of the API methods. See +/// https://github.com/flutter/flutter/issues/117819. +@property(nonatomic, strong) id bitmap; +@end + +/// Pigeon equivalent of [DefaultMarker]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#defaultMarker(float) +@interface FGMPlatformBitmapDefaultMarker : NSObject ++ (instancetype)makeWithHue:(nullable NSNumber *)hue; +@property(nonatomic, strong, nullable) NSNumber *hue; +@end + +/// Pigeon equivalent of [BytesBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#fromBitmap(android.graphics.Bitmap) +@interface FGMPlatformBitmapBytes : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithByteData:(FlutterStandardTypedData *)byteData + size:(nullable FGMPlatformSize *)size; +@property(nonatomic, strong) FlutterStandardTypedData *byteData; +@property(nonatomic, strong, nullable) FGMPlatformSize *size; +@end + +/// Pigeon equivalent of [AssetBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-fromasset-string-assetname +@interface FGMPlatformBitmapAsset : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithName:(NSString *)name pkg:(nullable NSString *)pkg; +@property(nonatomic, copy) NSString *name; +@property(nonatomic, copy, nullable) NSString *pkg; +@end + +/// Pigeon equivalent of [AssetImageBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-fromasset-string-assetname +@interface FGMPlatformBitmapAssetImage : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithName:(NSString *)name + scale:(double)scale + size:(nullable FGMPlatformSize *)size; +@property(nonatomic, copy) NSString *name; +@property(nonatomic, assign) double scale; +@property(nonatomic, strong, nullable) FGMPlatformSize *size; +@end + +/// Pigeon equivalent of [AssetMapBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-fromasset-string-assetname +@interface FGMPlatformBitmapAssetMap : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithAssetName:(NSString *)assetName + bitmapScaling:(FGMPlatformMapBitmapScaling)bitmapScaling + imagePixelRatio:(double)imagePixelRatio + width:(nullable NSNumber *)width + height:(nullable NSNumber *)height; +@property(nonatomic, copy) NSString *assetName; +@property(nonatomic, assign) FGMPlatformMapBitmapScaling bitmapScaling; +@property(nonatomic, assign) double imagePixelRatio; +@property(nonatomic, strong, nullable) NSNumber *width; +@property(nonatomic, strong, nullable) NSNumber *height; +@end + +/// Pigeon equivalent of [BytesMapBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-frombitmap-bitmap-image +@interface FGMPlatformBitmapBytesMap : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithByteData:(FlutterStandardTypedData *)byteData + bitmapScaling:(FGMPlatformMapBitmapScaling)bitmapScaling + imagePixelRatio:(double)imagePixelRatio + width:(nullable NSNumber *)width + height:(nullable NSNumber *)height; +@property(nonatomic, strong) FlutterStandardTypedData *byteData; +@property(nonatomic, assign) FGMPlatformMapBitmapScaling bitmapScaling; +@property(nonatomic, assign) double imagePixelRatio; +@property(nonatomic, strong, nullable) NSNumber *width; +@property(nonatomic, strong, nullable) NSNumber *height; +@end + /// The codec used by all APIs. NSObject *FGMGetMessagesCodec(void); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m index 73072d76dc70..5d3474cf79cc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" @@ -50,6 +50,39 @@ - (instancetype)initWithValue:(FGMPlatformMapType)value { } @end +/// Join types for polyline joints. +@implementation FGMPlatformJointTypeBox +- (instancetype)initWithValue:(FGMPlatformJointType)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +/// Enumeration of possible types for PatternItem. +@implementation FGMPlatformPatternItemTypeBox +- (instancetype)initWithValue:(FGMPlatformPatternItemType)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +/// Pigeon equivalent of [MapBitmapScaling]. +@implementation FGMPlatformMapBitmapScalingBox +- (instancetype)initWithValue:(FGMPlatformMapBitmapScaling)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + @interface FGMPlatformCameraPosition () + (FGMPlatformCameraPosition *)fromList:(NSArray *)list; + (nullable FGMPlatformCameraPosition *)nullableFromList:(NSArray *)list; @@ -62,6 +95,54 @@ + (nullable FGMPlatformCameraUpdate *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformCameraUpdateNewCameraPosition () ++ (FGMPlatformCameraUpdateNewCameraPosition *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateNewCameraPosition *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCameraUpdateNewLatLng () ++ (FGMPlatformCameraUpdateNewLatLng *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateNewLatLng *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCameraUpdateNewLatLngBounds () ++ (FGMPlatformCameraUpdateNewLatLngBounds *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateNewLatLngBounds *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCameraUpdateNewLatLngZoom () ++ (FGMPlatformCameraUpdateNewLatLngZoom *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateNewLatLngZoom *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCameraUpdateScrollBy () ++ (FGMPlatformCameraUpdateScrollBy *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateScrollBy *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCameraUpdateZoomBy () ++ (FGMPlatformCameraUpdateZoomBy *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateZoomBy *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCameraUpdateZoom () ++ (FGMPlatformCameraUpdateZoom *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateZoom *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCameraUpdateZoomTo () ++ (FGMPlatformCameraUpdateZoomTo *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdateZoomTo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformCircle () + (FGMPlatformCircle *)fromList:(NSArray *)list; + (nullable FGMPlatformCircle *)nullableFromList:(NSArray *)list; @@ -74,6 +155,12 @@ + (nullable FGMPlatformHeatmap *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformInfoWindow () ++ (FGMPlatformInfoWindow *)fromList:(NSArray *)list; ++ (nullable FGMPlatformInfoWindow *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformCluster () + (FGMPlatformCluster *)fromList:(NSArray *)list; + (nullable FGMPlatformCluster *)nullableFromList:(NSArray *)list; @@ -104,6 +191,12 @@ + (nullable FGMPlatformPolyline *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformPatternItem () ++ (FGMPlatformPatternItem *)fromList:(NSArray *)list; ++ (nullable FGMPlatformPatternItem *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformTile () + (FGMPlatformTile *)fromList:(NSArray *)list; + (nullable FGMPlatformTile *)nullableFromList:(NSArray *)list; @@ -158,6 +251,12 @@ + (nullable FGMPlatformPoint *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformSize () ++ (FGMPlatformSize *)fromList:(NSArray *)list; ++ (nullable FGMPlatformSize *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformTileLayer () + (FGMPlatformTileLayer *)fromList:(NSArray *)list; + (nullable FGMPlatformTileLayer *)nullableFromList:(NSArray *)list; @@ -170,6 +269,48 @@ + (nullable FGMPlatformZoomRange *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformBitmap () ++ (FGMPlatformBitmap *)fromList:(NSArray *)list; ++ (nullable FGMPlatformBitmap *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformBitmapDefaultMarker () ++ (FGMPlatformBitmapDefaultMarker *)fromList:(NSArray *)list; ++ (nullable FGMPlatformBitmapDefaultMarker *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformBitmapBytes () ++ (FGMPlatformBitmapBytes *)fromList:(NSArray *)list; ++ (nullable FGMPlatformBitmapBytes *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformBitmapAsset () ++ (FGMPlatformBitmapAsset *)fromList:(NSArray *)list; ++ (nullable FGMPlatformBitmapAsset *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformBitmapAssetImage () ++ (FGMPlatformBitmapAssetImage *)fromList:(NSArray *)list; ++ (nullable FGMPlatformBitmapAssetImage *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformBitmapAssetMap () ++ (FGMPlatformBitmapAssetMap *)fromList:(NSArray *)list; ++ (nullable FGMPlatformBitmapAssetMap *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformBitmapBytesMap () ++ (FGMPlatformBitmapBytesMap *)fromList:(NSArray *)list; ++ (nullable FGMPlatformBitmapBytesMap *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @implementation FGMPlatformCameraPosition + (instancetype)makeWithBearing:(double)bearing target:(FGMPlatformLatLng *)target @@ -204,14 +345,14 @@ + (nullable FGMPlatformCameraPosition *)nullableFromList:(NSArray *)list { @end @implementation FGMPlatformCameraUpdate -+ (instancetype)makeWithJson:(id)json { ++ (instancetype)makeWithCameraUpdate:(id)cameraUpdate { FGMPlatformCameraUpdate *pigeonResult = [[FGMPlatformCameraUpdate alloc] init]; - pigeonResult.json = json; + pigeonResult.cameraUpdate = cameraUpdate; return pigeonResult; } + (FGMPlatformCameraUpdate *)fromList:(NSArray *)list { FGMPlatformCameraUpdate *pigeonResult = [[FGMPlatformCameraUpdate alloc] init]; - pigeonResult.json = GetNullableObjectAtIndex(list, 0); + pigeonResult.cameraUpdate = GetNullableObjectAtIndex(list, 0); return pigeonResult; } + (nullable FGMPlatformCameraUpdate *)nullableFromList:(NSArray *)list { @@ -219,20 +360,230 @@ + (nullable FGMPlatformCameraUpdate *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - self.json ?: [NSNull null], + self.cameraUpdate ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformCameraUpdateNewCameraPosition ++ (instancetype)makeWithCameraPosition:(FGMPlatformCameraPosition *)cameraPosition { + FGMPlatformCameraUpdateNewCameraPosition *pigeonResult = + [[FGMPlatformCameraUpdateNewCameraPosition alloc] init]; + pigeonResult.cameraPosition = cameraPosition; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateNewCameraPosition *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateNewCameraPosition *pigeonResult = + [[FGMPlatformCameraUpdateNewCameraPosition alloc] init]; + pigeonResult.cameraPosition = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateNewCameraPosition *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateNewCameraPosition fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.cameraPosition ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformCameraUpdateNewLatLng ++ (instancetype)makeWithLatLng:(FGMPlatformLatLng *)latLng { + FGMPlatformCameraUpdateNewLatLng *pigeonResult = [[FGMPlatformCameraUpdateNewLatLng alloc] init]; + pigeonResult.latLng = latLng; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateNewLatLng *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateNewLatLng *pigeonResult = [[FGMPlatformCameraUpdateNewLatLng alloc] init]; + pigeonResult.latLng = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateNewLatLng *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateNewLatLng fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.latLng ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformCameraUpdateNewLatLngBounds ++ (instancetype)makeWithBounds:(FGMPlatformLatLngBounds *)bounds padding:(double)padding { + FGMPlatformCameraUpdateNewLatLngBounds *pigeonResult = + [[FGMPlatformCameraUpdateNewLatLngBounds alloc] init]; + pigeonResult.bounds = bounds; + pigeonResult.padding = padding; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateNewLatLngBounds *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateNewLatLngBounds *pigeonResult = + [[FGMPlatformCameraUpdateNewLatLngBounds alloc] init]; + pigeonResult.bounds = GetNullableObjectAtIndex(list, 0); + pigeonResult.padding = [GetNullableObjectAtIndex(list, 1) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateNewLatLngBounds *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateNewLatLngBounds fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.bounds ?: [NSNull null], + @(self.padding), + ]; +} +@end + +@implementation FGMPlatformCameraUpdateNewLatLngZoom ++ (instancetype)makeWithLatLng:(FGMPlatformLatLng *)latLng zoom:(double)zoom { + FGMPlatformCameraUpdateNewLatLngZoom *pigeonResult = + [[FGMPlatformCameraUpdateNewLatLngZoom alloc] init]; + pigeonResult.latLng = latLng; + pigeonResult.zoom = zoom; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateNewLatLngZoom *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateNewLatLngZoom *pigeonResult = + [[FGMPlatformCameraUpdateNewLatLngZoom alloc] init]; + pigeonResult.latLng = GetNullableObjectAtIndex(list, 0); + pigeonResult.zoom = [GetNullableObjectAtIndex(list, 1) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateNewLatLngZoom *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateNewLatLngZoom fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.latLng ?: [NSNull null], + @(self.zoom), + ]; +} +@end + +@implementation FGMPlatformCameraUpdateScrollBy ++ (instancetype)makeWithDx:(double)dx dy:(double)dy { + FGMPlatformCameraUpdateScrollBy *pigeonResult = [[FGMPlatformCameraUpdateScrollBy alloc] init]; + pigeonResult.dx = dx; + pigeonResult.dy = dy; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateScrollBy *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateScrollBy *pigeonResult = [[FGMPlatformCameraUpdateScrollBy alloc] init]; + pigeonResult.dx = [GetNullableObjectAtIndex(list, 0) doubleValue]; + pigeonResult.dy = [GetNullableObjectAtIndex(list, 1) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateScrollBy *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateScrollBy fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.dx), + @(self.dy), + ]; +} +@end + +@implementation FGMPlatformCameraUpdateZoomBy ++ (instancetype)makeWithAmount:(double)amount focus:(nullable FGMPlatformPoint *)focus { + FGMPlatformCameraUpdateZoomBy *pigeonResult = [[FGMPlatformCameraUpdateZoomBy alloc] init]; + pigeonResult.amount = amount; + pigeonResult.focus = focus; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateZoomBy *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateZoomBy *pigeonResult = [[FGMPlatformCameraUpdateZoomBy alloc] init]; + pigeonResult.amount = [GetNullableObjectAtIndex(list, 0) doubleValue]; + pigeonResult.focus = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateZoomBy *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateZoomBy fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.amount), + self.focus ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformCameraUpdateZoom ++ (instancetype)makeWithOut:(BOOL)out { + FGMPlatformCameraUpdateZoom *pigeonResult = [[FGMPlatformCameraUpdateZoom alloc] init]; + pigeonResult.out = out; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateZoom *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateZoom *pigeonResult = [[FGMPlatformCameraUpdateZoom alloc] init]; + pigeonResult.out = [GetNullableObjectAtIndex(list, 0) boolValue]; + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateZoom *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateZoom fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.out), + ]; +} +@end + +@implementation FGMPlatformCameraUpdateZoomTo ++ (instancetype)makeWithZoom:(double)zoom { + FGMPlatformCameraUpdateZoomTo *pigeonResult = [[FGMPlatformCameraUpdateZoomTo alloc] init]; + pigeonResult.zoom = zoom; + return pigeonResult; +} ++ (FGMPlatformCameraUpdateZoomTo *)fromList:(NSArray *)list { + FGMPlatformCameraUpdateZoomTo *pigeonResult = [[FGMPlatformCameraUpdateZoomTo alloc] init]; + pigeonResult.zoom = [GetNullableObjectAtIndex(list, 0) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdateZoomTo *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdateZoomTo fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.zoom), ]; } @end @implementation FGMPlatformCircle -+ (instancetype)makeWithJson:(id)json { ++ (instancetype)makeWithConsumeTapEvents:(BOOL)consumeTapEvents + fillColor:(NSInteger)fillColor + strokeColor:(NSInteger)strokeColor + visible:(BOOL)visible + strokeWidth:(NSInteger)strokeWidth + zIndex:(double)zIndex + center:(FGMPlatformLatLng *)center + radius:(double)radius + circleId:(NSString *)circleId { FGMPlatformCircle *pigeonResult = [[FGMPlatformCircle alloc] init]; - pigeonResult.json = json; + pigeonResult.consumeTapEvents = consumeTapEvents; + pigeonResult.fillColor = fillColor; + pigeonResult.strokeColor = strokeColor; + pigeonResult.visible = visible; + pigeonResult.strokeWidth = strokeWidth; + pigeonResult.zIndex = zIndex; + pigeonResult.center = center; + pigeonResult.radius = radius; + pigeonResult.circleId = circleId; return pigeonResult; } + (FGMPlatformCircle *)fromList:(NSArray *)list { FGMPlatformCircle *pigeonResult = [[FGMPlatformCircle alloc] init]; - pigeonResult.json = GetNullableObjectAtIndex(list, 0); + pigeonResult.consumeTapEvents = [GetNullableObjectAtIndex(list, 0) boolValue]; + pigeonResult.fillColor = [GetNullableObjectAtIndex(list, 1) integerValue]; + pigeonResult.strokeColor = [GetNullableObjectAtIndex(list, 2) integerValue]; + pigeonResult.visible = [GetNullableObjectAtIndex(list, 3) boolValue]; + pigeonResult.strokeWidth = [GetNullableObjectAtIndex(list, 4) integerValue]; + pigeonResult.zIndex = [GetNullableObjectAtIndex(list, 5) doubleValue]; + pigeonResult.center = GetNullableObjectAtIndex(list, 6); + pigeonResult.radius = [GetNullableObjectAtIndex(list, 7) doubleValue]; + pigeonResult.circleId = GetNullableObjectAtIndex(list, 8); return pigeonResult; } + (nullable FGMPlatformCircle *)nullableFromList:(NSArray *)list { @@ -240,7 +591,15 @@ + (nullable FGMPlatformCircle *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - self.json ?: [NSNull null], + @(self.consumeTapEvents), + @(self.fillColor), + @(self.strokeColor), + @(self.visible), + @(self.strokeWidth), + @(self.zIndex), + self.center ?: [NSNull null], + @(self.radius), + self.circleId ?: [NSNull null], ]; } @end @@ -266,6 +625,35 @@ + (nullable FGMPlatformHeatmap *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformInfoWindow ++ (instancetype)makeWithTitle:(nullable NSString *)title + snippet:(nullable NSString *)snippet + anchor:(FGMPlatformPoint *)anchor { + FGMPlatformInfoWindow *pigeonResult = [[FGMPlatformInfoWindow alloc] init]; + pigeonResult.title = title; + pigeonResult.snippet = snippet; + pigeonResult.anchor = anchor; + return pigeonResult; +} ++ (FGMPlatformInfoWindow *)fromList:(NSArray *)list { + FGMPlatformInfoWindow *pigeonResult = [[FGMPlatformInfoWindow alloc] init]; + pigeonResult.title = GetNullableObjectAtIndex(list, 0); + pigeonResult.snippet = GetNullableObjectAtIndex(list, 1); + pigeonResult.anchor = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable FGMPlatformInfoWindow *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformInfoWindow fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.title ?: [NSNull null], + self.snippet ?: [NSNull null], + self.anchor ?: [NSNull null], + ]; +} +@end + @implementation FGMPlatformCluster + (instancetype)makeWithClusterManagerId:(NSString *)clusterManagerId position:(FGMPlatformLatLng *)position @@ -321,14 +709,50 @@ + (nullable FGMPlatformClusterManager *)nullableFromList:(NSArray *)list { @end @implementation FGMPlatformMarker -+ (instancetype)makeWithJson:(id)json { ++ (instancetype)makeWithAlpha:(double)alpha + anchor:(FGMPlatformPoint *)anchor + consumeTapEvents:(BOOL)consumeTapEvents + draggable:(BOOL)draggable + flat:(BOOL)flat + icon:(FGMPlatformBitmap *)icon + infoWindow:(FGMPlatformInfoWindow *)infoWindow + position:(FGMPlatformLatLng *)position + rotation:(double)rotation + visible:(BOOL)visible + zIndex:(double)zIndex + markerId:(NSString *)markerId + clusterManagerId:(nullable NSString *)clusterManagerId { FGMPlatformMarker *pigeonResult = [[FGMPlatformMarker alloc] init]; - pigeonResult.json = json; + pigeonResult.alpha = alpha; + pigeonResult.anchor = anchor; + pigeonResult.consumeTapEvents = consumeTapEvents; + pigeonResult.draggable = draggable; + pigeonResult.flat = flat; + pigeonResult.icon = icon; + pigeonResult.infoWindow = infoWindow; + pigeonResult.position = position; + pigeonResult.rotation = rotation; + pigeonResult.visible = visible; + pigeonResult.zIndex = zIndex; + pigeonResult.markerId = markerId; + pigeonResult.clusterManagerId = clusterManagerId; return pigeonResult; } + (FGMPlatformMarker *)fromList:(NSArray *)list { FGMPlatformMarker *pigeonResult = [[FGMPlatformMarker alloc] init]; - pigeonResult.json = GetNullableObjectAtIndex(list, 0); + pigeonResult.alpha = [GetNullableObjectAtIndex(list, 0) doubleValue]; + pigeonResult.anchor = GetNullableObjectAtIndex(list, 1); + pigeonResult.consumeTapEvents = [GetNullableObjectAtIndex(list, 2) boolValue]; + pigeonResult.draggable = [GetNullableObjectAtIndex(list, 3) boolValue]; + pigeonResult.flat = [GetNullableObjectAtIndex(list, 4) boolValue]; + pigeonResult.icon = GetNullableObjectAtIndex(list, 5); + pigeonResult.infoWindow = GetNullableObjectAtIndex(list, 6); + pigeonResult.position = GetNullableObjectAtIndex(list, 7); + pigeonResult.rotation = [GetNullableObjectAtIndex(list, 8) doubleValue]; + pigeonResult.visible = [GetNullableObjectAtIndex(list, 9) boolValue]; + pigeonResult.zIndex = [GetNullableObjectAtIndex(list, 10) doubleValue]; + pigeonResult.markerId = GetNullableObjectAtIndex(list, 11); + pigeonResult.clusterManagerId = GetNullableObjectAtIndex(list, 12); return pigeonResult; } + (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list { @@ -336,20 +760,59 @@ + (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - self.json ?: [NSNull null], + @(self.alpha), + self.anchor ?: [NSNull null], + @(self.consumeTapEvents), + @(self.draggable), + @(self.flat), + self.icon ?: [NSNull null], + self.infoWindow ?: [NSNull null], + self.position ?: [NSNull null], + @(self.rotation), + @(self.visible), + @(self.zIndex), + self.markerId ?: [NSNull null], + self.clusterManagerId ?: [NSNull null], ]; } @end @implementation FGMPlatformPolygon -+ (instancetype)makeWithJson:(id)json { ++ (instancetype)makeWithPolygonId:(NSString *)polygonId + consumesTapEvents:(BOOL)consumesTapEvents + fillColor:(NSInteger)fillColor + geodesic:(BOOL)geodesic + points:(NSArray *)points + holes:(NSArray *> *)holes + visible:(BOOL)visible + strokeColor:(NSInteger)strokeColor + strokeWidth:(NSInteger)strokeWidth + zIndex:(NSInteger)zIndex { FGMPlatformPolygon *pigeonResult = [[FGMPlatformPolygon alloc] init]; - pigeonResult.json = json; + pigeonResult.polygonId = polygonId; + pigeonResult.consumesTapEvents = consumesTapEvents; + pigeonResult.fillColor = fillColor; + pigeonResult.geodesic = geodesic; + pigeonResult.points = points; + pigeonResult.holes = holes; + pigeonResult.visible = visible; + pigeonResult.strokeColor = strokeColor; + pigeonResult.strokeWidth = strokeWidth; + pigeonResult.zIndex = zIndex; return pigeonResult; } + (FGMPlatformPolygon *)fromList:(NSArray *)list { FGMPlatformPolygon *pigeonResult = [[FGMPlatformPolygon alloc] init]; - pigeonResult.json = GetNullableObjectAtIndex(list, 0); + pigeonResult.polygonId = GetNullableObjectAtIndex(list, 0); + pigeonResult.consumesTapEvents = [GetNullableObjectAtIndex(list, 1) boolValue]; + pigeonResult.fillColor = [GetNullableObjectAtIndex(list, 2) integerValue]; + pigeonResult.geodesic = [GetNullableObjectAtIndex(list, 3) boolValue]; + pigeonResult.points = GetNullableObjectAtIndex(list, 4); + pigeonResult.holes = GetNullableObjectAtIndex(list, 5); + pigeonResult.visible = [GetNullableObjectAtIndex(list, 6) boolValue]; + pigeonResult.strokeColor = [GetNullableObjectAtIndex(list, 7) integerValue]; + pigeonResult.strokeWidth = [GetNullableObjectAtIndex(list, 8) integerValue]; + pigeonResult.zIndex = [GetNullableObjectAtIndex(list, 9) integerValue]; return pigeonResult; } + (nullable FGMPlatformPolygon *)nullableFromList:(NSArray *)list { @@ -357,20 +820,57 @@ + (nullable FGMPlatformPolygon *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - self.json ?: [NSNull null], + self.polygonId ?: [NSNull null], + @(self.consumesTapEvents), + @(self.fillColor), + @(self.geodesic), + self.points ?: [NSNull null], + self.holes ?: [NSNull null], + @(self.visible), + @(self.strokeColor), + @(self.strokeWidth), + @(self.zIndex), ]; } @end @implementation FGMPlatformPolyline -+ (instancetype)makeWithJson:(id)json { ++ (instancetype)makeWithPolylineId:(NSString *)polylineId + consumesTapEvents:(BOOL)consumesTapEvents + color:(NSInteger)color + geodesic:(BOOL)geodesic + jointType:(FGMPlatformJointType)jointType + patterns:(NSArray *)patterns + points:(NSArray *)points + visible:(BOOL)visible + width:(NSInteger)width + zIndex:(NSInteger)zIndex { FGMPlatformPolyline *pigeonResult = [[FGMPlatformPolyline alloc] init]; - pigeonResult.json = json; + pigeonResult.polylineId = polylineId; + pigeonResult.consumesTapEvents = consumesTapEvents; + pigeonResult.color = color; + pigeonResult.geodesic = geodesic; + pigeonResult.jointType = jointType; + pigeonResult.patterns = patterns; + pigeonResult.points = points; + pigeonResult.visible = visible; + pigeonResult.width = width; + pigeonResult.zIndex = zIndex; return pigeonResult; } + (FGMPlatformPolyline *)fromList:(NSArray *)list { FGMPlatformPolyline *pigeonResult = [[FGMPlatformPolyline alloc] init]; - pigeonResult.json = GetNullableObjectAtIndex(list, 0); + pigeonResult.polylineId = GetNullableObjectAtIndex(list, 0); + pigeonResult.consumesTapEvents = [GetNullableObjectAtIndex(list, 1) boolValue]; + pigeonResult.color = [GetNullableObjectAtIndex(list, 2) integerValue]; + pigeonResult.geodesic = [GetNullableObjectAtIndex(list, 3) boolValue]; + FGMPlatformJointTypeBox *boxedFGMPlatformJointType = GetNullableObjectAtIndex(list, 4); + pigeonResult.jointType = boxedFGMPlatformJointType.value; + pigeonResult.patterns = GetNullableObjectAtIndex(list, 5); + pigeonResult.points = GetNullableObjectAtIndex(list, 6); + pigeonResult.visible = [GetNullableObjectAtIndex(list, 7) boolValue]; + pigeonResult.width = [GetNullableObjectAtIndex(list, 8) integerValue]; + pigeonResult.zIndex = [GetNullableObjectAtIndex(list, 9) integerValue]; return pigeonResult; } + (nullable FGMPlatformPolyline *)nullableFromList:(NSArray *)list { @@ -378,7 +878,42 @@ + (nullable FGMPlatformPolyline *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - self.json ?: [NSNull null], + self.polylineId ?: [NSNull null], + @(self.consumesTapEvents), + @(self.color), + @(self.geodesic), + [[FGMPlatformJointTypeBox alloc] initWithValue:self.jointType], + self.patterns ?: [NSNull null], + self.points ?: [NSNull null], + @(self.visible), + @(self.width), + @(self.zIndex), + ]; +} +@end + +@implementation FGMPlatformPatternItem ++ (instancetype)makeWithType:(FGMPlatformPatternItemType)type length:(nullable NSNumber *)length { + FGMPlatformPatternItem *pigeonResult = [[FGMPlatformPatternItem alloc] init]; + pigeonResult.type = type; + pigeonResult.length = length; + return pigeonResult; +} ++ (FGMPlatformPatternItem *)fromList:(NSArray *)list { + FGMPlatformPatternItem *pigeonResult = [[FGMPlatformPatternItem alloc] init]; + FGMPlatformPatternItemTypeBox *boxedFGMPlatformPatternItemType = + GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedFGMPlatformPatternItemType.value; + pigeonResult.length = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable FGMPlatformPatternItem *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformPatternItem fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + [[FGMPlatformPatternItemTypeBox alloc] initWithValue:self.type], + self.length ?: [NSNull null], ]; } @end @@ -413,14 +948,29 @@ + (nullable FGMPlatformTile *)nullableFromList:(NSArray *)list { @end @implementation FGMPlatformTileOverlay -+ (instancetype)makeWithJson:(id)json { ++ (instancetype)makeWithTileOverlayId:(NSString *)tileOverlayId + fadeIn:(BOOL)fadeIn + transparency:(double)transparency + zIndex:(NSInteger)zIndex + visible:(BOOL)visible + tileSize:(NSInteger)tileSize { FGMPlatformTileOverlay *pigeonResult = [[FGMPlatformTileOverlay alloc] init]; - pigeonResult.json = json; + pigeonResult.tileOverlayId = tileOverlayId; + pigeonResult.fadeIn = fadeIn; + pigeonResult.transparency = transparency; + pigeonResult.zIndex = zIndex; + pigeonResult.visible = visible; + pigeonResult.tileSize = tileSize; return pigeonResult; } + (FGMPlatformTileOverlay *)fromList:(NSArray *)list { FGMPlatformTileOverlay *pigeonResult = [[FGMPlatformTileOverlay alloc] init]; - pigeonResult.json = GetNullableObjectAtIndex(list, 0); + pigeonResult.tileOverlayId = GetNullableObjectAtIndex(list, 0); + pigeonResult.fadeIn = [GetNullableObjectAtIndex(list, 1) boolValue]; + pigeonResult.transparency = [GetNullableObjectAtIndex(list, 2) doubleValue]; + pigeonResult.zIndex = [GetNullableObjectAtIndex(list, 3) integerValue]; + pigeonResult.visible = [GetNullableObjectAtIndex(list, 4) boolValue]; + pigeonResult.tileSize = [GetNullableObjectAtIndex(list, 5) integerValue]; return pigeonResult; } + (nullable FGMPlatformTileOverlay *)nullableFromList:(NSArray *)list { @@ -428,7 +978,12 @@ + (nullable FGMPlatformTileOverlay *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - self.json ?: [NSNull null], + self.tileOverlayId ?: [NSNull null], + @(self.fadeIn), + @(self.transparency), + @(self.zIndex), + @(self.visible), + @(self.tileSize), ]; } @end @@ -699,6 +1254,30 @@ + (nullable FGMPlatformPoint *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformSize ++ (instancetype)makeWithWidth:(double)width height:(double)height { + FGMPlatformSize *pigeonResult = [[FGMPlatformSize alloc] init]; + pigeonResult.width = width; + pigeonResult.height = height; + return pigeonResult; +} ++ (FGMPlatformSize *)fromList:(NSArray *)list { + FGMPlatformSize *pigeonResult = [[FGMPlatformSize alloc] init]; + pigeonResult.width = [GetNullableObjectAtIndex(list, 0) doubleValue]; + pigeonResult.height = [GetNullableObjectAtIndex(list, 1) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformSize *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformSize fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.width), + @(self.height), + ]; +} +@end + @implementation FGMPlatformTileLayer + (instancetype)makeWithVisible:(BOOL)visible fadeIn:(BOOL)fadeIn @@ -756,6 +1335,204 @@ + (nullable FGMPlatformZoomRange *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformBitmap ++ (instancetype)makeWithBitmap:(id)bitmap { + FGMPlatformBitmap *pigeonResult = [[FGMPlatformBitmap alloc] init]; + pigeonResult.bitmap = bitmap; + return pigeonResult; +} ++ (FGMPlatformBitmap *)fromList:(NSArray *)list { + FGMPlatformBitmap *pigeonResult = [[FGMPlatformBitmap alloc] init]; + pigeonResult.bitmap = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformBitmap *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformBitmap fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.bitmap ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformBitmapDefaultMarker ++ (instancetype)makeWithHue:(nullable NSNumber *)hue { + FGMPlatformBitmapDefaultMarker *pigeonResult = [[FGMPlatformBitmapDefaultMarker alloc] init]; + pigeonResult.hue = hue; + return pigeonResult; +} ++ (FGMPlatformBitmapDefaultMarker *)fromList:(NSArray *)list { + FGMPlatformBitmapDefaultMarker *pigeonResult = [[FGMPlatformBitmapDefaultMarker alloc] init]; + pigeonResult.hue = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformBitmapDefaultMarker *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformBitmapDefaultMarker fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.hue ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformBitmapBytes ++ (instancetype)makeWithByteData:(FlutterStandardTypedData *)byteData + size:(nullable FGMPlatformSize *)size { + FGMPlatformBitmapBytes *pigeonResult = [[FGMPlatformBitmapBytes alloc] init]; + pigeonResult.byteData = byteData; + pigeonResult.size = size; + return pigeonResult; +} ++ (FGMPlatformBitmapBytes *)fromList:(NSArray *)list { + FGMPlatformBitmapBytes *pigeonResult = [[FGMPlatformBitmapBytes alloc] init]; + pigeonResult.byteData = GetNullableObjectAtIndex(list, 0); + pigeonResult.size = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable FGMPlatformBitmapBytes *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformBitmapBytes fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.byteData ?: [NSNull null], + self.size ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformBitmapAsset ++ (instancetype)makeWithName:(NSString *)name pkg:(nullable NSString *)pkg { + FGMPlatformBitmapAsset *pigeonResult = [[FGMPlatformBitmapAsset alloc] init]; + pigeonResult.name = name; + pigeonResult.pkg = pkg; + return pigeonResult; +} ++ (FGMPlatformBitmapAsset *)fromList:(NSArray *)list { + FGMPlatformBitmapAsset *pigeonResult = [[FGMPlatformBitmapAsset alloc] init]; + pigeonResult.name = GetNullableObjectAtIndex(list, 0); + pigeonResult.pkg = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable FGMPlatformBitmapAsset *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformBitmapAsset fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.name ?: [NSNull null], + self.pkg ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformBitmapAssetImage ++ (instancetype)makeWithName:(NSString *)name + scale:(double)scale + size:(nullable FGMPlatformSize *)size { + FGMPlatformBitmapAssetImage *pigeonResult = [[FGMPlatformBitmapAssetImage alloc] init]; + pigeonResult.name = name; + pigeonResult.scale = scale; + pigeonResult.size = size; + return pigeonResult; +} ++ (FGMPlatformBitmapAssetImage *)fromList:(NSArray *)list { + FGMPlatformBitmapAssetImage *pigeonResult = [[FGMPlatformBitmapAssetImage alloc] init]; + pigeonResult.name = GetNullableObjectAtIndex(list, 0); + pigeonResult.scale = [GetNullableObjectAtIndex(list, 1) doubleValue]; + pigeonResult.size = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable FGMPlatformBitmapAssetImage *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformBitmapAssetImage fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.name ?: [NSNull null], + @(self.scale), + self.size ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformBitmapAssetMap ++ (instancetype)makeWithAssetName:(NSString *)assetName + bitmapScaling:(FGMPlatformMapBitmapScaling)bitmapScaling + imagePixelRatio:(double)imagePixelRatio + width:(nullable NSNumber *)width + height:(nullable NSNumber *)height { + FGMPlatformBitmapAssetMap *pigeonResult = [[FGMPlatformBitmapAssetMap alloc] init]; + pigeonResult.assetName = assetName; + pigeonResult.bitmapScaling = bitmapScaling; + pigeonResult.imagePixelRatio = imagePixelRatio; + pigeonResult.width = width; + pigeonResult.height = height; + return pigeonResult; +} ++ (FGMPlatformBitmapAssetMap *)fromList:(NSArray *)list { + FGMPlatformBitmapAssetMap *pigeonResult = [[FGMPlatformBitmapAssetMap alloc] init]; + pigeonResult.assetName = GetNullableObjectAtIndex(list, 0); + FGMPlatformMapBitmapScalingBox *boxedFGMPlatformMapBitmapScaling = + GetNullableObjectAtIndex(list, 1); + pigeonResult.bitmapScaling = boxedFGMPlatformMapBitmapScaling.value; + pigeonResult.imagePixelRatio = [GetNullableObjectAtIndex(list, 2) doubleValue]; + pigeonResult.width = GetNullableObjectAtIndex(list, 3); + pigeonResult.height = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable FGMPlatformBitmapAssetMap *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformBitmapAssetMap fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.assetName ?: [NSNull null], + [[FGMPlatformMapBitmapScalingBox alloc] initWithValue:self.bitmapScaling], + @(self.imagePixelRatio), + self.width ?: [NSNull null], + self.height ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformBitmapBytesMap ++ (instancetype)makeWithByteData:(FlutterStandardTypedData *)byteData + bitmapScaling:(FGMPlatformMapBitmapScaling)bitmapScaling + imagePixelRatio:(double)imagePixelRatio + width:(nullable NSNumber *)width + height:(nullable NSNumber *)height { + FGMPlatformBitmapBytesMap *pigeonResult = [[FGMPlatformBitmapBytesMap alloc] init]; + pigeonResult.byteData = byteData; + pigeonResult.bitmapScaling = bitmapScaling; + pigeonResult.imagePixelRatio = imagePixelRatio; + pigeonResult.width = width; + pigeonResult.height = height; + return pigeonResult; +} ++ (FGMPlatformBitmapBytesMap *)fromList:(NSArray *)list { + FGMPlatformBitmapBytesMap *pigeonResult = [[FGMPlatformBitmapBytesMap alloc] init]; + pigeonResult.byteData = GetNullableObjectAtIndex(list, 0); + FGMPlatformMapBitmapScalingBox *boxedFGMPlatformMapBitmapScaling = + GetNullableObjectAtIndex(list, 1); + pigeonResult.bitmapScaling = boxedFGMPlatformMapBitmapScaling.value; + pigeonResult.imagePixelRatio = [GetNullableObjectAtIndex(list, 2) doubleValue]; + pigeonResult.width = GetNullableObjectAtIndex(list, 3); + pigeonResult.height = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable FGMPlatformBitmapBytesMap *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformBitmapBytesMap fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.byteData ?: [NSNull null], + [[FGMPlatformMapBitmapScalingBox alloc] initWithValue:self.bitmapScaling], + @(self.imagePixelRatio), + self.width ?: [NSNull null], + self.height ?: [NSNull null], + ]; +} +@end + @interface FGMMessagesPigeonCodecReader : FlutterStandardReader @end @implementation FGMMessagesPigeonCodecReader @@ -767,46 +1544,100 @@ - (nullable id)readValueOfType:(UInt8)type { ? nil : [[FGMPlatformMapTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; } - case 130: + case 130: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[FGMPlatformJointTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 131: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil + : [[FGMPlatformPatternItemTypeBox alloc] + initWithValue:[enumAsNumber integerValue]]; + } + case 132: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil + : [[FGMPlatformMapBitmapScalingBox alloc] + initWithValue:[enumAsNumber integerValue]]; + } + case 133: return [FGMPlatformCameraPosition fromList:[self readValue]]; - case 131: + case 134: return [FGMPlatformCameraUpdate fromList:[self readValue]]; - case 132: + case 135: + return [FGMPlatformCameraUpdateNewCameraPosition fromList:[self readValue]]; + case 136: + return [FGMPlatformCameraUpdateNewLatLng fromList:[self readValue]]; + case 137: + return [FGMPlatformCameraUpdateNewLatLngBounds fromList:[self readValue]]; + case 138: + return [FGMPlatformCameraUpdateNewLatLngZoom fromList:[self readValue]]; + case 139: + return [FGMPlatformCameraUpdateScrollBy fromList:[self readValue]]; + case 140: + return [FGMPlatformCameraUpdateZoomBy fromList:[self readValue]]; + case 141: + return [FGMPlatformCameraUpdateZoom fromList:[self readValue]]; + case 142: + return [FGMPlatformCameraUpdateZoomTo fromList:[self readValue]]; + case 143: return [FGMPlatformCircle fromList:[self readValue]]; - case 133: + case 144: return [FGMPlatformHeatmap fromList:[self readValue]]; - case 134: + case 145: + return [FGMPlatformInfoWindow fromList:[self readValue]]; + case 146: return [FGMPlatformCluster fromList:[self readValue]]; - case 135: + case 147: return [FGMPlatformClusterManager fromList:[self readValue]]; - case 136: + case 148: return [FGMPlatformMarker fromList:[self readValue]]; - case 137: + case 149: return [FGMPlatformPolygon fromList:[self readValue]]; - case 138: + case 150: return [FGMPlatformPolyline fromList:[self readValue]]; - case 139: + case 151: + return [FGMPlatformPatternItem fromList:[self readValue]]; + case 152: return [FGMPlatformTile fromList:[self readValue]]; - case 140: + case 153: return [FGMPlatformTileOverlay fromList:[self readValue]]; - case 141: + case 154: return [FGMPlatformEdgeInsets fromList:[self readValue]]; - case 142: + case 155: return [FGMPlatformLatLng fromList:[self readValue]]; - case 143: + case 156: return [FGMPlatformLatLngBounds fromList:[self readValue]]; - case 144: + case 157: return [FGMPlatformCameraTargetBounds fromList:[self readValue]]; - case 145: + case 158: return [FGMPlatformMapViewCreationParams fromList:[self readValue]]; - case 146: + case 159: return [FGMPlatformMapConfiguration fromList:[self readValue]]; - case 147: + case 160: return [FGMPlatformPoint fromList:[self readValue]]; - case 148: + case 161: + return [FGMPlatformSize fromList:[self readValue]]; + case 162: return [FGMPlatformTileLayer fromList:[self readValue]]; - case 149: + case 163: return [FGMPlatformZoomRange fromList:[self readValue]]; + case 164: + return [FGMPlatformBitmap fromList:[self readValue]]; + case 165: + return [FGMPlatformBitmapDefaultMarker fromList:[self readValue]]; + case 166: + return [FGMPlatformBitmapBytes fromList:[self readValue]]; + case 167: + return [FGMPlatformBitmapAsset fromList:[self readValue]]; + case 168: + return [FGMPlatformBitmapAssetImage fromList:[self readValue]]; + case 169: + return [FGMPlatformBitmapAssetMap fromList:[self readValue]]; + case 170: + return [FGMPlatformBitmapBytesMap fromList:[self readValue]]; default: return [super readValueOfType:type]; } @@ -821,65 +1652,131 @@ - (void)writeValue:(id)value { FGMPlatformMapTypeBox *box = (FGMPlatformMapTypeBox *)value; [self writeByte:129]; [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; - } else if ([value isKindOfClass:[FGMPlatformCameraPosition class]]) { + } else if ([value isKindOfClass:[FGMPlatformJointTypeBox class]]) { + FGMPlatformJointTypeBox *box = (FGMPlatformJointTypeBox *)value; [self writeByte:130]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[FGMPlatformPatternItemTypeBox class]]) { + FGMPlatformPatternItemTypeBox *box = (FGMPlatformPatternItemTypeBox *)value; + [self writeByte:131]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[FGMPlatformMapBitmapScalingBox class]]) { + FGMPlatformMapBitmapScalingBox *box = (FGMPlatformMapBitmapScalingBox *)value; + [self writeByte:132]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[FGMPlatformCameraPosition class]]) { + [self writeByte:133]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformCameraUpdate class]]) { - [self writeByte:131]; + [self writeByte:134]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateNewCameraPosition class]]) { + [self writeByte:135]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateNewLatLng class]]) { + [self writeByte:136]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateNewLatLngBounds class]]) { + [self writeByte:137]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateNewLatLngZoom class]]) { + [self writeByte:138]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateScrollBy class]]) { + [self writeByte:139]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateZoomBy class]]) { + [self writeByte:140]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateZoom class]]) { + [self writeByte:141]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformCameraUpdateZoomTo class]]) { + [self writeByte:142]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformCircle class]]) { - [self writeByte:132]; + [self writeByte:143]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformHeatmap class]]) { - [self writeByte:133]; + [self writeByte:144]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformInfoWindow class]]) { + [self writeByte:145]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformCluster class]]) { - [self writeByte:134]; + [self writeByte:146]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformClusterManager class]]) { - [self writeByte:135]; + [self writeByte:147]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { - [self writeByte:136]; + [self writeByte:148]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { - [self writeByte:137]; + [self writeByte:149]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { - [self writeByte:138]; + [self writeByte:150]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformPatternItem class]]) { + [self writeByte:151]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformTile class]]) { - [self writeByte:139]; + [self writeByte:152]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { - [self writeByte:140]; + [self writeByte:153]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformEdgeInsets class]]) { - [self writeByte:141]; + [self writeByte:154]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { - [self writeByte:142]; + [self writeByte:155]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { - [self writeByte:143]; + [self writeByte:156]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformCameraTargetBounds class]]) { - [self writeByte:144]; + [self writeByte:157]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformMapViewCreationParams class]]) { - [self writeByte:145]; + [self writeByte:158]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { - [self writeByte:146]; + [self writeByte:159]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { - [self writeByte:147]; + [self writeByte:160]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformSize class]]) { + [self writeByte:161]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { - [self writeByte:148]; + [self writeByte:162]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { - [self writeByte:149]; + [self writeByte:163]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmap class]]) { + [self writeByte:164]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmapDefaultMarker class]]) { + [self writeByte:165]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmapBytes class]]) { + [self writeByte:166]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmapAsset class]]) { + [self writeByte:167]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmapAssetImage class]]) { + [self writeByte:168]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmapAssetMap class]]) { + [self writeByte:169]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformBitmapBytesMap class]]) { + [self writeByte:170]; [self writeValue:[value toList]]; } else { [super writeValue:value]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index 9af2e061bde1..89c6b90ddc60 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -348,7 +348,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { required int mapId, }) { return _hostApi(mapId) - .animateCamera(PlatformCameraUpdate(json: cameraUpdate.toJson())); + .animateCamera(_platformCameraUpdateFromCameraUpdate(cameraUpdate)); } @override @@ -357,7 +357,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { required int mapId, }) { return _hostApi(mapId) - .moveCamera(PlatformCameraUpdate(json: cameraUpdate.toJson())); + .moveCamera(_platformCameraUpdateFromCameraUpdate(cameraUpdate)); } @override @@ -565,12 +565,8 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { /// Converts a Pigeon [PlatformCluster] to the corresponding [Cluster]. static Cluster clusterFromPlatformCluster(PlatformCluster cluster) { - return Cluster( - ClusterManagerId(cluster.clusterManagerId), - cluster.markerIds - // See comment in messages.dart for why the force unwrap is okay. - .map((String? markerId) => MarkerId(markerId!)) - .toList(), + return Cluster(ClusterManagerId(cluster.clusterManagerId), + cluster.markerIds.map((String markerId) => MarkerId(markerId)).toList(), position: _latLngFromPlatformLatLng(cluster.position), bounds: _latLngBoundsFromPlatformLatLngBounds(cluster.bounds)); } @@ -586,29 +582,108 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { x: coordinate.x.toDouble(), y: coordinate.y.toDouble()); } + static PlatformPoint _platformPointFromOffset(Offset offset) { + return PlatformPoint(x: offset.dx, y: offset.dy); + } + + static PlatformSize _platformSizeFromSize(Size size) { + return PlatformSize(width: size.width, height: size.height); + } + static PlatformCircle _platformCircleFromCircle(Circle circle) { - return PlatformCircle(json: circle.toJson()); + return PlatformCircle( + consumeTapEvents: circle.consumeTapEvents, + fillColor: circle.fillColor.value, + strokeColor: circle.strokeColor.value, + visible: circle.visible, + strokeWidth: circle.strokeWidth, + zIndex: circle.zIndex.toDouble(), + center: _platformLatLngFromLatLng(circle.center), + radius: circle.radius, + circleId: circle.circleId.value, + ); } static PlatformHeatmap _platformHeatmapFromHeatmap(Heatmap heatmap) { return PlatformHeatmap(json: serializeHeatmap(heatmap)); } + static PlatformInfoWindow _platformInfoWindowFromInfoWindow( + InfoWindow window) { + return PlatformInfoWindow( + title: window.title, + snippet: window.snippet, + anchor: _platformPointFromOffset(window.anchor)); + } + static PlatformMarker _platformMarkerFromMarker(Marker marker) { - return PlatformMarker(json: marker.toJson()); + return PlatformMarker( + alpha: marker.alpha, + anchor: _platformPointFromOffset(marker.anchor), + consumeTapEvents: marker.consumeTapEvents, + draggable: marker.draggable, + flat: marker.flat, + icon: platformBitmapFromBitmapDescriptor(marker.icon), + infoWindow: _platformInfoWindowFromInfoWindow(marker.infoWindow), + position: _platformLatLngFromLatLng(marker.position), + rotation: marker.rotation, + visible: marker.visible, + zIndex: marker.zIndex, + markerId: marker.markerId.value, + clusterManagerId: marker.clusterManagerId?.value, + ); } static PlatformPolygon _platformPolygonFromPolygon(Polygon polygon) { - return PlatformPolygon(json: polygon.toJson()); + final List points = + polygon.points.map(_platformLatLngFromLatLng).toList(); + final List> holes = + polygon.holes.map((List hole) { + return hole.map(_platformLatLngFromLatLng).toList(); + }).toList(); + return PlatformPolygon( + polygonId: polygon.polygonId.value, + fillColor: polygon.fillColor.value, + geodesic: polygon.geodesic, + consumesTapEvents: polygon.consumeTapEvents, + points: points, + holes: holes, + strokeColor: polygon.strokeColor.value, + strokeWidth: polygon.strokeWidth, + zIndex: polygon.zIndex, + visible: polygon.visible, + ); } static PlatformPolyline _platformPolylineFromPolyline(Polyline polyline) { - return PlatformPolyline(json: polyline.toJson()); + final List points = + polyline.points.map(_platformLatLngFromLatLng).toList(); + final List pattern = + polyline.patterns.map(platformPatternItemFromPatternItem).toList(); + return PlatformPolyline( + polylineId: polyline.polylineId.value, + consumesTapEvents: polyline.consumeTapEvents, + color: polyline.color.value, + geodesic: polyline.geodesic, + visible: polyline.visible, + width: polyline.width, + zIndex: polyline.zIndex, + points: points, + jointType: platformJointTypeFromJointType(polyline.jointType), + patterns: pattern, + ); } static PlatformTileOverlay _platformTileOverlayFromTileOverlay( TileOverlay tileOverlay) { - return PlatformTileOverlay(json: tileOverlay.toJson()); + return PlatformTileOverlay( + tileOverlayId: tileOverlay.tileOverlayId.value, + fadeIn: tileOverlay.fadeIn, + transparency: tileOverlay.transparency, + zIndex: tileOverlay.zIndex, + visible: tileOverlay.visible, + tileSize: tileOverlay.tileSize, + ); } static PlatformClusterManager _platformClusterManagerFromClusterManager( @@ -616,6 +691,131 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return PlatformClusterManager( identifier: clusterManager.clusterManagerId.value); } + + static PlatformCameraUpdate _platformCameraUpdateFromCameraUpdate( + CameraUpdate update) { + switch (update.updateType) { + case CameraUpdateType.newCameraPosition: + update as CameraUpdateNewCameraPosition; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateNewCameraPosition( + cameraPosition: _platformCameraPositionFromCameraPosition( + update.cameraPosition))); + case CameraUpdateType.newLatLng: + update as CameraUpdateNewLatLng; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateNewLatLng( + latLng: _platformLatLngFromLatLng(update.latLng))); + case CameraUpdateType.newLatLngZoom: + update as CameraUpdateNewLatLngZoom; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateNewLatLngZoom( + latLng: _platformLatLngFromLatLng(update.latLng), + zoom: update.zoom)); + case CameraUpdateType.newLatLngBounds: + update as CameraUpdateNewLatLngBounds; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateNewLatLngBounds( + bounds: _platformLatLngBoundsFromLatLngBounds(update.bounds)!, + padding: update.padding)); + case CameraUpdateType.zoomTo: + update as CameraUpdateZoomTo; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateZoomTo(zoom: update.zoom)); + case CameraUpdateType.zoomBy: + update as CameraUpdateZoomBy; + final Offset? focus = update.focus; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateZoomBy( + amount: update.amount, + focus: focus == null ? null : _platformPointFromOffset(focus))); + case CameraUpdateType.zoomIn: + update as CameraUpdateZoomIn; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateZoom(out: false)); + case CameraUpdateType.zoomOut: + update as CameraUpdateZoomOut; + return PlatformCameraUpdate( + cameraUpdate: PlatformCameraUpdateZoom(out: true)); + case CameraUpdateType.scrollBy: + update as CameraUpdateScrollBy; + return PlatformCameraUpdate( + cameraUpdate: + PlatformCameraUpdateScrollBy(dx: update.dx, dy: update.dy)); + } + } + + /// Converts [MapBitmapScaling] from platform interface to [PlatformMapBitmapScaling] Pigeon. + @visibleForTesting + static PlatformMapBitmapScaling platformMapBitmapScalingFromScaling( + MapBitmapScaling scaling) { + switch (scaling) { + case MapBitmapScaling.auto: + return PlatformMapBitmapScaling.auto; + case MapBitmapScaling.none: + return PlatformMapBitmapScaling.none; + } + + // The enum comes from a different package, which could get a new value at + // any time, so provide a fallback that ensures this won't break when used + // with a version that contains new values. This is deliberately outside + // the switch rather than a `default` so that the linter will flag the + // switch as needing an update. + // ignore: dead_code + return PlatformMapBitmapScaling.auto; + } + + /// Converts [BitmapDescriptor] from platform interface to [PlatformBitmap] pigeon. + @visibleForTesting + static PlatformBitmap platformBitmapFromBitmapDescriptor( + BitmapDescriptor bitmap) { + switch (bitmap) { + case final DefaultMarker marker: + return PlatformBitmap( + bitmap: PlatformBitmapDefaultMarker(hue: marker.hue?.toDouble())); + // Clients may still use this deprecated format, so it must be supported. + // ignore: deprecated_member_use + case final BytesBitmap bytes: + final Size? size = bytes.size; + return PlatformBitmap( + bitmap: PlatformBitmapBytes( + byteData: bytes.byteData, + size: (size == null) ? null : _platformSizeFromSize(size))); + case final AssetBitmap asset: + return PlatformBitmap( + bitmap: PlatformBitmapAsset(name: asset.name, pkg: asset.package)); + // Clients may still use this deprecated format, so it must be supported. + // ignore: deprecated_member_use + case final AssetImageBitmap asset: + final Size? size = asset.size; + return PlatformBitmap( + bitmap: PlatformBitmapAssetImage( + name: asset.name, + scale: asset.scale, + size: (size == null) ? null : _platformSizeFromSize(size))); + case final AssetMapBitmap asset: + return PlatformBitmap( + bitmap: PlatformBitmapAssetMap( + assetName: asset.assetName, + bitmapScaling: + platformMapBitmapScalingFromScaling(asset.bitmapScaling), + imagePixelRatio: asset.imagePixelRatio, + width: asset.width, + height: asset.height)); + case final BytesMapBitmap bytes: + return PlatformBitmap( + bitmap: PlatformBitmapBytesMap( + byteData: bytes.byteData, + bitmapScaling: + platformMapBitmapScalingFromScaling(bytes.bitmapScaling), + imagePixelRatio: bytes.imagePixelRatio, + width: bytes.width, + height: bytes.height)); + default: + throw ArgumentError( + 'Unrecognized type of bitmap ${bitmap.runtimeType}', 'bitmap'); + } + } } /// Callback handler for map events from the platform host. @@ -952,6 +1152,52 @@ PlatformZoomRange? _platformZoomRangeFromMinMaxZoomPreferenceJson( return PlatformZoomRange(min: minMaxZoom[0], max: minMaxZoom[1]); } +/// Converts platform interface's JointType to Pigeon's PlatformJointType. +@visibleForTesting +PlatformJointType platformJointTypeFromJointType(JointType jointType) { + switch (jointType) { + case JointType.mitered: + return PlatformJointType.mitered; + case JointType.bevel: + return PlatformJointType.bevel; + case JointType.round: + return PlatformJointType.round; + } + // The enum comes from a different package, which could get a new value at + // any time, so provide a fallback that ensures this won't break when used + // with a version that contains new values. This is deliberately outside + // the switch rather than a `default` so that the linter will flag the + // switch as needing an update. + // ignore: dead_code + return PlatformJointType.mitered; +} + +/// Converts a PatternItem to Pigeon's PlatformPatternItem for PlatformPolyline +/// pattern member. +@visibleForTesting +PlatformPatternItem platformPatternItemFromPatternItem(PatternItem item) { + switch (item.type) { + case PatternItemType.dot: + return PlatformPatternItem(type: PlatformPatternItemType.dot); + case PatternItemType.dash: + final double length = (item as VariableLengthPatternItem).length; + return PlatformPatternItem( + type: PlatformPatternItemType.dash, length: length); + case PatternItemType.gap: + final double length = (item as VariableLengthPatternItem).length; + return PlatformPatternItem( + type: PlatformPatternItemType.gap, length: length); + } + + // The enum comes from a different package, which could get a new value at + // any time, so provide a fallback that ensures this won't break when used + // with a version that contains new values. This is deliberately outside + // the switch rather than a `default` so that the linter will flag the + // switch as needing an update. + // ignore: dead_code + return PlatformPatternItem(type: PlatformPatternItemType.dot); +} + /// Update specification for a set of [TileOverlay]s. // TODO(stuartmorgan): Fix the missing export of this class in the platform // interface, and remove this copy. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart index 699d9e4e44af..30a3e4e138df 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -38,6 +38,26 @@ enum PlatformMapType { hybrid, } +/// Join types for polyline joints. +enum PlatformJointType { + mitered, + bevel, + round, +} + +/// Enumeration of possible types for PatternItem. +enum PlatformPatternItemType { + dot, + dash, + gap, +} + +/// Pigeon equivalent of [MapBitmapScaling]. +enum PlatformMapBitmapScaling { + auto, + none, +} + /// Pigeon representatation of a CameraPosition. class PlatformCameraPosition { PlatformCameraPosition({ @@ -78,24 +98,221 @@ class PlatformCameraPosition { /// Pigeon representation of a CameraUpdate. class PlatformCameraUpdate { PlatformCameraUpdate({ - required this.json, + required this.cameraUpdate, }); - /// The update data, as JSON. This should only be set from - /// CameraUpdate.toJson, and the native code must interpret it according to the - /// internal implementation details of the CameraUpdate class. - Object json; + /// This Object must be one of the classes below prefixed with + /// PlatformCameraUpdate. Each such class represents a different type of + /// camera update, and each holds a different set of data, preventing the + /// use of a single unified class. + Object cameraUpdate; Object encode() { return [ - json, + cameraUpdate, ]; } static PlatformCameraUpdate decode(Object result) { result as List; return PlatformCameraUpdate( - json: result[0]!, + cameraUpdate: result[0]!, + ); + } +} + +/// Pigeon equivalent of NewCameraPosition +class PlatformCameraUpdateNewCameraPosition { + PlatformCameraUpdateNewCameraPosition({ + required this.cameraPosition, + }); + + PlatformCameraPosition cameraPosition; + + Object encode() { + return [ + cameraPosition, + ]; + } + + static PlatformCameraUpdateNewCameraPosition decode(Object result) { + result as List; + return PlatformCameraUpdateNewCameraPosition( + cameraPosition: result[0]! as PlatformCameraPosition, + ); + } +} + +/// Pigeon equivalent of NewLatLng +class PlatformCameraUpdateNewLatLng { + PlatformCameraUpdateNewLatLng({ + required this.latLng, + }); + + PlatformLatLng latLng; + + Object encode() { + return [ + latLng, + ]; + } + + static PlatformCameraUpdateNewLatLng decode(Object result) { + result as List; + return PlatformCameraUpdateNewLatLng( + latLng: result[0]! as PlatformLatLng, + ); + } +} + +/// Pigeon equivalent of NewLatLngBounds +class PlatformCameraUpdateNewLatLngBounds { + PlatformCameraUpdateNewLatLngBounds({ + required this.bounds, + required this.padding, + }); + + PlatformLatLngBounds bounds; + + double padding; + + Object encode() { + return [ + bounds, + padding, + ]; + } + + static PlatformCameraUpdateNewLatLngBounds decode(Object result) { + result as List; + return PlatformCameraUpdateNewLatLngBounds( + bounds: result[0]! as PlatformLatLngBounds, + padding: result[1]! as double, + ); + } +} + +/// Pigeon equivalent of NewLatLngZoom +class PlatformCameraUpdateNewLatLngZoom { + PlatformCameraUpdateNewLatLngZoom({ + required this.latLng, + required this.zoom, + }); + + PlatformLatLng latLng; + + double zoom; + + Object encode() { + return [ + latLng, + zoom, + ]; + } + + static PlatformCameraUpdateNewLatLngZoom decode(Object result) { + result as List; + return PlatformCameraUpdateNewLatLngZoom( + latLng: result[0]! as PlatformLatLng, + zoom: result[1]! as double, + ); + } +} + +/// Pigeon equivalent of ScrollBy +class PlatformCameraUpdateScrollBy { + PlatformCameraUpdateScrollBy({ + required this.dx, + required this.dy, + }); + + double dx; + + double dy; + + Object encode() { + return [ + dx, + dy, + ]; + } + + static PlatformCameraUpdateScrollBy decode(Object result) { + result as List; + return PlatformCameraUpdateScrollBy( + dx: result[0]! as double, + dy: result[1]! as double, + ); + } +} + +/// Pigeon equivalent of ZoomBy +class PlatformCameraUpdateZoomBy { + PlatformCameraUpdateZoomBy({ + required this.amount, + this.focus, + }); + + double amount; + + PlatformPoint? focus; + + Object encode() { + return [ + amount, + focus, + ]; + } + + static PlatformCameraUpdateZoomBy decode(Object result) { + result as List; + return PlatformCameraUpdateZoomBy( + amount: result[0]! as double, + focus: result[1] as PlatformPoint?, + ); + } +} + +/// Pigeon equivalent of ZoomIn/ZoomOut +class PlatformCameraUpdateZoom { + PlatformCameraUpdateZoom({ + required this.out, + }); + + bool out; + + Object encode() { + return [ + out, + ]; + } + + static PlatformCameraUpdateZoom decode(Object result) { + result as List; + return PlatformCameraUpdateZoom( + out: result[0]! as bool, + ); + } +} + +/// Pigeon equivalent of ZoomTo +class PlatformCameraUpdateZoomTo { + PlatformCameraUpdateZoomTo({ + required this.zoom, + }); + + double zoom; + + Object encode() { + return [ + zoom, + ]; + } + + static PlatformCameraUpdateZoomTo decode(Object result) { + result as List; + return PlatformCameraUpdateZoomTo( + zoom: result[0]! as double, ); } } @@ -103,24 +320,61 @@ class PlatformCameraUpdate { /// Pigeon equivalent of the Circle class. class PlatformCircle { PlatformCircle({ - required this.json, + this.consumeTapEvents = false, + this.fillColor = 0x00000000, + this.strokeColor = 0xFF000000, + this.visible = true, + this.strokeWidth = 10, + this.zIndex = 0.0, + required this.center, + this.radius = 0, + required this.circleId, }); - /// The circle data, as JSON. This should only be set from - /// Circle.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - Object json; + bool consumeTapEvents; + + int fillColor; + + int strokeColor; + + bool visible; + + int strokeWidth; + + double zIndex; + + PlatformLatLng center; + + double radius; + + String circleId; Object encode() { return [ - json, + consumeTapEvents, + fillColor, + strokeColor, + visible, + strokeWidth, + zIndex, + center, + radius, + circleId, ]; } static PlatformCircle decode(Object result) { result as List; return PlatformCircle( - json: result[0]!, + consumeTapEvents: result[0]! as bool, + fillColor: result[1]! as int, + strokeColor: result[2]! as int, + visible: result[3]! as bool, + strokeWidth: result[4]! as int, + zIndex: result[5]! as double, + center: result[6]! as PlatformLatLng, + radius: result[7]! as double, + circleId: result[8]! as String, ); } } @@ -150,6 +404,38 @@ class PlatformHeatmap { } } +/// Pigeon equivalent of the InfoWindow class. +class PlatformInfoWindow { + PlatformInfoWindow({ + this.title, + this.snippet, + required this.anchor, + }); + + String? title; + + String? snippet; + + PlatformPoint anchor; + + Object encode() { + return [ + title, + snippet, + anchor, + ]; + } + + static PlatformInfoWindow decode(Object result) { + result as List; + return PlatformInfoWindow( + title: result[0] as String?, + snippet: result[1] as String?, + anchor: result[2]! as PlatformPoint, + ); + } +} + /// Pigeon equivalent of Cluster. class PlatformCluster { PlatformCluster({ @@ -212,24 +498,81 @@ class PlatformClusterManager { /// Pigeon equivalent of the Marker class. class PlatformMarker { PlatformMarker({ - required this.json, + this.alpha = 1.0, + required this.anchor, + this.consumeTapEvents = false, + this.draggable = false, + this.flat = false, + required this.icon, + required this.infoWindow, + required this.position, + this.rotation = 0.0, + this.visible = true, + this.zIndex = 0.0, + required this.markerId, + this.clusterManagerId, }); - /// The marker data, as JSON. This should only be set from - /// Marker.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - Object json; + double alpha; + + PlatformPoint anchor; + + bool consumeTapEvents; + + bool draggable; + + bool flat; + + PlatformBitmap icon; + + PlatformInfoWindow infoWindow; + + PlatformLatLng position; + + double rotation; + + bool visible; + + double zIndex; + + String markerId; + + String? clusterManagerId; Object encode() { return [ - json, + alpha, + anchor, + consumeTapEvents, + draggable, + flat, + icon, + infoWindow, + position, + rotation, + visible, + zIndex, + markerId, + clusterManagerId, ]; } static PlatformMarker decode(Object result) { result as List; return PlatformMarker( - json: result[0]!, + alpha: result[0]! as double, + anchor: result[1]! as PlatformPoint, + consumeTapEvents: result[2]! as bool, + draggable: result[3]! as bool, + flat: result[4]! as bool, + icon: result[5]! as PlatformBitmap, + infoWindow: result[6]! as PlatformInfoWindow, + position: result[7]! as PlatformLatLng, + rotation: result[8]! as double, + visible: result[9]! as bool, + zIndex: result[10]! as double, + markerId: result[11]! as String, + clusterManagerId: result[12] as String?, ); } } @@ -237,24 +580,66 @@ class PlatformMarker { /// Pigeon equivalent of the Polygon class. class PlatformPolygon { PlatformPolygon({ - required this.json, + required this.polygonId, + required this.consumesTapEvents, + required this.fillColor, + required this.geodesic, + required this.points, + required this.holes, + required this.visible, + required this.strokeColor, + required this.strokeWidth, + required this.zIndex, }); - /// The polygon data, as JSON. This should only be set from - /// Polygon.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - Object json; + String polygonId; + + bool consumesTapEvents; + + int fillColor; + + bool geodesic; + + List points; + + List> holes; + + bool visible; + + int strokeColor; + + int strokeWidth; + + int zIndex; Object encode() { return [ - json, + polygonId, + consumesTapEvents, + fillColor, + geodesic, + points, + holes, + visible, + strokeColor, + strokeWidth, + zIndex, ]; } static PlatformPolygon decode(Object result) { result as List; return PlatformPolygon( - json: result[0]!, + polygonId: result[0]! as String, + consumesTapEvents: result[1]! as bool, + fillColor: result[2]! as int, + geodesic: result[3]! as bool, + points: (result[4] as List?)!.cast(), + holes: (result[5] as List?)!.cast>(), + visible: result[6]! as bool, + strokeColor: result[7]! as int, + strokeWidth: result[8]! as int, + zIndex: result[9]! as int, ); } } @@ -262,24 +647,95 @@ class PlatformPolygon { /// Pigeon equivalent of the Polyline class. class PlatformPolyline { PlatformPolyline({ - required this.json, + required this.polylineId, + required this.consumesTapEvents, + required this.color, + required this.geodesic, + required this.jointType, + required this.patterns, + required this.points, + required this.visible, + required this.width, + required this.zIndex, }); - /// The polyline data, as JSON. This should only be set from - /// Polyline.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - Object json; + String polylineId; + + bool consumesTapEvents; + + int color; + + bool geodesic; + + /// The joint type. + PlatformJointType jointType; + + /// The pattern data, as a list of pattern items. + List patterns; + + List points; + + bool visible; + + int width; + + int zIndex; Object encode() { return [ - json, + polylineId, + consumesTapEvents, + color, + geodesic, + jointType, + patterns, + points, + visible, + width, + zIndex, ]; } static PlatformPolyline decode(Object result) { result as List; return PlatformPolyline( - json: result[0]!, + polylineId: result[0]! as String, + consumesTapEvents: result[1]! as bool, + color: result[2]! as int, + geodesic: result[3]! as bool, + jointType: result[4]! as PlatformJointType, + patterns: (result[5] as List?)!.cast(), + points: (result[6] as List?)!.cast(), + visible: result[7]! as bool, + width: result[8]! as int, + zIndex: result[9]! as int, + ); + } +} + +/// Pigeon equivalent of the PatternItem class. +class PlatformPatternItem { + PlatformPatternItem({ + required this.type, + this.length, + }); + + PlatformPatternItemType type; + + double? length; + + Object encode() { + return [ + type, + length, + ]; + } + + static PlatformPatternItem decode(Object result) { + result as List; + return PlatformPatternItem( + type: result[0]! as PlatformPatternItemType, + length: result[1] as double?, ); } } @@ -319,24 +775,46 @@ class PlatformTile { /// Pigeon equivalent of the TileOverlay class. class PlatformTileOverlay { PlatformTileOverlay({ - required this.json, + required this.tileOverlayId, + required this.fadeIn, + required this.transparency, + required this.zIndex, + required this.visible, + required this.tileSize, }); - /// The tile overlay data, as JSON. This should only be set from - /// TileOverlay.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - Object json; + String tileOverlayId; + + bool fadeIn; + + double transparency; + + int zIndex; + + bool visible; + + int tileSize; Object encode() { return [ - json, + tileOverlayId, + fadeIn, + transparency, + zIndex, + visible, + tileSize, ]; } static PlatformTileOverlay decode(Object result) { result as List; return PlatformTileOverlay( - json: result[0]!, + tileOverlayId: result[0]! as String, + fadeIn: result[1]! as bool, + transparency: result[2]! as double, + zIndex: result[3]! as int, + visible: result[4]! as bool, + tileSize: result[5]! as int, ); } } @@ -650,6 +1128,33 @@ class PlatformPoint { } } +/// Pigeon representation of a size. +class PlatformSize { + PlatformSize({ + required this.width, + required this.height, + }); + + double width; + + double height; + + Object encode() { + return [ + width, + height, + ]; + } + + static PlatformSize decode(Object result) { + result as List; + return PlatformSize( + width: result[0]! as double, + height: result[1]! as double, + ); + } +} + /// Pigeon equivalent of GMSTileLayer properties. class PlatformTileLayer { PlatformTileLayer({ @@ -714,6 +1219,235 @@ class PlatformZoomRange { } } +/// Pigeon equivalent of [BitmapDescriptor]. As there are multiple disjoint +/// types of [BitmapDescriptor], [PlatformBitmap] contains a single field which +/// may hold the pigeon equivalent type of any of them. +class PlatformBitmap { + PlatformBitmap({ + required this.bitmap, + }); + + /// One of [PlatformBitmapAssetMap], [PlatformBitmapAsset], + /// [PlatformBitmapAssetImage], [PlatformBitmapBytesMap], + /// [PlatformBitmapBytes], or [PlatformBitmapDefaultMarker]. + /// As Pigeon does not currently support data class inheritance, this + /// approach allows for the different bitmap implementations to be valid + /// argument and return types of the API methods. See + /// https://github.com/flutter/flutter/issues/117819. + Object bitmap; + + Object encode() { + return [ + bitmap, + ]; + } + + static PlatformBitmap decode(Object result) { + result as List; + return PlatformBitmap( + bitmap: result[0]!, + ); + } +} + +/// Pigeon equivalent of [DefaultMarker]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#defaultMarker(float) +class PlatformBitmapDefaultMarker { + PlatformBitmapDefaultMarker({ + this.hue, + }); + + double? hue; + + Object encode() { + return [ + hue, + ]; + } + + static PlatformBitmapDefaultMarker decode(Object result) { + result as List; + return PlatformBitmapDefaultMarker( + hue: result[0] as double?, + ); + } +} + +/// Pigeon equivalent of [BytesBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#fromBitmap(android.graphics.Bitmap) +class PlatformBitmapBytes { + PlatformBitmapBytes({ + required this.byteData, + this.size, + }); + + Uint8List byteData; + + PlatformSize? size; + + Object encode() { + return [ + byteData, + size, + ]; + } + + static PlatformBitmapBytes decode(Object result) { + result as List; + return PlatformBitmapBytes( + byteData: result[0]! as Uint8List, + size: result[1] as PlatformSize?, + ); + } +} + +/// Pigeon equivalent of [AssetBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-fromasset-string-assetname +class PlatformBitmapAsset { + PlatformBitmapAsset({ + required this.name, + this.pkg, + }); + + String name; + + String? pkg; + + Object encode() { + return [ + name, + pkg, + ]; + } + + static PlatformBitmapAsset decode(Object result) { + result as List; + return PlatformBitmapAsset( + name: result[0]! as String, + pkg: result[1] as String?, + ); + } +} + +/// Pigeon equivalent of [AssetImageBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-fromasset-string-assetname +class PlatformBitmapAssetImage { + PlatformBitmapAssetImage({ + required this.name, + required this.scale, + this.size, + }); + + String name; + + double scale; + + PlatformSize? size; + + Object encode() { + return [ + name, + scale, + size, + ]; + } + + static PlatformBitmapAssetImage decode(Object result) { + result as List; + return PlatformBitmapAssetImage( + name: result[0]! as String, + scale: result[1]! as double, + size: result[2] as PlatformSize?, + ); + } +} + +/// Pigeon equivalent of [AssetMapBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-fromasset-string-assetname +class PlatformBitmapAssetMap { + PlatformBitmapAssetMap({ + required this.assetName, + required this.bitmapScaling, + required this.imagePixelRatio, + this.width, + this.height, + }); + + String assetName; + + PlatformMapBitmapScaling bitmapScaling; + + double imagePixelRatio; + + double? width; + + double? height; + + Object encode() { + return [ + assetName, + bitmapScaling, + imagePixelRatio, + width, + height, + ]; + } + + static PlatformBitmapAssetMap decode(Object result) { + result as List; + return PlatformBitmapAssetMap( + assetName: result[0]! as String, + bitmapScaling: result[1]! as PlatformMapBitmapScaling, + imagePixelRatio: result[2]! as double, + width: result[3] as double?, + height: result[4] as double?, + ); + } +} + +/// Pigeon equivalent of [BytesMapBitmap]. See +/// https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/model/BitmapDescriptorFactory#public-static-bitmapdescriptor-frombitmap-bitmap-image +class PlatformBitmapBytesMap { + PlatformBitmapBytesMap({ + required this.byteData, + required this.bitmapScaling, + required this.imagePixelRatio, + this.width, + this.height, + }); + + Uint8List byteData; + + PlatformMapBitmapScaling bitmapScaling; + + double imagePixelRatio; + + double? width; + + double? height; + + Object encode() { + return [ + byteData, + bitmapScaling, + imagePixelRatio, + width, + height, + ]; + } + + static PlatformBitmapBytesMap decode(Object result) { + result as List; + return PlatformBitmapBytesMap( + byteData: result[0]! as Uint8List, + bitmapScaling: result[1]! as PlatformMapBitmapScaling, + imagePixelRatio: result[2]! as double, + width: result[3] as double?, + height: result[4] as double?, + ); + } +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -724,65 +1458,128 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is PlatformMapType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is PlatformCameraPosition) { + } else if (value is PlatformJointType) { buffer.putUint8(130); + writeValue(buffer, value.index); + } else if (value is PlatformPatternItemType) { + buffer.putUint8(131); + writeValue(buffer, value.index); + } else if (value is PlatformMapBitmapScaling) { + buffer.putUint8(132); + writeValue(buffer, value.index); + } else if (value is PlatformCameraPosition) { + buffer.putUint8(133); writeValue(buffer, value.encode()); } else if (value is PlatformCameraUpdate) { - buffer.putUint8(131); + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateNewCameraPosition) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateNewLatLng) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateNewLatLngBounds) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateNewLatLngZoom) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateScrollBy) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateZoomBy) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateZoom) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else if (value is PlatformCameraUpdateZoomTo) { + buffer.putUint8(142); writeValue(buffer, value.encode()); } else if (value is PlatformCircle) { - buffer.putUint8(132); + buffer.putUint8(143); writeValue(buffer, value.encode()); } else if (value is PlatformHeatmap) { - buffer.putUint8(133); + buffer.putUint8(144); + writeValue(buffer, value.encode()); + } else if (value is PlatformInfoWindow) { + buffer.putUint8(145); writeValue(buffer, value.encode()); } else if (value is PlatformCluster) { - buffer.putUint8(134); + buffer.putUint8(146); writeValue(buffer, value.encode()); } else if (value is PlatformClusterManager) { - buffer.putUint8(135); + buffer.putUint8(147); writeValue(buffer, value.encode()); } else if (value is PlatformMarker) { - buffer.putUint8(136); + buffer.putUint8(148); writeValue(buffer, value.encode()); } else if (value is PlatformPolygon) { - buffer.putUint8(137); + buffer.putUint8(149); writeValue(buffer, value.encode()); } else if (value is PlatformPolyline) { - buffer.putUint8(138); + buffer.putUint8(150); + writeValue(buffer, value.encode()); + } else if (value is PlatformPatternItem) { + buffer.putUint8(151); writeValue(buffer, value.encode()); } else if (value is PlatformTile) { - buffer.putUint8(139); + buffer.putUint8(152); writeValue(buffer, value.encode()); } else if (value is PlatformTileOverlay) { - buffer.putUint8(140); + buffer.putUint8(153); writeValue(buffer, value.encode()); } else if (value is PlatformEdgeInsets) { - buffer.putUint8(141); + buffer.putUint8(154); writeValue(buffer, value.encode()); } else if (value is PlatformLatLng) { - buffer.putUint8(142); + buffer.putUint8(155); writeValue(buffer, value.encode()); } else if (value is PlatformLatLngBounds) { - buffer.putUint8(143); + buffer.putUint8(156); writeValue(buffer, value.encode()); } else if (value is PlatformCameraTargetBounds) { - buffer.putUint8(144); + buffer.putUint8(157); writeValue(buffer, value.encode()); } else if (value is PlatformMapViewCreationParams) { - buffer.putUint8(145); + buffer.putUint8(158); writeValue(buffer, value.encode()); } else if (value is PlatformMapConfiguration) { - buffer.putUint8(146); + buffer.putUint8(159); writeValue(buffer, value.encode()); } else if (value is PlatformPoint) { - buffer.putUint8(147); + buffer.putUint8(160); + writeValue(buffer, value.encode()); + } else if (value is PlatformSize) { + buffer.putUint8(161); writeValue(buffer, value.encode()); } else if (value is PlatformTileLayer) { - buffer.putUint8(148); + buffer.putUint8(162); writeValue(buffer, value.encode()); } else if (value is PlatformZoomRange) { - buffer.putUint8(149); + buffer.putUint8(163); + writeValue(buffer, value.encode()); + } else if (value is PlatformBitmap) { + buffer.putUint8(164); + writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapDefaultMarker) { + buffer.putUint8(165); + writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapBytes) { + buffer.putUint8(166); + writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapAsset) { + buffer.putUint8(167); + writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapAssetImage) { + buffer.putUint8(168); + writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapAssetMap) { + buffer.putUint8(169); + writeValue(buffer, value.encode()); + } else if (value is PlatformBitmapBytesMap) { + buffer.putUint8(170); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -796,45 +1593,90 @@ class _PigeonCodec extends StandardMessageCodec { final int? value = readValue(buffer) as int?; return value == null ? null : PlatformMapType.values[value]; case 130: - return PlatformCameraPosition.decode(readValue(buffer)!); + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformJointType.values[value]; case 131: - return PlatformCameraUpdate.decode(readValue(buffer)!); + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformPatternItemType.values[value]; case 132: - return PlatformCircle.decode(readValue(buffer)!); + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformMapBitmapScaling.values[value]; case 133: - return PlatformHeatmap.decode(readValue(buffer)!); + return PlatformCameraPosition.decode(readValue(buffer)!); case 134: - return PlatformCluster.decode(readValue(buffer)!); + return PlatformCameraUpdate.decode(readValue(buffer)!); case 135: - return PlatformClusterManager.decode(readValue(buffer)!); + return PlatformCameraUpdateNewCameraPosition.decode(readValue(buffer)!); case 136: - return PlatformMarker.decode(readValue(buffer)!); + return PlatformCameraUpdateNewLatLng.decode(readValue(buffer)!); case 137: - return PlatformPolygon.decode(readValue(buffer)!); + return PlatformCameraUpdateNewLatLngBounds.decode(readValue(buffer)!); case 138: - return PlatformPolyline.decode(readValue(buffer)!); + return PlatformCameraUpdateNewLatLngZoom.decode(readValue(buffer)!); case 139: - return PlatformTile.decode(readValue(buffer)!); + return PlatformCameraUpdateScrollBy.decode(readValue(buffer)!); case 140: - return PlatformTileOverlay.decode(readValue(buffer)!); + return PlatformCameraUpdateZoomBy.decode(readValue(buffer)!); case 141: - return PlatformEdgeInsets.decode(readValue(buffer)!); + return PlatformCameraUpdateZoom.decode(readValue(buffer)!); case 142: - return PlatformLatLng.decode(readValue(buffer)!); + return PlatformCameraUpdateZoomTo.decode(readValue(buffer)!); case 143: - return PlatformLatLngBounds.decode(readValue(buffer)!); + return PlatformCircle.decode(readValue(buffer)!); case 144: - return PlatformCameraTargetBounds.decode(readValue(buffer)!); + return PlatformHeatmap.decode(readValue(buffer)!); case 145: - return PlatformMapViewCreationParams.decode(readValue(buffer)!); + return PlatformInfoWindow.decode(readValue(buffer)!); case 146: - return PlatformMapConfiguration.decode(readValue(buffer)!); + return PlatformCluster.decode(readValue(buffer)!); case 147: - return PlatformPoint.decode(readValue(buffer)!); + return PlatformClusterManager.decode(readValue(buffer)!); case 148: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformMarker.decode(readValue(buffer)!); case 149: + return PlatformPolygon.decode(readValue(buffer)!); + case 150: + return PlatformPolyline.decode(readValue(buffer)!); + case 151: + return PlatformPatternItem.decode(readValue(buffer)!); + case 152: + return PlatformTile.decode(readValue(buffer)!); + case 153: + return PlatformTileOverlay.decode(readValue(buffer)!); + case 154: + return PlatformEdgeInsets.decode(readValue(buffer)!); + case 155: + return PlatformLatLng.decode(readValue(buffer)!); + case 156: + return PlatformLatLngBounds.decode(readValue(buffer)!); + case 157: + return PlatformCameraTargetBounds.decode(readValue(buffer)!); + case 158: + return PlatformMapViewCreationParams.decode(readValue(buffer)!); + case 159: + return PlatformMapConfiguration.decode(readValue(buffer)!); + case 160: + return PlatformPoint.decode(readValue(buffer)!); + case 161: + return PlatformSize.decode(readValue(buffer)!); + case 162: + return PlatformTileLayer.decode(readValue(buffer)!); + case 163: return PlatformZoomRange.decode(readValue(buffer)!); + case 164: + return PlatformBitmap.decode(readValue(buffer)!); + case 165: + return PlatformBitmapDefaultMarker.decode(readValue(buffer)!); + case 166: + return PlatformBitmapBytes.decode(readValue(buffer)!); + case 167: + return PlatformBitmapAsset.decode(readValue(buffer)!); + case 168: + return PlatformBitmapAssetImage.decode(readValue(buffer)!); + case 169: + return PlatformBitmapAssetMap.decode(readValue(buffer)!); + case 170: + return PlatformBitmapBytesMap.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart index 8cc612c684c7..342bd9ea8019 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart @@ -38,29 +38,92 @@ class PlatformCameraPosition { /// Pigeon representation of a CameraUpdate. class PlatformCameraUpdate { - PlatformCameraUpdate(this.json); - - /// The update data, as JSON. This should only be set from - /// CameraUpdate.toJson, and the native code must interpret it according to the - /// internal implementation details of the CameraUpdate class. - // TODO(stuartmorgan): Update the google_maps_platform_interface CameraUpdate - // class to provide a structured representation of an update. Currently it - // uses JSON as its only state, so there is no way to preserve structure. - // This wrapper class exists as a placeholder for now to at least provide - // type safety in the top-level call's arguments. - final Object json; + PlatformCameraUpdate(this.cameraUpdate); + + /// This Object must be one of the classes below prefixed with + /// PlatformCameraUpdate. Each such class represents a different type of + /// camera update, and each holds a different set of data, preventing the + /// use of a single unified class. + // Pigeon does not support inheritance, which prevents a more strict type + // bound. See https://github.com/flutter/flutter/issues/117819. + final Object cameraUpdate; +} + +/// Pigeon equivalent of NewCameraPosition +class PlatformCameraUpdateNewCameraPosition { + PlatformCameraUpdateNewCameraPosition(this.cameraPosition); + final PlatformCameraPosition cameraPosition; +} + +/// Pigeon equivalent of NewLatLng +class PlatformCameraUpdateNewLatLng { + PlatformCameraUpdateNewLatLng(this.latLng); + final PlatformLatLng latLng; +} + +/// Pigeon equivalent of NewLatLngBounds +class PlatformCameraUpdateNewLatLngBounds { + PlatformCameraUpdateNewLatLngBounds(this.bounds, this.padding); + final PlatformLatLngBounds bounds; + final double padding; +} + +/// Pigeon equivalent of NewLatLngZoom +class PlatformCameraUpdateNewLatLngZoom { + PlatformCameraUpdateNewLatLngZoom(this.latLng, this.zoom); + final PlatformLatLng latLng; + final double zoom; +} + +/// Pigeon equivalent of ScrollBy +class PlatformCameraUpdateScrollBy { + PlatformCameraUpdateScrollBy(this.dx, this.dy); + final double dx; + final double dy; +} + +/// Pigeon equivalent of ZoomBy +class PlatformCameraUpdateZoomBy { + PlatformCameraUpdateZoomBy(this.amount, [this.focus]); + final double amount; + final PlatformPoint? focus; +} + +/// Pigeon equivalent of ZoomIn/ZoomOut +class PlatformCameraUpdateZoom { + PlatformCameraUpdateZoom(this.out); + final bool out; +} + +/// Pigeon equivalent of ZoomTo +class PlatformCameraUpdateZoomTo { + PlatformCameraUpdateZoomTo(this.zoom); + final double zoom; } /// Pigeon equivalent of the Circle class. class PlatformCircle { - PlatformCircle(this.json); + PlatformCircle({ + required this.circleId, + required this.center, + this.consumeTapEvents = false, + this.fillColor = 0x00000000, + this.strokeColor = 0xFF000000, + this.visible = true, + this.strokeWidth = 10, + this.zIndex = 0.0, + this.radius = 0, + }); - /// The circle data, as JSON. This should only be set from - /// Circle.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - // TODO(stuartmorgan): Replace this with structured data. This exists only to - // allow incremental migration to Pigeon. - final Object json; + final bool consumeTapEvents; + final int fillColor; + final int strokeColor; + final bool visible; + final int strokeWidth; + final double zIndex; + final PlatformLatLng center; + final double radius; + final String circleId; } /// Pigeon equivalent of the Heatmap class. @@ -75,6 +138,19 @@ class PlatformHeatmap { final Object json; } +/// Pigeon equivalent of the InfoWindow class. +class PlatformInfoWindow { + PlatformInfoWindow({ + required this.anchor, + this.title, + this.snippet, + }); + + final String? title; + final String? snippet; + final PlatformPoint anchor; +} + /// Pigeon equivalent of Cluster. class PlatformCluster { PlatformCluster({ @@ -99,38 +175,117 @@ class PlatformClusterManager { /// Pigeon equivalent of the Marker class. class PlatformMarker { - PlatformMarker(this.json); + PlatformMarker({ + required this.markerId, + required this.icon, + this.alpha = 1.0, + required this.anchor, + this.consumeTapEvents = false, + this.draggable = false, + this.flat = false, + required this.infoWindow, + required this.position, + this.rotation = 0.0, + this.visible = true, + this.zIndex = 0.0, + this.clusterManagerId, + }); - /// The marker data, as JSON. This should only be set from - /// Marker.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - // TODO(stuartmorgan): Replace this with structured data. This exists only to - // allow incremental migration to Pigeon. - final Object json; + final double alpha; + final PlatformPoint anchor; + final bool consumeTapEvents; + final bool draggable; + final bool flat; + + final PlatformBitmap icon; + final PlatformInfoWindow infoWindow; + final PlatformLatLng position; + final double rotation; + final bool visible; + final double zIndex; + final String markerId; + final String? clusterManagerId; } /// Pigeon equivalent of the Polygon class. class PlatformPolygon { - PlatformPolygon(this.json); + PlatformPolygon({ + required this.polygonId, + required this.consumesTapEvents, + required this.fillColor, + required this.geodesic, + required this.points, + required this.holes, + required this.visible, + required this.strokeColor, + required this.strokeWidth, + required this.zIndex, + }); - /// The polygon data, as JSON. This should only be set from - /// Polygon.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - // TODO(stuartmorgan): Replace this with structured data. This exists only to - // allow incremental migration to Pigeon. - final Object json; + final String polygonId; + final bool consumesTapEvents; + final int fillColor; + final bool geodesic; + final List points; + final List> holes; + final bool visible; + final int strokeColor; + final int strokeWidth; + final int zIndex; +} + +/// Join types for polyline joints. +enum PlatformJointType { + mitered, + bevel, + round, } /// Pigeon equivalent of the Polyline class. class PlatformPolyline { - PlatformPolyline(this.json); + PlatformPolyline({ + required this.polylineId, + required this.consumesTapEvents, + required this.color, + required this.geodesic, + required this.jointType, + required this.patterns, + required this.points, + required this.visible, + required this.width, + required this.zIndex, + }); - /// The polyline data, as JSON. This should only be set from - /// Polyline.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - // TODO(stuartmorgan): Replace this with structured data. This exists only to - // allow incremental migration to Pigeon. - final Object json; + final String polylineId; + final bool consumesTapEvents; + final int color; + final bool geodesic; + + /// The joint type. + final PlatformJointType jointType; + + /// The pattern data, as a list of pattern items. + final List patterns; + final List points; + + final bool visible; + final int width; + final int zIndex; +} + +/// Enumeration of possible types for PatternItem. +enum PlatformPatternItemType { + dot, + dash, + gap, +} + +/// Pigeon equivalent of the PatternItem class. +class PlatformPatternItem { + PlatformPatternItem({required this.type, this.length}); + + final PlatformPatternItemType type; + final double? length; } /// Pigeon equivalent of the Tile class. @@ -144,14 +299,21 @@ class PlatformTile { /// Pigeon equivalent of the TileOverlay class. class PlatformTileOverlay { - PlatformTileOverlay(this.json); + PlatformTileOverlay({ + required this.tileOverlayId, + required this.fadeIn, + required this.transparency, + required this.zIndex, + required this.visible, + required this.tileSize, + }); - /// The tile overlay data, as JSON. This should only be set from - /// TileOverlay.toJson, and the native code must interpret it according to the - /// internal implementation details of that method. - // TODO(stuartmorgan): Replace this with structured data. This exists only to - // allow incremental migration to Pigeon. - final Object json; + final String tileOverlayId; + final bool fadeIn; + final double transparency; + final int zIndex; + final bool visible; + final int tileSize; } /// Pigeon equivalent of Flutter's EdgeInsets. @@ -269,6 +431,14 @@ class PlatformPoint { final double y; } +/// Pigeon representation of a size. +class PlatformSize { + PlatformSize({required this.width, required this.height}); + + final double width; + final double height; +} + /// Pigeon equivalent of GMSTileLayer properties. class PlatformTileLayer { PlatformTileLayer({ @@ -292,6 +462,90 @@ class PlatformZoomRange { final double? max; } +/// Pigeon equivalent of [BitmapDescriptor]. As there are multiple disjoint +/// types of [BitmapDescriptor], [PlatformBitmap] contains a single field which +/// may hold the pigeon equivalent type of any of them. +class PlatformBitmap { + PlatformBitmap({required this.bitmap}); + + /// One of [PlatformBitmapAssetMap], [PlatformBitmapAsset], + /// [PlatformBitmapAssetImage], [PlatformBitmapBytesMap], + /// [PlatformBitmapBytes], or [PlatformBitmapDefaultMarker]. + /// As Pigeon does not currently support data class inheritance, this + /// approach allows for the different bitmap implementations to be valid + /// argument and return types of the API methods. See + /// https://github.com/flutter/flutter/issues/117819. + final Object bitmap; +} + +/// Pigeon equivalent of [DefaultMarker]. +class PlatformBitmapDefaultMarker { + PlatformBitmapDefaultMarker({this.hue}); + + final double? hue; +} + +/// Pigeon equivalent of [BytesBitmap]. +class PlatformBitmapBytes { + PlatformBitmapBytes({required this.byteData, this.size}); + + final Uint8List byteData; + final PlatformSize? size; +} + +/// Pigeon equivalent of [AssetBitmap]. +class PlatformBitmapAsset { + PlatformBitmapAsset({required this.name, this.pkg}); + + final String name; + final String? pkg; +} + +/// Pigeon equivalent of [AssetImageBitmap]. +class PlatformBitmapAssetImage { + PlatformBitmapAssetImage( + {required this.name, required this.scale, this.size}); + final String name; + final double scale; + final PlatformSize? size; +} + +/// Pigeon equivalent of [AssetMapBitmap]. +class PlatformBitmapAssetMap { + PlatformBitmapAssetMap( + {required this.assetName, + required this.bitmapScaling, + required this.imagePixelRatio, + this.width, + this.height}); + final String assetName; + final PlatformMapBitmapScaling bitmapScaling; + final double imagePixelRatio; + final double? width; + final double? height; +} + +/// Pigeon equivalent of [BytesMapBitmap]. +class PlatformBitmapBytesMap { + PlatformBitmapBytesMap( + {required this.byteData, + required this.bitmapScaling, + required this.imagePixelRatio, + this.width, + this.height}); + final Uint8List byteData; + final PlatformMapBitmapScaling bitmapScaling; + final double imagePixelRatio; + final double? width; + final double? height; +} + +/// Pigeon equivalent of [MapBitmapScaling]. +enum PlatformMapBitmapScaling { + auto, + none, +} + /// Interface for non-test interactions with the native SDK. /// /// For test-only state queries, see [MapsInspectorApi]. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index 9d727a0c46fa..b90fc561e983 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.13.1 +version: 2.13.2 environment: sdk: ^3.3.0 @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.9.0 + google_maps_flutter_platform_interface: ^2.9.5 stream_transform: ^2.0.0 dev_dependencies: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart index 7a2f26809dd3..51a5e04cd975 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart @@ -104,7 +104,7 @@ void main() { expect(bounds, expectedBounds); }); - test('moveCamera calls through', () async { + test('moveCamera calls through with expected scrollBy', () async { const int mapId = 1; final (GoogleMapsFlutterIOS maps, MockMapsApi api) = setUpMockMap(mapId: mapId); @@ -115,10 +115,14 @@ void main() { final VerificationResult verification = verify(api.moveCamera(captureAny)); final PlatformCameraUpdate passedUpdate = verification.captured[0] as PlatformCameraUpdate; - expect(passedUpdate.json, update.toJson()); + final PlatformCameraUpdateScrollBy scroll = + passedUpdate.cameraUpdate as PlatformCameraUpdateScrollBy; + update as CameraUpdateScrollBy; + expect(scroll.dx, update.dx); + expect(scroll.dy, update.dy); }); - test('animateCamera calls through', () async { + test('animateCamera calls through with expected scrollBy', () async { const int mapId = 1; final (GoogleMapsFlutterIOS maps, MockMapsApi api) = setUpMockMap(mapId: mapId); @@ -130,7 +134,11 @@ void main() { verify(api.animateCamera(captureAny)); final PlatformCameraUpdate passedUpdate = verification.captured[0] as PlatformCameraUpdate; - expect(passedUpdate.json, update.toJson()); + final PlatformCameraUpdateScrollBy scroll = + passedUpdate.cameraUpdate as PlatformCameraUpdateScrollBy; + update as CameraUpdateScrollBy; + expect(scroll.dx, update.dx); + expect(scroll.dy, update.dy); }); test('getZoomLevel passes values correctly', () async { @@ -291,20 +299,73 @@ void main() { final VerificationResult verification = verify(api.updateCircles(captureAny, captureAny, captureAny)); - final List toAdd = - verification.captured[0] as List; - final List toChange = - verification.captured[1] as List; - final List toRemove = verification.captured[2] as List; + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; // Object one should be removed. expect(toRemove.length, 1); expect(toRemove.first, object1.circleId.value); // Object two should be changed. - expect(toChange.length, 1); - expect(toChange.first?.json, object2new.toJson()); + { + expect(toChange.length, 1); + final PlatformCircle firstChanged = toChange.first; + expect(firstChanged.consumeTapEvents, object2new.consumeTapEvents); + expect(firstChanged.fillColor, object2new.fillColor.value); + expect(firstChanged.strokeColor, object2new.strokeColor.value); + expect(firstChanged.visible, object2new.visible); + expect(firstChanged.strokeWidth, object2new.strokeWidth); + expect(firstChanged.zIndex, object2new.zIndex.toDouble()); + expect(firstChanged.center.latitude, object2new.center.latitude); + expect(firstChanged.center.longitude, object2new.center.longitude); + expect(firstChanged.radius, object2new.radius); + expect(firstChanged.circleId, object2new.circleId.value); + } + // Object 3 should be added. + { + expect(toAdd.length, 1); + final PlatformCircle firstAdded = toAdd.first; + expect(firstAdded.consumeTapEvents, object3.consumeTapEvents); + expect(firstAdded.fillColor, object3.fillColor.value); + expect(firstAdded.strokeColor, object3.strokeColor.value); + expect(firstAdded.visible, object3.visible); + expect(firstAdded.strokeWidth, object3.strokeWidth); + expect(firstAdded.zIndex, object3.zIndex.toDouble()); + expect(firstAdded.center.latitude, object3.center.latitude); + expect(firstAdded.center.longitude, object3.center.longitude); + expect(firstAdded.radius, object3.radius); + expect(firstAdded.circleId, object3.circleId.value); + } + }); + + test('updateClusterManagers passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const ClusterManager object1 = + ClusterManager(clusterManagerId: ClusterManagerId('1')); + const ClusterManager object3 = + ClusterManager(clusterManagerId: ClusterManagerId('3')); + await maps.updateClusterManagers( + ClusterManagerUpdates.from( + {object1}, {object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updateClusterManagers(captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toRemove = verification.captured[1] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.clusterManagerId.value); + // Unlike other map object types, changes are not possible for cluster + // managers, since they have no non-ID properties. // Object 3 should be added. expect(toAdd.length, 1); - expect(toAdd.first?.json, object3.toJson()); + expect(toAdd.first.identifier, object3.clusterManagerId.value); }); test('updateMarkers passes expected arguments', () async { @@ -323,20 +384,69 @@ void main() { final VerificationResult verification = verify(api.updateMarkers(captureAny, captureAny, captureAny)); - final List toAdd = - verification.captured[0] as List; - final List toChange = - verification.captured[1] as List; - final List toRemove = verification.captured[2] as List; + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; // Object one should be removed. expect(toRemove.length, 1); expect(toRemove.first, object1.markerId.value); // Object two should be changed. - expect(toChange.length, 1); - expect(toChange.first?.json, object2new.toJson()); + { + expect(toChange.length, 1); + final PlatformMarker firstChanged = toChange.first; + expect(firstChanged.alpha, object2new.alpha); + expect(firstChanged.anchor.x, object2new.anchor.dx); + expect(firstChanged.anchor.y, object2new.anchor.dy); + expect(firstChanged.consumeTapEvents, object2new.consumeTapEvents); + expect(firstChanged.draggable, object2new.draggable); + expect(firstChanged.flat, object2new.flat); + expect( + firstChanged.icon.bitmap.runtimeType, + GoogleMapsFlutterIOS.platformBitmapFromBitmapDescriptor( + object2new.icon) + .bitmap + .runtimeType); + expect(firstChanged.infoWindow.title, object2new.infoWindow.title); + expect(firstChanged.infoWindow.snippet, object2new.infoWindow.snippet); + expect(firstChanged.infoWindow.anchor.x, object2new.infoWindow.anchor.dx); + expect(firstChanged.infoWindow.anchor.y, object2new.infoWindow.anchor.dy); + expect(firstChanged.position.latitude, object2new.position.latitude); + expect(firstChanged.position.longitude, object2new.position.longitude); + expect(firstChanged.rotation, object2new.rotation); + expect(firstChanged.visible, object2new.visible); + expect(firstChanged.zIndex, object2new.zIndex); + expect(firstChanged.markerId, object2new.markerId.value); + expect(firstChanged.clusterManagerId, object2new.clusterManagerId?.value); + } // Object 3 should be added. - expect(toAdd.length, 1); - expect(toAdd.first?.json, object3.toJson()); + { + expect(toAdd.length, 1); + final PlatformMarker firstAdded = toAdd.first; + expect(firstAdded.alpha, object3.alpha); + expect(firstAdded.anchor.x, object3.anchor.dx); + expect(firstAdded.anchor.y, object3.anchor.dy); + expect(firstAdded.consumeTapEvents, object3.consumeTapEvents); + expect(firstAdded.draggable, object3.draggable); + expect(firstAdded.flat, object3.flat); + expect( + firstAdded.icon.bitmap.runtimeType, + GoogleMapsFlutterIOS.platformBitmapFromBitmapDescriptor(object3.icon) + .bitmap + .runtimeType); + expect(firstAdded.infoWindow.title, object3.infoWindow.title); + expect(firstAdded.infoWindow.snippet, object3.infoWindow.snippet); + expect(firstAdded.infoWindow.anchor.x, object3.infoWindow.anchor.dx); + expect(firstAdded.infoWindow.anchor.y, object3.infoWindow.anchor.dy); + expect(firstAdded.position.latitude, object3.position.latitude); + expect(firstAdded.position.longitude, object3.position.longitude); + expect(firstAdded.rotation, object3.rotation); + expect(firstAdded.visible, object3.visible); + expect(firstAdded.zIndex, object3.zIndex); + expect(firstAdded.markerId, object3.markerId.value); + expect(firstAdded.clusterManagerId, object3.clusterManagerId?.value); + } }); test('updatePolygons passes expected arguments', () async { @@ -355,20 +465,44 @@ void main() { final VerificationResult verification = verify(api.updatePolygons(captureAny, captureAny, captureAny)); - final List toAdd = - verification.captured[0] as List; - final List toChange = - verification.captured[1] as List; - final List toRemove = verification.captured[2] as List; + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; // Object one should be removed. expect(toRemove.length, 1); expect(toRemove.first, object1.polygonId.value); + void expectPolygon(PlatformPolygon actual, Polygon expected) { + expect(actual.polygonId, expected.polygonId.value); + expect(actual.consumesTapEvents, expected.consumeTapEvents); + expect(actual.fillColor, expected.fillColor.value); + expect(actual.geodesic, expected.geodesic); + expect(actual.points.length, expected.points.length); + for (final (int i, PlatformLatLng? point) in actual.points.indexed) { + expect(point?.latitude, expected.points[i].latitude); + expect(point?.longitude, expected.points[i].longitude); + } + expect(actual.holes.length, expected.holes.length); + for (final (int i, List? hole) in actual.holes.indexed) { + final List expectedHole = expected.holes[i]; + for (final (int j, PlatformLatLng? point) in hole!.indexed) { + expect(point?.latitude, expectedHole[j].latitude); + expect(point?.longitude, expectedHole[j].longitude); + } + } + expect(actual.visible, expected.visible); + expect(actual.strokeColor, expected.strokeColor.value); + expect(actual.strokeWidth, expected.strokeWidth); + expect(actual.zIndex, expected.zIndex); + } + // Object two should be changed. expect(toChange.length, 1); - expect(toChange.first?.json, object2new.toJson()); + expectPolygon(toChange.first, object2new); // Object 3 should be added. expect(toAdd.length, 1); - expect(toAdd.first?.json, object3.toJson()); + expectPolygon(toAdd.first, object3); }); test('updatePolylines passes expected arguments', () async { @@ -378,8 +512,14 @@ void main() { const Polyline object1 = Polyline(polylineId: PolylineId('1')); const Polyline object2old = Polyline(polylineId: PolylineId('2')); - final Polyline object2new = object2old.copyWith(widthParam: 42); - const Polyline object3 = Polyline(polylineId: PolylineId('3')); + final Polyline object2new = object2old.copyWith( + widthParam: 42, startCapParam: Cap.squareCap, endCapParam: Cap.buttCap); + final Cap customCap = + Cap.customCapFromBitmap(BitmapDescriptor.defaultMarker, refWidth: 15); + final Polyline object3 = Polyline( + polylineId: const PolylineId('3'), + startCap: customCap, + endCap: Cap.roundCap); await maps.updatePolylines( PolylineUpdates.from( {object1, object2old}, {object2new, object3}), @@ -387,20 +527,43 @@ void main() { final VerificationResult verification = verify(api.updatePolylines(captureAny, captureAny, captureAny)); - final List toAdd = - verification.captured[0] as List; - final List toChange = - verification.captured[1] as List; - final List toRemove = verification.captured[2] as List; + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + void expectPolyline(PlatformPolyline actual, Polyline expected) { + expect(actual.polylineId, expected.polylineId.value); + expect(actual.consumesTapEvents, expected.consumeTapEvents); + expect(actual.color, expected.color.value); + expect(actual.geodesic, expected.geodesic); + expect( + actual.jointType, platformJointTypeFromJointType(expected.jointType)); + expect(actual.visible, expected.visible); + expect(actual.width, expected.width); + expect(actual.zIndex, expected.zIndex); + expect(actual.points.length, expected.points.length); + for (final (int i, PlatformLatLng? point) in actual.points.indexed) { + expect(point?.latitude, actual.points[i].latitude); + expect(point?.longitude, actual.points[i].longitude); + } + expect(actual.patterns.length, expected.patterns.length); + for (final (int i, PlatformPatternItem? pattern) + in actual.patterns.indexed) { + expect(pattern?.encode(), + platformPatternItemFromPatternItem(expected.patterns[i]).encode()); + } + } + // Object one should be removed. expect(toRemove.length, 1); expect(toRemove.first, object1.polylineId.value); // Object two should be changed. expect(toChange.length, 1); - expect(toChange.first?.json, object2new.toJson()); + expectPolyline(toChange.first, object2new); // Object 3 should be added. expect(toAdd.length, 1); - expect(toAdd.first?.json, object3.toJson()); + expectPolyline(toAdd.first, object3); }); test('updateTileOverlays passes expected arguments', () async { @@ -424,20 +587,29 @@ void main() { final VerificationResult verification = verify(api.updateTileOverlays(captureAny, captureAny, captureAny)); - final List toAdd = - verification.captured[0] as List; - final List toChange = - verification.captured[1] as List; - final List toRemove = verification.captured[2] as List; + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + void expectTileOverlay(PlatformTileOverlay actual, TileOverlay expected) { + expect(actual.tileOverlayId, expected.tileOverlayId.value); + expect(actual.fadeIn, expected.fadeIn); + expect(actual.transparency, expected.transparency); + expect(actual.zIndex, expected.zIndex); + expect(actual.visible, expected.visible); + expect(actual.tileSize, expected.tileSize); + } + // Object one should be removed. expect(toRemove.length, 1); expect(toRemove.first, object1.tileOverlayId.value); // Object two should be changed. expect(toChange.length, 1); - expect(toChange.first?.json, object2new.toJson()); + expectTileOverlay(toChange.first, object2new); // Object 3 should be added. expect(toAdd.length, 1); - expect(toAdd.first?.json, object3.toJson()); + expectTileOverlay(toAdd.first, object3); }); test('markers send drag event to correct streams', () async { @@ -503,6 +675,45 @@ void main() { expect((await stream.next).value.value, equals(objectId)); }); + test('clusters send tap events to correct stream', () async { + const int mapId = 1; + const String managerId = 'manager-id'; + final PlatformLatLng fakePosition = + PlatformLatLng(latitude: 10, longitude: 20); + final PlatformLatLngBounds fakeBounds = PlatformLatLngBounds( + southwest: PlatformLatLng(latitude: 30, longitude: 40), + northeast: PlatformLatLng(latitude: 50, longitude: 60)); + const List markerIds = ['marker-1', 'marker-2']; + final PlatformCluster cluster = PlatformCluster( + clusterManagerId: managerId, + position: fakePosition, + bounds: fakeBounds, + markerIds: markerIds); + + final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = + maps.ensureHandlerInitialized(mapId); + + final StreamQueue stream = + StreamQueue(maps.onClusterTap(mapId: mapId)); + + // Simulate message from the native side. + callbackHandler.onClusterTap(cluster); + + final Cluster eventValue = (await stream.next).value; + expect(eventValue.clusterManagerId.value, managerId); + expect(eventValue.position.latitude, fakePosition.latitude); + expect(eventValue.position.longitude, fakePosition.longitude); + expect(eventValue.bounds.southwest.latitude, fakeBounds.southwest.latitude); + expect( + eventValue.bounds.southwest.longitude, fakeBounds.southwest.longitude); + expect(eventValue.bounds.northeast.latitude, fakeBounds.northeast.latitude); + expect( + eventValue.bounds.northeast.longitude, fakeBounds.northeast.longitude); + expect(eventValue.markerIds.length, markerIds.length); + expect(eventValue.markerIds.first.value, markerIds.first); + }); + test('polygons send tap events to correct stream', () async { const int mapId = 1; const String objectId = 'object-id'; @@ -537,6 +748,217 @@ void main() { expect((await stream.next).value.value, equals(objectId)); }); + test('moveCamera calls through with expected newCameraPosition', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const LatLng latLng = LatLng(10.0, 20.0); + const CameraPosition position = CameraPosition(target: latLng); + final CameraUpdate update = CameraUpdate.newCameraPosition(position); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateNewCameraPosition typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateNewCameraPosition; + update as CameraUpdateNewCameraPosition; + expect(typedUpdate.cameraPosition.target.latitude, + update.cameraPosition.target.latitude); + expect(typedUpdate.cameraPosition.target.longitude, + update.cameraPosition.target.longitude); + }); + + test('moveCamera calls through with expected newLatLng', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const LatLng latLng = LatLng(10.0, 20.0); + final CameraUpdate update = CameraUpdate.newLatLng(latLng); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateNewLatLng typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateNewLatLng; + update as CameraUpdateNewLatLng; + expect(typedUpdate.latLng.latitude, update.latLng.latitude); + expect(typedUpdate.latLng.longitude, update.latLng.longitude); + }); + + test('moveCamera calls through with expected newLatLngBounds', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final LatLngBounds latLng = LatLngBounds( + northeast: const LatLng(10.0, 20.0), + southwest: const LatLng(9.0, 21.0)); + final CameraUpdate update = CameraUpdate.newLatLngBounds(latLng, 1.0); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateNewLatLngBounds typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateNewLatLngBounds; + update as CameraUpdateNewLatLngBounds; + expect(typedUpdate.bounds.northeast.latitude, + update.bounds.northeast.latitude); + expect(typedUpdate.bounds.northeast.longitude, + update.bounds.northeast.longitude); + expect(typedUpdate.bounds.southwest.latitude, + update.bounds.southwest.latitude); + expect(typedUpdate.bounds.southwest.longitude, + update.bounds.southwest.longitude); + expect(typedUpdate.padding, update.padding); + }); + + test('moveCamera calls through with expected newLatLngZoom', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const LatLng latLng = LatLng(10.0, 20.0); + final CameraUpdate update = CameraUpdate.newLatLngZoom(latLng, 2.0); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateNewLatLngZoom typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateNewLatLngZoom; + update as CameraUpdateNewLatLngZoom; + expect(typedUpdate.latLng.latitude, update.latLng.latitude); + expect(typedUpdate.latLng.longitude, update.latLng.longitude); + expect(typedUpdate.zoom, update.zoom); + }); + + test('moveCamera calls through with expected zoomBy', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Offset focus = Offset(10.0, 20.0); + final CameraUpdate update = CameraUpdate.zoomBy(2.0, focus); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateZoomBy typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateZoomBy; + update as CameraUpdateZoomBy; + expect(typedUpdate.focus?.x, update.focus?.dx); + expect(typedUpdate.focus?.y, update.focus?.dy); + expect(typedUpdate.amount, update.amount); + }); + + test('moveCamera calls through with expected zoomTo', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final CameraUpdate update = CameraUpdate.zoomTo(2.0); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateZoomTo typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateZoomTo; + update as CameraUpdateZoomTo; + expect(typedUpdate.zoom, update.zoom); + }); + + test('moveCamera calls through with expected zoomIn', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final CameraUpdate update = CameraUpdate.zoomIn(); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateZoom typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateZoom; + expect(typedUpdate.out, false); + }); + + test('moveCamera calls through with expected zoomOut', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final CameraUpdate update = CameraUpdate.zoomOut(); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + final PlatformCameraUpdateZoom typedUpdate = + passedUpdate.cameraUpdate as PlatformCameraUpdateZoom; + expect(typedUpdate.out, true); + }); + + test('MapBitmapScaling to PlatformMapBitmapScaling', () { + expect( + GoogleMapsFlutterIOS.platformMapBitmapScalingFromScaling( + MapBitmapScaling.auto), + PlatformMapBitmapScaling.auto); + expect( + GoogleMapsFlutterIOS.platformMapBitmapScalingFromScaling( + MapBitmapScaling.none), + PlatformMapBitmapScaling.none); + }); + + test('DefaultMarker bitmap to PlatformBitmap', () { + final BitmapDescriptor bitmap = BitmapDescriptor.defaultMarkerWithHue(10.0); + final PlatformBitmap platformBitmap = + GoogleMapsFlutterIOS.platformBitmapFromBitmapDescriptor(bitmap); + expect(platformBitmap.bitmap, isA()); + final PlatformBitmapDefaultMarker typedBitmap = + platformBitmap.bitmap as PlatformBitmapDefaultMarker; + expect(typedBitmap.hue, 10.0); + }); + + test('BytesMapBitmap bitmap to PlatformBitmap', () { + final Uint8List data = Uint8List.fromList([1, 2, 3, 4]); + final BytesMapBitmap bitmap = BitmapDescriptor.bytes(data, + imagePixelRatio: 2.0, width: 100.0, height: 200.0); + final PlatformBitmap platformBitmap = + GoogleMapsFlutterIOS.platformBitmapFromBitmapDescriptor(bitmap); + expect(platformBitmap.bitmap, isA()); + final PlatformBitmapBytesMap typedBitmap = + platformBitmap.bitmap as PlatformBitmapBytesMap; + expect(typedBitmap.byteData, data); + expect(typedBitmap.bitmapScaling, PlatformMapBitmapScaling.auto); + expect(typedBitmap.imagePixelRatio, 2.0); + expect(typedBitmap.width, 100.0); + expect(typedBitmap.height, 200.0); + }); + + test('AssetMapBitmap bitmap to PlatformBitmap', () { + const String assetName = 'fake_asset_name'; + final AssetMapBitmap bitmap = AssetMapBitmap(assetName, + imagePixelRatio: 2.0, width: 100.0, height: 200.0); + final PlatformBitmap platformBitmap = + GoogleMapsFlutterIOS.platformBitmapFromBitmapDescriptor(bitmap); + expect(platformBitmap.bitmap, isA()); + final PlatformBitmapAssetMap typedBitmap = + platformBitmap.bitmap as PlatformBitmapAssetMap; + expect(typedBitmap.assetName, assetName); + expect(typedBitmap.bitmapScaling, PlatformMapBitmapScaling.auto); + expect(typedBitmap.imagePixelRatio, 2.0); + expect(typedBitmap.width, 100.0); + expect(typedBitmap.height, 200.0); + }); + testWidgets('cloudMapId is passed', (WidgetTester tester) async { const String cloudMapId = '000000000000000'; // Dummy map ID. final Completer passedCloudMapIdCompleter = Completer(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 89f5e33e2387..cbc12bf562f2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -194,9 +194,10 @@ class GoogleMapController { /// own configuration and are rendered on top of a GMap instance later. This /// happens in the second half of this method. /// - /// This method is eagerly called from the [GoogleMapsPlugin.buildView] method - /// so the internal [GoogleMapsController] of a Web Map initializes as soon as - /// possible. Check [_attachMapEvents] to see how this controller notifies the + /// This method is eagerly called from the + /// [GoogleMapsPlugin.buildViewWithConfiguration] method so the internal + /// [GoogleMapsController] of a Web Map initializes as soon as possible. + /// Check [_attachMapEvents] to see how this controller notifies the /// plugin of it being fully ready (through the `onTilesloaded.first` event). /// /// Failure to call this method would result in the GMap not rendering at all, diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj index 6d9eec9e3fab..8c716ddb73fd 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -58,6 +59,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -160,6 +162,9 @@ dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -170,7 +175,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -187,6 +192,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -234,11 +242,19 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/AppAuth/AppAuthCore_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GTMAppAuth/GTMAppAuth_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher_Core_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher_Full_Privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/google_sign_in_ios/google_sign_in_ios_privacy.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AppAuthCore_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMAppAuth_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMSessionFetcher_Core_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GTMSessionFetcher_Full_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/google_sign_in_ios_privacy.bundle", ); @@ -491,6 +507,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8e83ef7194ee..330fa765f0d6 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/project.pbxproj index a681b86eb700..1ba5e62c06c9 100644 --- a/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/project.pbxproj @@ -191,6 +191,8 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + CDA781BB611B257CD4D32B4D /* [CP] Embed Pods Frameworks */, + 5845DE94A5B9AC1E93542D07 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -209,7 +211,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -298,6 +300,40 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 5845DE94A5B9AC1E93542D07 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + CDA781BB611B257CD4D32B4D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; DD22313F1BC2623931943E8D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 397f3d339fde..ac78810cdd2c 100644 --- a/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_sign_in/google_sign_in/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/packages/google_sign_in/google_sign_in/example/macos/Runner/AppDelegate.swift b/packages/google_sign_in/google_sign_in/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/google_sign_in/google_sign_in/example/macos/Runner/AppDelegate.swift +++ b/packages/google_sign_in/google_sign_in/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index 35af262af528..75dcc58af336 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.34 + +* Removes unnecessary native code. + ## 6.1.33 * Updates Pigeon for non-nullable collection type support. diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index 301642bce1e8..92f3f9927df3 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -31,14 +31,11 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugins.googlesignin.Messages.FlutterError; import io.flutter.plugins.googlesignin.Messages.GoogleSignInApi; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -111,241 +108,16 @@ public void onDetachedFromActivity() { disposeActivity(); } - // TODO(stuartmorgan): Remove this, and convert the unit tests to IDelegate tests. This is left - // here only to allow the existing tests to continue to work unchanged during the Pigeon migration - // to ensure that the refactoring didn't change any behavior, and is not actually used by the - // plugin. - @VisibleForTesting - void onMethodCall( - @NonNull io.flutter.plugin.common.MethodCall call, @NonNull MethodChannel.Result result) { - switch (call.method) { - case "init": - String signInOption = Objects.requireNonNull(call.argument("signInOption")); - List requestedScopes = Objects.requireNonNull(call.argument("scopes")); - String hostedDomain = call.argument("hostedDomain"); - String clientId = call.argument("clientId"); - String serverClientId = call.argument("serverClientId"); - boolean forceCodeForRefreshToken = - Objects.requireNonNull(call.argument("forceCodeForRefreshToken")); - delegate.init( - result, - signInOption, - requestedScopes, - hostedDomain, - clientId, - serverClientId, - forceCodeForRefreshToken); - break; - - case "signInSilently": - delegate.signInSilently(result); - break; - - case "signIn": - delegate.signIn(result); - break; - - case "getTokens": - String email = Objects.requireNonNull(call.argument("email")); - boolean shouldRecoverAuth = Objects.requireNonNull(call.argument("shouldRecoverAuth")); - delegate.getTokens(result, email, shouldRecoverAuth); - break; - - case "signOut": - delegate.signOut(result); - break; - - case "clearAuthCache": - String token = Objects.requireNonNull(call.argument("token")); - delegate.clearAuthCache(result, token); - break; - - case "disconnect": - delegate.disconnect(result); - break; - - case "isSignedIn": - delegate.isSignedIn(result); - break; - - case "requestScopes": - List scopes = Objects.requireNonNull(call.argument("scopes")); - delegate.requestScopes(result, scopes); - break; - - default: - result.notImplemented(); - } - } - - /** - * A delegate interface that exposes all of the sign-in functionality for other plugins to use. - * The below {@link Delegate} implementation should be used by any clients unless they need to - * override some of these functions, such as for testing. - */ - public interface IDelegate { - /** Initializes this delegate so that it is ready to perform other operations. */ - void init( - @NonNull MethodChannel.Result result, - @NonNull String signInOption, - @NonNull List requestedScopes, - @Nullable String hostedDomain, - @Nullable String clientId, - @Nullable String serverClientId, - boolean forceCodeForRefreshToken); - - /** - * Returns the account information for the user who is signed in to this app. If no user is - * signed in, tries to sign the user in without displaying any user interface. - */ - void signInSilently(@NonNull MethodChannel.Result result); - - /** - * Signs the user in via the sign-in user interface, including the OAuth consent flow if scopes - * were requested. - */ - void signIn(@NonNull MethodChannel.Result result); - - /** - * Gets an OAuth access token with the scopes that were specified during initialization for the - * user with the specified email address. - * - *

If shouldRecoverAuth is set to true and user needs to recover authentication for method to - * complete, the method will attempt to recover authentication and rerun method. - */ - void getTokens( - final @NonNull MethodChannel.Result result, - final @NonNull String email, - final boolean shouldRecoverAuth); - - /** - * Clears the token from any client cache forcing the next {@link #getTokens} call to fetch a - * new one. - */ - void clearAuthCache(final @NonNull MethodChannel.Result result, final @NonNull String token); - - /** - * Signs the user out. Their credentials may remain valid, meaning they'll be able to silently - * sign back in. - */ - void signOut(@NonNull MethodChannel.Result result); - - /** Signs the user out, and revokes their credentials. */ - void disconnect(@NonNull MethodChannel.Result result); - - /** Checks if there is a signed in user. */ - void isSignedIn(@NonNull MethodChannel.Result result); - - /** Prompts the user to grant an additional Oauth scopes. */ - void requestScopes( - final @NonNull MethodChannel.Result result, final @NonNull List scopes); - } - - /** - * Helper class for supporting the legacy IDelegate interface based on raw method channels, which - * handles converting any FlutterErrors (or other {@code Throwable}s in case any non- FlutterError - * exceptions slip through) thrown by the new code paths into {@code error} callbacks. - */ - private abstract static class ErrorConvertingMethodChannelVoidResult - implements Messages.VoidResult { - final @NonNull MethodChannel.Result result; - - public ErrorConvertingMethodChannelVoidResult(@NonNull MethodChannel.Result result) { - this.result = result; - } - - @Override - public void error(@NonNull Throwable error) { - if (error instanceof FlutterError) { - FlutterError flutterError = (FlutterError) error; - result.error(flutterError.code, flutterError.getMessage(), flutterError.details); - } else { - result.error("exception", error.getMessage(), null); - } - } - } - - /** - * Helper class for supporting the legacy IDelegate interface based on raw method channels, which - * handles converting any FlutterErrors (or other {@code Throwable}s in case any non- FlutterError - * exceptions slip through) thrown by the new code paths into {@code error} callbacks. - * - * @param The Result type of the result to convert from. - */ - private abstract static class ErrorConvertingMethodChannelResult - implements Messages.Result { - final @NonNull MethodChannel.Result result; - - public ErrorConvertingMethodChannelResult(@NonNull MethodChannel.Result result) { - this.result = result; - } - - @Override - public void error(@NonNull Throwable error) { - if (error instanceof FlutterError) { - FlutterError flutterError = (FlutterError) error; - result.error(flutterError.code, flutterError.getMessage(), flutterError.details); - } else { - result.error("exception", error.getMessage(), null); - } - } - } - - /** - * Helper class for supporting the legacy IDelegate interface based on raw method channels, which - * handles converting responses from methods that return {@code Messages.UserData}. - */ - private static class UserDataMethodChannelResult - extends ErrorConvertingMethodChannelResult { - public UserDataMethodChannelResult(MethodChannel.Result result) { - super(result); - } - - @Override - public void success(Messages.UserData data) { - Map response = new HashMap<>(); - response.put("email", data.getEmail()); - response.put("id", data.getId()); - response.put("idToken", data.getIdToken()); - response.put("serverAuthCode", data.getServerAuthCode()); - response.put("displayName", data.getDisplayName()); - if (data.getPhotoUrl() != null) { - response.put("photoUrl", data.getPhotoUrl()); - } - result.success(response); - } - } - - /** - * Helper class for supporting the legacy IDelegate interface based on raw method channels, which - * handles converting responses from methods that return {@code Void}. - */ - private static class VoidMethodChannelResult extends ErrorConvertingMethodChannelVoidResult { - public VoidMethodChannelResult(MethodChannel.Result result) { - super(result); - } - - @Override - public void success() { - result.success(null); - } - } - /** * Delegate class that does the work for the Google sign-in plugin. This is exposed as a dedicated * class for use in other plugins that wrap basic sign-in functionality. * *

All methods in this class assume that they are run to completion before any other method is - * invoked. In this context, "run to completion" means that their {@link MethodChannel.Result} - * argument has been completed (either successfully or in error). This class provides no - * synchronization constructs to guarantee such behavior; callers are responsible for providing - * such guarantees. + * invoked. In this context, "run to completion" means that their {@link Messages.Result} argument + * has been completed (either successfully or in error). This class provides no synchronization + * constructs to guarantee such behavior; callers are responsible for providing such guarantees. */ - // TODO(stuartmorgan): Remove this in a breaking change, replacing it with something using - // structured types rather than strings and dictionaries left over from the pre-Pigeon method - // channel implementation. - public static class Delegate - implements IDelegate, PluginRegistry.ActivityResultListener, GoogleSignInApi { + public static class Delegate implements PluginRegistry.ActivityResultListener, GoogleSignInApi { private static final int REQUEST_CODE_SIGNIN = 53293; private static final int REQUEST_CODE_RECOVER_AUTH = 53294; @VisibleForTesting static final int REQUEST_CODE_REQUEST_SCOPE = 53295; @@ -360,9 +132,6 @@ public static class Delegate private static final String ERROR_FAILURE_TO_RECOVER_AUTH = "failed_to_recover_auth"; private static final String ERROR_USER_RECOVERABLE_AUTH = "user_recoverable_auth"; - private static final String DEFAULT_SIGN_IN = "SignInOption.standard"; - private static final String DEFAULT_GAMES_SIGN_IN = "SignInOption.games"; - private final @NonNull Context context; // Only set activity for v2 embedder. Always access activity from getActivity() method. private @Nullable Activity activity; @@ -496,43 +265,6 @@ public void init(@NonNull Messages.InitParams params) { } } - // IDelegate version, for backwards compatibility. - @Override - public void init( - @NonNull MethodChannel.Result result, - @NonNull String signInOption, - @NonNull List requestedScopes, - @Nullable String hostedDomain, - @Nullable String clientId, - @Nullable String serverClientId, - boolean forceCodeForRefreshToken) { - try { - Messages.SignInType type; - switch (signInOption) { - case DEFAULT_GAMES_SIGN_IN: - type = Messages.SignInType.GAMES; - break; - case DEFAULT_SIGN_IN: - type = Messages.SignInType.STANDARD; - break; - default: - throw new IllegalStateException("Unknown signInOption"); - } - init( - new Messages.InitParams.Builder() - .setSignInType(type) - .setScopes(requestedScopes) - .setHostedDomain(hostedDomain) - .setClientId(clientId) - .setServerClientId(serverClientId) - .setForceCodeForRefreshToken(forceCodeForRefreshToken) - .build()); - result.success(null); - } catch (FlutterError e) { - result.error(e.code, e.getMessage(), e.details); - } - } - /** * Returns the account information for the user who is signed in to this app. If no user is * signed in, tries to sign the user in without displaying any user interface. @@ -549,12 +281,6 @@ public void signInSilently(@NonNull Messages.Result result) { } } - // IDelegate version, for backwards compatibility. - @Override - public void signInSilently(@NonNull MethodChannel.Result result) { - signInSilently(new UserDataMethodChannelResult(result)); - } - /** * Signs the user in via the sign-in user interface, including the OAuth consent flow if scopes * were requested. @@ -570,12 +296,6 @@ public void signIn(@NonNull Messages.Result result) { getActivity().startActivityForResult(signInIntent, REQUEST_CODE_SIGNIN); } - // IDelegate version, for backwards compatibility. - @Override - public void signIn(@NonNull MethodChannel.Result result) { - signIn(new UserDataMethodChannelResult(result)); - } - /** * Signs the user out. Their credentials may remain valid, meaning they'll be able to silently * sign back in. @@ -596,12 +316,6 @@ public void signOut(@NonNull Messages.VoidResult result) { }); } - // IDelegate version, for backwards compatibility. - @Override - public void signOut(@NonNull MethodChannel.Result result) { - signOut(new VoidMethodChannelResult(result)); - } - /** Signs the user out, and revokes their credentials. */ @Override public void disconnect(@NonNull Messages.VoidResult result) { @@ -619,12 +333,6 @@ public void disconnect(@NonNull Messages.VoidResult result) { }); } - // IDelegate version, for backwards compatibility. - @Override - public void disconnect(@NonNull MethodChannel.Result result) { - signOut(new VoidMethodChannelResult(result)); - } - /** Checks if there is a signed in user. */ @NonNull @Override @@ -632,12 +340,6 @@ public Boolean isSignedIn() { return GoogleSignIn.getLastSignedInAccount(context) != null; } - // IDelegate version, for backwards compatibility. - @Override - public void isSignedIn(final @NonNull MethodChannel.Result result) { - result.success(isSignedIn()); - } - @Override public void requestScopes( @NonNull List scopes, @NonNull Messages.Result result) { @@ -667,19 +369,6 @@ public void requestScopes( getActivity(), REQUEST_CODE_REQUEST_SCOPE, account, wrappedScopes.toArray(new Scope[0])); } - // IDelegate version, for backwards compatibility. - @Override - public void requestScopes(@NonNull MethodChannel.Result result, @NonNull List scopes) { - requestScopes( - scopes, - new ErrorConvertingMethodChannelResult(result) { - @Override - public void success(Boolean value) { - result.success(value); - } - }); - } - private void onSignInResult(Task completedTask) { try { GoogleSignInAccount account = completedTask.getResult(ApiException.class); @@ -816,13 +505,6 @@ public void clearAuthCache(@NonNull String token, @NonNull Messages.VoidResult r }); } - // IDelegate version, for backwards compatibility. - @Override - public void clearAuthCache( - final @NonNull MethodChannel.Result result, final @NonNull String token) { - clearAuthCache(token, new VoidMethodChannelResult(result)); - } - /** * Gets an OAuth access token with the scopes that were specified during initialization for the * user with the specified email address. @@ -884,25 +566,6 @@ public void getAccessToken( }); } - // IDelegate version, for backwards compatibility. - @Override - public void getTokens( - @NonNull final MethodChannel.Result result, - @NonNull final String email, - final boolean shouldRecoverAuth) { - getAccessToken( - email, - shouldRecoverAuth, - new ErrorConvertingMethodChannelResult(result) { - @Override - public void success(String value) { - HashMap tokenResult = new HashMap<>(); - tokenResult.put("accessToken", value); - result.success(tokenResult); - } - }); - } - @Override public boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (pendingOperation == null) { diff --git a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInLegacyMethodChannelTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInLegacyMethodChannelTest.java deleted file mode 100644 index cd5b92217c65..000000000000 --- a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInLegacyMethodChannelTest.java +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins.googlesignin; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import com.google.android.gms.auth.api.signin.GoogleSignInAccount; -import com.google.android.gms.auth.api.signin.GoogleSignInClient; -import com.google.android.gms.auth.api.signin.GoogleSignInOptions; -import com.google.android.gms.common.api.ApiException; -import com.google.android.gms.common.api.CommonStatusCodes; -import com.google.android.gms.common.api.Scope; -import com.google.android.gms.common.api.Status; -import com.google.android.gms.tasks.Task; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.PluginRegistry; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; - -public class GoogleSignInLegacyMethodChannelTest { - @Mock Context mockContext; - @Mock Resources mockResources; - @Mock Activity mockActivity; - @Mock BinaryMessenger mockMessenger; - @Spy MethodChannel.Result result; - @Mock GoogleSignInWrapper mockGoogleSignIn; - @Mock GoogleSignInAccount account; - @Mock GoogleSignInClient mockClient; - @Mock Task mockSignInTask; - @Mock ActivityPluginBinding mockActivityPluginBinding; - - private GoogleSignInPlugin plugin; - private AutoCloseable mockCloseable; - - @Before - public void setUp() { - mockCloseable = MockitoAnnotations.openMocks(this); - when(mockContext.getResources()).thenReturn(mockResources); - when(mockActivityPluginBinding.getActivity()).thenReturn(mockActivity); - plugin = new GoogleSignInPlugin(); - plugin.initInstance(mockMessenger, mockContext, mockGoogleSignIn); - plugin.onAttachedToActivity(mockActivityPluginBinding); - } - - @After - public void tearDown() throws Exception { - mockCloseable.close(); - } - - @Test - public void requestScopes_ResultErrorIfAccountIsNull() { - HashMap> arguments = new HashMap<>(); - arguments.put("scopes", Collections.singletonList("requestedScope")); - MethodCall methodCall = new MethodCall("requestScopes", arguments); - when(mockGoogleSignIn.getLastSignedInAccount(mockContext)).thenReturn(null); - plugin.onMethodCall(methodCall, result); - verify(result).error("sign_in_required", "No account to grant scopes.", null); - } - - @Test - public void requestScopes_ResultTrueIfAlreadyGranted() { - HashMap> arguments = new HashMap<>(); - arguments.put("scopes", Collections.singletonList("requestedScope")); - - MethodCall methodCall = new MethodCall("requestScopes", arguments); - Scope requestedScope = new Scope("requestedScope"); - when(mockGoogleSignIn.getLastSignedInAccount(mockContext)).thenReturn(account); - when(account.getGrantedScopes()).thenReturn(Collections.singleton(requestedScope)); - when(mockGoogleSignIn.hasPermissions(account, requestedScope)).thenReturn(true); - - plugin.onMethodCall(methodCall, result); - verify(result).success(true); - } - - @Test - public void requestScopes_RequestsPermissionIfNotGranted() { - HashMap> arguments = new HashMap<>(); - arguments.put("scopes", Collections.singletonList("requestedScope")); - MethodCall methodCall = new MethodCall("requestScopes", arguments); - Scope requestedScope = new Scope("requestedScope"); - - when(mockGoogleSignIn.getLastSignedInAccount(mockContext)).thenReturn(account); - when(account.getGrantedScopes()).thenReturn(Collections.singleton(requestedScope)); - when(mockGoogleSignIn.hasPermissions(account, requestedScope)).thenReturn(false); - - plugin.onMethodCall(methodCall, result); - - verify(mockGoogleSignIn) - .requestPermissions(mockActivity, 53295, account, new Scope[] {requestedScope}); - } - - @Test - public void requestScopes_ReturnsFalseIfPermissionDenied() { - HashMap> arguments = new HashMap<>(); - arguments.put("scopes", Collections.singletonList("requestedScope")); - MethodCall methodCall = new MethodCall("requestScopes", arguments); - Scope requestedScope = new Scope("requestedScope"); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(PluginRegistry.ActivityResultListener.class); - verify(mockActivityPluginBinding).addActivityResultListener(captor.capture()); - PluginRegistry.ActivityResultListener listener = captor.getValue(); - - when(mockGoogleSignIn.getLastSignedInAccount(mockContext)).thenReturn(account); - when(account.getGrantedScopes()).thenReturn(Collections.singleton(requestedScope)); - when(mockGoogleSignIn.hasPermissions(account, requestedScope)).thenReturn(false); - - plugin.onMethodCall(methodCall, result); - listener.onActivityResult( - GoogleSignInPlugin.Delegate.REQUEST_CODE_REQUEST_SCOPE, - Activity.RESULT_CANCELED, - new Intent()); - - verify(result).success(false); - } - - @Test - public void requestScopes_ReturnsTrueIfPermissionGranted() { - HashMap> arguments = new HashMap<>(); - arguments.put("scopes", Collections.singletonList("requestedScope")); - MethodCall methodCall = new MethodCall("requestScopes", arguments); - Scope requestedScope = new Scope("requestedScope"); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(PluginRegistry.ActivityResultListener.class); - verify(mockActivityPluginBinding).addActivityResultListener(captor.capture()); - PluginRegistry.ActivityResultListener listener = captor.getValue(); - - when(mockGoogleSignIn.getLastSignedInAccount(mockContext)).thenReturn(account); - when(account.getGrantedScopes()).thenReturn(Collections.singleton(requestedScope)); - when(mockGoogleSignIn.hasPermissions(account, requestedScope)).thenReturn(false); - - plugin.onMethodCall(methodCall, result); - listener.onActivityResult( - GoogleSignInPlugin.Delegate.REQUEST_CODE_REQUEST_SCOPE, Activity.RESULT_OK, new Intent()); - - verify(result).success(true); - } - - @Test - public void requestScopes_mayBeCalledRepeatedly_ifAlreadyGranted() { - HashMap> arguments = new HashMap<>(); - arguments.put("scopes", Collections.singletonList("requestedScope")); - MethodCall methodCall = new MethodCall("requestScopes", arguments); - Scope requestedScope = new Scope("requestedScope"); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(PluginRegistry.ActivityResultListener.class); - verify(mockActivityPluginBinding).addActivityResultListener(captor.capture()); - PluginRegistry.ActivityResultListener listener = captor.getValue(); - - when(mockGoogleSignIn.getLastSignedInAccount(mockContext)).thenReturn(account); - when(account.getGrantedScopes()).thenReturn(Collections.singleton(requestedScope)); - when(mockGoogleSignIn.hasPermissions(account, requestedScope)).thenReturn(false); - - plugin.onMethodCall(methodCall, result); - listener.onActivityResult( - GoogleSignInPlugin.Delegate.REQUEST_CODE_REQUEST_SCOPE, Activity.RESULT_OK, new Intent()); - plugin.onMethodCall(methodCall, result); - listener.onActivityResult( - GoogleSignInPlugin.Delegate.REQUEST_CODE_REQUEST_SCOPE, Activity.RESULT_OK, new Intent()); - - verify(result, times(2)).success(true); - } - - @Test - public void requestScopes_mayBeCalledRepeatedly_ifNotSignedIn() { - HashMap> arguments = new HashMap<>(); - arguments.put("scopes", Collections.singletonList("requestedScope")); - MethodCall methodCall = new MethodCall("requestScopes", arguments); - Scope requestedScope = new Scope("requestedScope"); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(PluginRegistry.ActivityResultListener.class); - verify(mockActivityPluginBinding).addActivityResultListener(captor.capture()); - PluginRegistry.ActivityResultListener listener = captor.getValue(); - - when(mockGoogleSignIn.getLastSignedInAccount(mockContext)).thenReturn(null); - - plugin.onMethodCall(methodCall, result); - listener.onActivityResult( - GoogleSignInPlugin.Delegate.REQUEST_CODE_REQUEST_SCOPE, Activity.RESULT_OK, new Intent()); - plugin.onMethodCall(methodCall, result); - listener.onActivityResult( - GoogleSignInPlugin.Delegate.REQUEST_CODE_REQUEST_SCOPE, Activity.RESULT_OK, new Intent()); - - verify(result, times(2)).error("sign_in_required", "No account to grant scopes.", null); - } - - @Test(expected = IllegalStateException.class) - public void signInThrowsWithoutActivity() { - final GoogleSignInPlugin plugin = new GoogleSignInPlugin(); - plugin.initInstance( - mock(BinaryMessenger.class), mock(Context.class), mock(GoogleSignInWrapper.class)); - - plugin.onMethodCall(new MethodCall("signIn", null), null); - } - - @Test - public void signInSilentlyThatImmediatelyCompletesWithoutResultFinishesWithError() - throws ApiException { - final String clientId = "fakeClientId"; - MethodCall methodCall = buildInitMethodCall(clientId, null); - initAndAssertServerClientId(methodCall, clientId); - - ApiException exception = - new ApiException(new Status(CommonStatusCodes.SIGN_IN_REQUIRED, "Error text")); - when(mockClient.silentSignIn()).thenReturn(mockSignInTask); - when(mockSignInTask.isComplete()).thenReturn(true); - when(mockSignInTask.getResult(ApiException.class)).thenThrow(exception); - - plugin.onMethodCall(new MethodCall("signInSilently", null), result); - verify(result) - .error( - "sign_in_required", - "com.google.android.gms.common.api.ApiException: 4: Error text", - null); - } - - @Test - public void init_LoadsServerClientIdFromResources() { - final String packageName = "fakePackageName"; - final String serverClientId = "fakeServerClientId"; - final int resourceId = 1; - MethodCall methodCall = buildInitMethodCall(null, null); - when(mockContext.getPackageName()).thenReturn(packageName); - when(mockResources.getIdentifier("default_web_client_id", "string", packageName)) - .thenReturn(resourceId); - when(mockContext.getString(resourceId)).thenReturn(serverClientId); - initAndAssertServerClientId(methodCall, serverClientId); - } - - @Test - public void init_InterpretsClientIdAsServerClientId() { - final String clientId = "fakeClientId"; - MethodCall methodCall = buildInitMethodCall(clientId, null); - initAndAssertServerClientId(methodCall, clientId); - } - - @Test - public void init_ForwardsServerClientId() { - final String serverClientId = "fakeServerClientId"; - MethodCall methodCall = buildInitMethodCall(null, serverClientId); - initAndAssertServerClientId(methodCall, serverClientId); - } - - @Test - public void init_IgnoresClientIdIfServerClientIdIsProvided() { - final String clientId = "fakeClientId"; - final String serverClientId = "fakeServerClientId"; - MethodCall methodCall = buildInitMethodCall(clientId, serverClientId); - initAndAssertServerClientId(methodCall, serverClientId); - } - - @Test - public void init_PassesForceCodeForRefreshTokenFalseWithServerClientIdParameter() { - MethodCall methodCall = buildInitMethodCall("fakeClientId", "fakeServerClientId", false); - - initAndAssertForceCodeForRefreshToken(methodCall, false); - } - - @Test - public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdParameter() { - MethodCall methodCall = buildInitMethodCall("fakeClientId", "fakeServerClientId", true); - - initAndAssertForceCodeForRefreshToken(methodCall, true); - } - - @Test - public void init_PassesForceCodeForRefreshTokenFalseWithServerClientIdFromResources() { - final String packageName = "fakePackageName"; - final String serverClientId = "fakeServerClientId"; - final int resourceId = 1; - MethodCall methodCall = buildInitMethodCall(null, null, false); - when(mockContext.getPackageName()).thenReturn(packageName); - when(mockResources.getIdentifier("default_web_client_id", "string", packageName)) - .thenReturn(resourceId); - when(mockContext.getString(resourceId)).thenReturn(serverClientId); - - initAndAssertForceCodeForRefreshToken(methodCall, false); - } - - @Test - public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdFromResources() { - final String packageName = "fakePackageName"; - final String serverClientId = "fakeServerClientId"; - final int resourceId = 1; - MethodCall methodCall = buildInitMethodCall(null, null, true); - when(mockContext.getPackageName()).thenReturn(packageName); - when(mockResources.getIdentifier("default_web_client_id", "string", packageName)) - .thenReturn(resourceId); - when(mockContext.getString(resourceId)).thenReturn(serverClientId); - - initAndAssertForceCodeForRefreshToken(methodCall, true); - } - - public void initAndAssertServerClientId(MethodCall methodCall, String serverClientId) { - ArgumentCaptor optionsCaptor = - ArgumentCaptor.forClass(GoogleSignInOptions.class); - when(mockGoogleSignIn.getClient(any(Context.class), optionsCaptor.capture())) - .thenReturn(mockClient); - plugin.onMethodCall(methodCall, result); - verify(result).success(null); - Assert.assertEquals(serverClientId, optionsCaptor.getValue().getServerClientId()); - } - - public void initAndAssertForceCodeForRefreshToken( - MethodCall methodCall, boolean forceCodeForRefreshToken) { - ArgumentCaptor optionsCaptor = - ArgumentCaptor.forClass(GoogleSignInOptions.class); - when(mockGoogleSignIn.getClient(any(Context.class), optionsCaptor.capture())) - .thenReturn(mockClient); - plugin.onMethodCall(methodCall, result); - verify(result).success(null); - Assert.assertEquals( - forceCodeForRefreshToken, optionsCaptor.getValue().isForceCodeForRefreshToken()); - } - - private static MethodCall buildInitMethodCall(String clientId, String serverClientId) { - return buildInitMethodCall( - "SignInOption.standard", Collections.emptyList(), clientId, serverClientId, false); - } - - private static MethodCall buildInitMethodCall( - String clientId, String serverClientId, boolean forceCodeForRefreshToken) { - return buildInitMethodCall( - "SignInOption.standard", - Collections.emptyList(), - clientId, - serverClientId, - forceCodeForRefreshToken); - } - - private static MethodCall buildInitMethodCall( - String signInOption, - List scopes, - String clientId, - String serverClientId, - boolean forceCodeForRefreshToken) { - HashMap arguments = new HashMap<>(); - arguments.put("signInOption", signInOption); - arguments.put("scopes", scopes); - if (clientId != null) { - arguments.put("clientId", clientId); - } - if (serverClientId != null) { - arguments.put("serverClientId", serverClientId); - } - arguments.put("forceCodeForRefreshToken", forceCodeForRefreshToken); - return new MethodCall("init", arguments); - } -} diff --git a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java index 45dbae355008..03853740b4ec 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java @@ -22,7 +22,6 @@ import com.google.android.gms.common.api.Scope; import com.google.android.gms.common.api.Status; import com.google.android.gms.tasks.Task; -import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.googlesignin.Messages.FlutterError; import io.flutter.plugins.googlesignin.Messages.InitParams; import java.util.Collections; @@ -40,7 +39,6 @@ public class GoogleSignInTest { @Mock Context mockContext; @Mock Resources mockResources; @Mock Activity mockActivity; - @Mock BinaryMessenger mockMessenger; @Spy Messages.VoidResult voidResult; @Spy Messages.Result boolResult; @Spy Messages.Result userDataResult; diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index 598eeb91a9df..b47fba900599 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 6.1.33 +version: 6.1.34 environment: sdk: ^3.5.0 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj index 03e7451a827f..038c6d966d2f 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */; }; F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */; }; F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -86,6 +87,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -209,6 +211,9 @@ /* Begin PBXNativeTarget section */ 97C146ED1CF9000F007C117D /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -271,6 +276,9 @@ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastUpgradeCheck = 1510; @@ -766,6 +774,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a937250e8c07..330fa765f0d6 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj index 062eccea1799..f28d06cf1918 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/project.pbxproj @@ -244,6 +244,7 @@ 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, 71BCCDB30AA132D041139429 /* [CP] Embed Pods Frameworks */, + 869627441CAFE5E045BD46CA /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -262,7 +263,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { @@ -425,6 +426,23 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 869627441CAFE5E045BD46CA /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 9140353488a6..0091f6af4c84 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner/AppDelegate.swift b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/macos/Runner/AppDelegate.swift +++ b/packages/google_sign_in/google_sign_in_ios/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/image_picker/image_picker/example/android/app/build.gradle b/packages/image_picker/image_picker/example/android/app/build.gradle index f11653b7fa1e..40689f19a670 100755 --- a/packages/image_picker/image_picker/example/android/app/build.gradle +++ b/packages/image_picker/image_picker/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.imagepickerexample' compileSdk flutter.compileSdkVersion @@ -33,7 +31,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.imagepicker.example" minSdkVersion flutter.minSdkVersion - targetSdkVersion 28 + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/image_picker/image_picker/example/android/app/src/main/AndroidManifest.xml b/packages/image_picker/image_picker/example/android/app/src/main/AndroidManifest.xml index 543fca922e1b..10fef8981ad5 100755 --- a/packages/image_picker/image_picker/example/android/app/src/main/AndroidManifest.xml +++ b/packages/image_picker/image_picker/example/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ android:theme="@android:style/Theme.Black.NoTitleBar" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize"> diff --git a/packages/image_picker/image_picker/example/android/build.gradle b/packages/image_picker/image_picker/example/android/build.gradle index 0bed8906c094..b9db5700753a 100755 --- a/packages/image_picker/image_picker/example/android/build.gradle +++ b/packages/image_picker/image_picker/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.2' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/image_picker/image_picker/example/android/settings.gradle b/packages/image_picker/image_picker/example/android/settings.gradle index e54a7e1fd6e5..0667903d5724 100755 --- a/packages/image_picker/image_picker/example/android/settings.gradle +++ b/packages/image_picker/image_picker/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.2" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj index c25357ffd2b3..d50406fc4357 100644 --- a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 5C9513011EC38BD300040975 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -62,6 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -169,13 +171,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 4E2B287455E42F1E5CEE81C5 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -187,7 +191,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -209,6 +213,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -249,26 +256,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 4E2B287455E42F1E5CEE81C5 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/image_picker_ios/image_picker_ios_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/video_player_avfoundation/video_player_avfoundation_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/image_picker_ios_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/video_player_avfoundation_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -513,6 +500,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 7780344d358d..ee6dadc31671 100755 --- a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/project.pbxproj index 5338270879f0..738081fb3be8 100644 --- a/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; E752A8CC1D9387A266D93ED4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 686DC79A64D670F5C504FD08 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ @@ -80,6 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, E752A8CC1D9387A266D93ED4 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -184,7 +186,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 3472B1913B7D85CC661265B6 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -192,6 +193,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* image_picker_example.app */; productType = "com.apple.product-type.application"; @@ -203,7 +207,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -231,6 +235,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -292,23 +299,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 3472B1913B7D85CC661265B6 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; D4E5037333BDDE606E4946C1 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -628,6 +618,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e89dcd7e133b..71981e19af74 100644 --- a/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/image_picker/image_picker/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/image_picker/image_picker/example/macos/Runner/AppDelegate.swift b/packages/image_picker/image_picker/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/image_picker/image_picker/example/macos/Runner/AppDelegate.swift +++ b/packages/image_picker/image_picker/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 7616f8f6dce4..ada4c073ddd6 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.12+18 + +* Fixes a security issue related to improperly trusting filenames provided by a `ContentProvider`. + ## 0.8.12+17 * Bumps androidx.annotation:annotation from 1.8.2 to 1.9.0. diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java index 8afed83aaef0..b24b6e5bd9ef 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java @@ -29,6 +29,8 @@ import android.net.Uri; import android.provider.MediaStore; import android.webkit.MimeTypeMap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.Log; import java.io.File; import java.io.FileOutputStream; @@ -42,6 +44,12 @@ class FileUtils { * Copies the file from the given content URI to a temporary directory, retaining the original * file name if possible. * + *

If the filename contains path indirection or separators (.. or /), the end file name will be + * the segment after the final separator, with indirection replaced by underscores. E.g. + * "example/../..file.png" -> "_file.png". See: Improperly + * trusting ContentProvider-provided filename. + * *

Each file is placed in its own directory to avoid conflicts according to the following * scheme: {cacheDir}/{randomUuid}/{fileName} * @@ -69,10 +77,11 @@ String getPathFromUri(final Context context, final Uri uri) { } else if (extension != null) { fileName = getBaseName(fileName) + extension; } - File file = new File(targetDirectory, fileName); - try (OutputStream outputStream = new FileOutputStream(file)) { + String filePath = new File(targetDirectory, fileName).getPath(); + File outputFile = saferOpenFile(filePath, targetDirectory.getCanonicalPath()); + try (OutputStream outputStream = new FileOutputStream(outputFile)) { copy(inputStream, outputStream); - return file.getPath(); + return outputFile.getPath(); } } catch (IOException e) { // If closing the output stream fails, we cannot be sure that the @@ -86,6 +95,10 @@ String getPathFromUri(final Context context, final Uri uri) { // // See https://github.com/flutter/flutter/issues/100025 for more details. return null; + } catch (IllegalArgumentException e) { + // This is likely a result of an IllegalArgumentException that we have thrown in + // saferOpenFile(). TODO(gmackall): surface this error in dart. + return null; } } @@ -110,14 +123,49 @@ private static String getImageExtension(Context context, Uri uriImage) { return null; } - return "." + extension; + return "." + sanitizeFilename(extension); + } + + // From https://developer.android.com/privacy-and-security/risks/untrustworthy-contentprovider-provided-filename#sanitize-provided-filenames. + protected static @Nullable String sanitizeFilename(@Nullable String displayName) { + if (displayName == null) { + return null; + } + + String[] badCharacters = new String[] {"..", "/"}; + String[] segments = displayName.split("/"); + String fileName = segments[segments.length - 1]; + for (String suspString : badCharacters) { + fileName = fileName.replace(suspString, "_"); + } + return fileName; + } + + /** + * Use with file name sanitization and an non-guessable directory. From .... + */ + protected static @NonNull File saferOpenFile(@NonNull String path, @NonNull String expectedDir) + throws IllegalArgumentException, IOException { + File f = new File(path); + String canonicalPath = f.getCanonicalPath(); + if (!canonicalPath.startsWith(expectedDir)) { + throw new IllegalArgumentException( + "Trying to open path outside of the expected directory. File: " + + f.getCanonicalPath() + + " was expected to be within directory: " + + expectedDir + + "."); + } + return f; } /** @return name of the image provided by ContentResolver; this may be null. */ private static String getImageName(Context context, Uri uriImage) { try (Cursor cursor = queryImageName(context, uriImage)) { if (cursor == null || !cursor.moveToFirst() || cursor.getColumnCount() < 1) return null; - return cursor.getString(0); + String unsanitizedImageName = cursor.getString(0); + return sanitizeFilename(unsanitizedImageName); } } diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java index c11f20e228d4..5c72a30b5915 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.2), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.imagepicker; diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java index 620bac74a17b..1a33302e1fdc 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java @@ -6,6 +6,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -129,6 +131,18 @@ public void FileUtil_getImageName_mismatchedType() throws IOException { assertTrue(path.endsWith("c.d.webp")); } + @Test + public void getPathFromUri_sanitizesPathIndirection() { + Uri uri = Uri.parse(MockMaliciousContentProvider.PNG_URI); + Robolectric.buildContentProvider(MockMaliciousContentProvider.class).create("dummy"); + shadowContentResolver.registerInputStream( + uri, new ByteArrayInputStream("fileStream".getBytes(UTF_8))); + String path = fileUtils.getPathFromUri(context, uri); + assertNotNull(path); + assertTrue(path.endsWith("_bar.png")); + assertFalse(path.contains("..")); + } + @Test public void FileUtil_getImageName_unknownType() throws IOException { Uri uri = MockContentProvider.UNKNOWN_URI; @@ -193,4 +207,56 @@ public int update( return 0; } } + + // Mocks a malicious content provider attempting to use path indirection to modify files outside + // of the intended directory. + // See https://developer.android.com/privacy-and-security/risks/untrustworthy-contentprovider-provided-filename#don%27t-trust-user-input. + private static class MockMaliciousContentProvider extends ContentProvider { + public static String PNG_URI = "content://dummy/a.png"; + + @Override + public boolean onCreate() { + return true; + } + + @Nullable + @Override + public Cursor query( + @NonNull Uri uri, + @Nullable String[] projection, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String sortOrder) { + MatrixCursor cursor = new MatrixCursor(new String[] {MediaStore.MediaColumns.DISPLAY_NAME}); + cursor.addRow(new Object[] {"foo/../..bar.png"}); + return cursor; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return "image/png"; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Override + public int delete( + @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int update( + @NonNull Uri uri, + @Nullable ContentValues values, + @Nullable String selection, + @Nullable String[] selectionArgs) { + return 0; + } + } } diff --git a/packages/image_picker/image_picker_android/example/android/app/build.gradle b/packages/image_picker/image_picker_android/example/android/app/build.gradle index 8d1665a9de7e..f8a614cef52b 100755 --- a/packages/image_picker/image_picker_android/example/android/app/build.gradle +++ b/packages/image_picker/image_picker_android/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.imagepickerexample' compileSdk flutter.compileSdkVersion diff --git a/packages/image_picker/image_picker_android/example/android/build.gradle b/packages/image_picker/image_picker_android/example/android/build.gradle index 7f029821ad3d..74d56a2a7912 100755 --- a/packages/image_picker/image_picker_android/example/android/build.gradle +++ b/packages/image_picker/image_picker_android/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.1' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/image_picker/image_picker_android/example/android/settings.gradle b/packages/image_picker/image_picker_android/example/android/settings.gradle index e54a7e1fd6e5..078698fe752d 100755 --- a/packages/image_picker/image_picker_android/example/android/settings.gradle +++ b/packages/image_picker/image_picker_android/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.1" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/image_picker/image_picker_android/lib/src/messages.g.dart b/packages/image_picker/image_picker_android/lib/src/messages.g.dart index e221123fd800..cbeb6a2e61a1 100644 --- a/packages/image_picker/image_picker_android/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_android/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 605dc0c1585a..b3f294e716ae 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.12+17 +version: 0.8.12+18 environment: sdk: ^3.5.0 diff --git a/packages/image_picker/image_picker_android/test/test_api.g.dart b/packages/image_picker/image_picker_android/test/test_api.g.dart index 4343c4958436..f28bafe9ea7f 100644 --- a/packages/image_picker/image_picker_android/test/test_api.g.dart +++ b/packages/image_picker/image_picker_android/test/test_api.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj index 8e004beddfa4..34a80cc4d408 100644 --- a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 7865C5FD294157BC0010E17F /* icnsImage.icns in Resources */ = {isa = PBXBuildFile; fileRef = 7865C5FB294157BB0010E17F /* icnsImage.icns */; }; 7865C5FF294252A60010E17F /* proRawImage.dng in Resources */ = {isa = PBXBuildFile; fileRef = 7865C5FE294252A60010E17F /* proRawImage.dng */; }; 7865C600294252A60010E17F /* proRawImage.dng in Resources */ = {isa = PBXBuildFile; fileRef = 7865C5FE294252A60010E17F /* proRawImage.dng */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 78CF8D862BC5E7070051231B /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 78CF8D852BC5E7070051231B /* OCMock */; }; 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */; }; @@ -146,6 +147,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -329,13 +331,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 6BD37CAC9EAC69E93E541F56 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -380,6 +384,7 @@ ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, 78CF8D842BC5E7070051231B /* XCRemoteSwiftPackageReference "ocmock" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; @@ -459,26 +464,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 6BD37CAC9EAC69E93E541F56 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/image_picker_ios/image_picker_ios_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/video_player_avfoundation/video_player_avfoundation_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/image_picker_ios_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/video_player_avfoundation_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -888,6 +873,13 @@ }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCRemoteSwiftPackageReference section */ 78CF8D842BC5E7070051231B /* XCRemoteSwiftPackageReference "ocmock" */ = { isa = XCRemoteSwiftPackageReference; @@ -900,6 +892,10 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; 78CF8D852BC5E7070051231B /* OCMock */ = { isa = XCSwiftPackageProductDependency; package = 78CF8D842BC5E7070051231B /* XCRemoteSwiftPackageReference "ocmock" */; diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index bca5e89903bb..0a3f9563c582 100755 --- a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/project.pbxproj index d9333e4704c4..69e3b8961bfa 100644 --- a/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + BBE2B8C47A32673657F9E2DC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B40E0F19CFF2111C7C9F07D /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,7 +56,7 @@ /* Begin PBXFileReference section */ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -66,8 +68,12 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 450A6A13EFEFF8F54A1685E1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7B40E0F19CFF2111C7C9F07D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + AFE3DE60B5E9D50D0C5C0524 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + EA63AF049335E53A5E609A89 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,6 +81,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + BBE2B8C47A32673657F9E2DC /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,6 +107,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + E0DBDFAB26ECD71761DCC07A /* Pods */, ); sourceTree = ""; }; @@ -148,10 +157,22 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + 7B40E0F19CFF2111C7C9F07D /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; }; + E0DBDFAB26ECD71761DCC07A /* Pods */ = { + isa = PBXGroup; + children = ( + 450A6A13EFEFF8F54A1685E1 /* Pods-Runner.debug.xcconfig */, + EA63AF049335E53A5E609A89 /* Pods-Runner.release.xcconfig */, + AFE3DE60B5E9D50D0C5C0524 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -159,6 +180,7 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + FAD9DA6D816F3A8C6E1C0C37 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, @@ -171,6 +193,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* example.app */; productType = "com.apple.product-type.application"; @@ -182,7 +207,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -210,6 +235,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -271,6 +299,28 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + FAD9DA6D816F3A8C6E1C0C37 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -568,6 +618,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fb7259e17785..8b5a3a2814eb 100644 --- a/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/image_picker/image_picker_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/image_picker/image_picker_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/image_picker/image_picker_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16ed0f..21a3cc14c74e 100644 --- a/packages/image_picker/image_picker_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/image_picker/image_picker_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/image_picker/image_picker_macos/example/macos/Runner/AppDelegate.swift b/packages/image_picker/image_picker_macos/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/image_picker/image_picker_macos/example/macos/Runner/AppDelegate.swift +++ b/packages/image_picker/image_picker_macos/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle index af2658da94d3..6886e42efc77 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -45,14 +51,6 @@ if (!configured) { logger.error('The app could not be configured for release signing. In app purchases will not be testable. See `example/README.md` for more info and instructions.') } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.inapppurchaseexample' signingConfigs { @@ -70,7 +68,7 @@ android { defaultConfig { applicationId project.APP_ID minSdkVersion flutter.minSdkVersion - targetSdkVersion 28 + targetSdkVersion 35 versionCode project.VERSION_CODE versionName project.VERSION_NAME testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/src/main/AndroidManifest.xml b/packages/in_app_purchase/in_app_purchase/example/android/app/src/main/AndroidManifest.xml index 027375c09e04..b11a28173bf8 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/src/main/AndroidManifest.xml +++ b/packages/in_app_purchase/in_app_purchase/example/android/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize"> diff --git a/packages/in_app_purchase/in_app_purchase/example/android/build.gradle b/packages/in_app_purchase/in_app_purchase/example/android/build.gradle index 0bed8906c094..b9db5700753a 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.2' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/in_app_purchase/in_app_purchase/example/android/settings.gradle b/packages/in_app_purchase/in_app_purchase/example/android/settings.gradle index 32735a3cfd4b..0667903d5724 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/settings.gradle +++ b/packages/in_app_purchase/in_app_purchase/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.2" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj index c3302f6efa7b..ba1e08645788 100644 --- a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 861D0D93B0757D95C8A69620 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2AB6BE1D4E2232AB5D4A002 /* libPods-Runner.a */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -61,6 +62,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 861D0D93B0757D95C8A69620 /* libPods-Runner.a in Frameworks */, A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */, ); @@ -166,6 +168,9 @@ dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -177,7 +182,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -199,6 +204,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -247,12 +255,10 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/in_app_purchase_storekit/in_app_purchase_storekit_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/shared_preferences_foundation/shared_preferences_foundation_privacy.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/in_app_purchase_storekit_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/shared_preferences_foundation_privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -509,6 +515,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 9c585c83b1d5..dd5e6e00c5f9 100644 --- a/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/project.pbxproj index 924f81e05833..5b107301d60a 100644 --- a/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -80,6 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 1936C695A67BE3AC115E6938 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -174,6 +176,9 @@ /* Begin PBXNativeTarget section */ 33CC10EC2044A3C60003C045 /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -199,10 +204,13 @@ /* Begin PBXProject section */ 33CC10E52044A3C60003C045 /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -627,6 +635,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fb7259e17785..8b5a3a2814eb 100644 --- a/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/in_app_purchase/in_app_purchase/example/macos/Runner/AppDelegate.swift b/packages/in_app_purchase/in_app_purchase/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/in_app_purchase/in_app_purchase/example/macos/Runner/AppDelegate.swift +++ b/packages/in_app_purchase/in_app_purchase/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle index 9279ec8b2520..7b05c0c61206 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -45,14 +51,6 @@ if (!configured) { logger.error('The app could not be configured for release signing. In app purchases will not be testable. See `example/README.md` for more info and instructions.') } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.inapppurchaseexample' compileSdk flutter.compileSdkVersion diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle index bb2a63600c80..38f044d2b4ca 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.1' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/settings.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/settings.gradle index 32735a3cfd4b..078698fe752d 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/settings.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.1" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 3d4cc8d98378..181db40db5e6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.3.20+2 + +* Fixes price not being displayed correctly. + +## 0.3.20+1 + +* Prevent devices below iOS 15 or macOS 15 from enabling StoreKit2. + ## 0.3.20 * Fixes manual invocation of `finishTransaction` causing a fatal crash. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift index 8954f2f46b13..089f1c5ca531 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -420,6 +420,15 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, FIAInAppPurchaseAPI { NSLog("Received an updatedDownloads callback, but downloads are not supported.") } + public func supportsStoreKit2WithError(_ error: AutoreleasingUnsafeMutablePointer) + -> NSNumber? + { + if #available(iOS 15.0, macOS 12.0, *) { + return true + } + return false + } + // MARK: - Methods exposed for testing func getProduct(productID: String) -> SKProduct? { return self.productsCache[productID] as? SKProduct diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index a6059f269d73..3ec07a946280 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -67,7 +67,8 @@ typedef NS_ENUM(NSUInteger, FIASKProductDiscountTypeMessage) { typedef NS_ENUM(NSUInteger, FIASKProductDiscountPaymentModeMessage) { /// Allows user to pay the discounted price at each payment period. FIASKProductDiscountPaymentModeMessagePayAsYouGo = 0, - /// Allows user to pay the discounted price upfront and receive the product for the rest of time + /// Allows user to pay the discounted price upfront and receive the product + /// for the rest of time /// that was paid for. FIASKProductDiscountPaymentModeMessagePayUpFront = 1, /// User pays nothing during the discounted period. @@ -278,6 +279,8 @@ NSObject *FIAGetMessagesCodec(void); - (void)registerPaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error; - (void)removePaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error; - (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)supportsStoreKit2WithError:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpFIAInAppPurchaseAPI(id binaryMessenger, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 62988eb04fee..50963e67039b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" @@ -978,4 +978,26 @@ void SetUpFIAInAppPurchaseAPIWithSuffix(id binaryMesseng [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.in_app_purchase_storekit." + @"InAppPurchaseAPI.supportsStoreKit2", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FIAGetMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(supportsStoreKit2WithError:)], + @"FIAInAppPurchaseAPI api (%@) doesn't respond to @selector(supportsStoreKit2WithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSNumber *output = [api supportsStoreKit2WithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index 9d01ee6865fe..1365b55f486c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -96,6 +97,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */, E680BD031412EB2D02C9190B /* libPods-Runner.a in Frameworks */, ); @@ -238,6 +240,9 @@ dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -296,6 +301,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -337,12 +345,10 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/in_app_purchase_storekit/in_app_purchase_storekit_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/shared_preferences_foundation/shared_preferences_foundation_privacy.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/in_app_purchase_storekit_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/shared_preferences_foundation_privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -735,6 +741,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 9c3ad5d54dda..a65139025b00 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index cab8ddf0c2db..06950a977e50 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -17,6 +17,7 @@ void main() { // When using the Android plugin directly it is mandatory to register // the plugin as default instance as part of initializing the app. InAppPurchaseStoreKitPlatform.registerPlatform(); + InAppPurchaseStoreKitPlatform.enableStoreKit2(); runApp(_MyApp()); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index ce3935ab2250..772ddc4df5bf 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ F2D527262C583C1C00C137C7 /* TranslatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D527252C583C1C00C137C7 /* TranslatorTests.swift */; }; F79BDC1C2905FC3200E3999D /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC1B2905FC3200E3999D /* Stubs.m */; }; F8270DE1AEF80A8CF2C45688 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 045C0F7D19875EDA98DF0B7F /* Pods_RunnerTests.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -115,6 +116,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 00F61209EEB7954AF8415A4A /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -243,6 +245,9 @@ /* Begin PBXNativeTarget section */ 33CC10EC2044A3C60003C045 /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -287,6 +292,9 @@ /* Begin PBXProject section */ 33CC10E52044A3C60003C045 /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1400; @@ -879,6 +887,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 5eb222feadb6..24e90cc0bc0b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner/AppDelegate.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner/AppDelegate.swift index 689c0ecd5254..21fbd0297b22 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner/AppDelegate.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner/AppDelegate.swift @@ -10,4 +10,8 @@ class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index e74b46ddfc2f..51787d9c6423 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -249,8 +249,10 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { Future getCountryCode() => countryCode(); /// Turns on StoreKit2. You cannot disable this after it is enabled. - void enableStoreKit2() { - _useStoreKit2 = true; + /// This can only be enabled if your device supports StoreKit 2. + static Future enableStoreKit2() async { + _useStoreKit2 = await SKRequestMaker.supportsStoreKit2(); + return _useStoreKit2; } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart index 8679df86f749..f5938cf3c25c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -984,4 +984,33 @@ class InAppPurchaseAPI { return; } } + + Future supportsStoreKit2() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.supportsStoreKit2$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart index 9075db9157ab..64d00922d11a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart @@ -55,4 +55,9 @@ class SKRequestMaker { {Map? receiptProperties}) { return _hostApi.refreshReceipt(receiptProperties: receiptProperties); } + + /// Check if current device supports StoreKit 2. + static Future supportsStoreKit2() async { + return _hostApi.supportsStoreKit2(); + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart index f0ab7257b375..6e1a38f16610 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_product_details.dart @@ -66,7 +66,7 @@ class AppStoreProduct2Details extends ProductDetails { id: product.id, title: product.displayName, description: product.description, - price: product.priceLocale.currencySymbol + product.price.toString(), + price: product.displayPrice, rawPrice: product.price, currencyCode: product.priceLocale.currencyCode, currencySymbol: product.priceLocale.currencySymbol, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index cd7b6b70a57a..774ab69e2203 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -264,4 +264,6 @@ abstract class InAppPurchaseAPI { void removePaymentQueueDelegate(); void showPriceConsentIfNeeded(); + + bool supportsStoreKit2(); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index aa3c1d7b3e64..e7ecbc0ebc1b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.20 +version: 0.3.20+2 environment: sdk: ^3.3.0 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index 55c490bb62b4..15ea45f2a42f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -280,6 +280,11 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { void stopObservingPaymentQueue() { queueIsActive = false; } + + @override + bool supportsStoreKit2() { + return true; + } } class FakeStoreKit2Platform implements TestInAppPurchase2Api { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart index 6fb2ba030704..658932e7ce63 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_2_platform_test.dart @@ -12,6 +12,7 @@ import 'package:in_app_purchase_storekit/store_kit_2_wrappers.dart'; import 'fakes/fake_storekit_platform.dart'; import 'sk2_test_api.g.dart'; +import 'test_api.g.dart'; void main() { final SK2Product dummyProductWrapper = SK2Product( @@ -26,17 +27,20 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); final FakeStoreKit2Platform fakeStoreKit2Platform = FakeStoreKit2Platform(); + final FakeStoreKitPlatform fakeStoreKitPlatform = FakeStoreKitPlatform(); + late InAppPurchaseStoreKitPlatform iapStoreKitPlatform; setUpAll(() { TestInAppPurchase2Api.setUp(fakeStoreKit2Platform); + TestInAppPurchaseApi.setUp(fakeStoreKitPlatform); }); setUp(() { InAppPurchaseStoreKitPlatform.registerPlatform(); iapStoreKitPlatform = InAppPurchasePlatform.instance as InAppPurchaseStoreKitPlatform; - iapStoreKitPlatform.enableStoreKit2(); + InAppPurchaseStoreKitPlatform.enableStoreKit2(); fakeStoreKit2Platform.reset(); }); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart index 3bdbd0af6124..5cbdd2f9719c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/pigeon_converter_test.dart @@ -1,9 +1,10 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - import 'package:flutter_test/flutter_test.dart'; +import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_storekit/src/messages.g.dart'; +import 'package:in_app_purchase_storekit/src/store_kit_2_wrappers/sk2_product_wrapper.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; void main() { @@ -104,4 +105,24 @@ void main() { expect(wrapper.domain, 'domain'); expect(wrapper.userInfo, {}); }); + + test('test AppStoreProduct2Details conversion', () { + final SK2Product product = SK2Product( + id: '123', + displayName: 'name', + displayPrice: '0.99', + description: 'description', + price: 9.99, + type: SK2ProductType.consumable, + priceLocale: SK2PriceLocale(currencyCode: 'USD', currencySymbol: r'$')); + + final AppStoreProduct2Details details = + AppStoreProduct2Details.fromSK2Product(product); + + expect(details.sk2Product, product); + expect(details.price, product.displayPrice); + expect(details.id, product.id); + expect(details.description, product.description); + expect(details.currencySymbol, product.priceLocale.currencySymbol); + }); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index b774552f84ba..8bd00d7de933 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -304,6 +304,11 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { void showPriceConsentIfNeeded() { showPriceConsent = true; } + + @override + bool supportsStoreKit2() { + return true; + } } class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper {} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index 2092930d208d..916b37ef1baa 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v22.6.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports @@ -153,6 +153,8 @@ abstract class TestInAppPurchaseApi { void showPriceConsentIfNeeded(); + bool supportsStoreKit2(); + static void setUp( TestInAppPurchaseApi? api, { BinaryMessenger? binaryMessenger, @@ -581,5 +583,31 @@ abstract class TestInAppPurchaseApi { }); } } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.supportsStoreKit2$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + try { + final bool output = api.supportsStoreKit2(); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } } } diff --git a/packages/interactive_media_ads/CHANGELOG.md b/packages/interactive_media_ads/CHANGELOG.md index cf488dd18cce..c188eda3cd81 100644 --- a/packages/interactive_media_ads/CHANGELOG.md +++ b/packages/interactive_media_ads/CHANGELOG.md @@ -1,3 +1,19 @@ +## 0.2.3+3 + +* Adds internal wrapper for Android native `CompanionAdSlot` and `CompanionAdSlot.ClickListener`. + +## 0.2.3+2 + +* Bumps `com.google.ads.interactivemedia.v3:interactivemedia` from 3.35.1 to 3.36.0. + +## 0.2.3+1 + +* Bumps androidx.annotation:annotation from 1.8.2 to 1.9.1. + +## 0.2.3 + +* Adds parameters to control the rendering of ads. See `AdsManager.init`. + ## 0.2.2+15 * Adds remaining methods for internal wrapper of the Android native `BaseManager`. diff --git a/packages/interactive_media_ads/README.md b/packages/interactive_media_ads/README.md index d3e1817f09ba..e558e0c8ba45 100644 --- a/packages/interactive_media_ads/README.md +++ b/packages/interactive_media_ads/README.md @@ -154,7 +154,7 @@ late final AdDisplayContainer _adDisplayContainer = AdDisplayContainer( }, )); - manager.init(); + manager.init(settings: AdsRenderingSettings(enablePreloading: true)); }, onAdsLoadError: (AdsLoadErrorData data) { debugPrint('OnAdsLoadError: ${data.error.message}'); diff --git a/packages/interactive_media_ads/android/build.gradle b/packages/interactive_media_ads/android/build.gradle index 9c57c1ff7cec..2a3dc43d1aa6 100644 --- a/packages/interactive_media_ads/android/build.gradle +++ b/packages/interactive_media_ads/android/build.gradle @@ -2,7 +2,7 @@ group 'dev.flutter.packages.interactive_media_ads' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.10' repositories { google() mavenCentral() @@ -48,11 +48,11 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.8.2' - implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.35.1' + implementation 'androidx.annotation:annotation:1.9.1' + implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.36.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.jetbrains.kotlin:kotlin-test' - testImplementation "org.mockito.kotlin:mockito-kotlin:4.1.0" + testImplementation "org.mockito.kotlin:mockito-kotlin:5.4.0" testImplementation 'org.mockito:mockito-inline:5.1.0' testImplementation 'androidx.test:core:1.3.0' } diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt index 5d14b8a72db3..b4a7fa165596 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt @@ -21,7 +21,7 @@ class AdsRequestProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : * * This must match the version in pubspec.yaml. */ - const val pluginVersion = "0.2.2+15" + const val pluginVersion = "0.2.3+3" } override fun setAdTagUrl(pigeon_instance: AdsRequest, adTagUrl: String) { diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotClickListenerProxyApi.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotClickListenerProxyApi.kt new file mode 100644 index 000000000000..ce92e8eaa2f4 --- /dev/null +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotClickListenerProxyApi.kt @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package dev.flutter.packages.interactive_media_ads + +import com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener + +/** + * ProxyApi implementation for [ClickListener]. + * + *

This class may handle instantiating native object instances that are attached to a Dart + * instance or handle method calls on the associated native class or an instance of that class. + */ +class CompanionAdSlotClickListenerProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : + PigeonApiCompanionAdSlotClickListener(pigeonRegistrar) { + internal class ClickListenerImpl(val api: CompanionAdSlotClickListenerProxyApi) : ClickListener { + override fun onCompanionAdClick() { + api.pigeonRegistrar.runOnMainThread { api.onCompanionAdClick(this) {} } + } + } + + override fun pigeon_defaultConstructor(): ClickListener { + return ClickListenerImpl(this) + } +} diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApi.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApi.kt new file mode 100644 index 000000000000..ec0bdafdbd20 --- /dev/null +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApi.kt @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package dev.flutter.packages.interactive_media_ads + +import android.view.ViewGroup +import com.google.ads.interactivemedia.v3.api.CompanionAdSlot +import com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener + +/** + * ProxyApi implementation for [CompanionAdSlot]. + * + *

This class may handle instantiating native object instances that are attached to a Dart + * instance or handle method calls on the associated native class or an instance of that class. + */ +class CompanionAdSlotProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : + PigeonApiCompanionAdSlot(pigeonRegistrar) { + override fun addClickListener(pigeon_instance: CompanionAdSlot, clickListener: ClickListener) { + return pigeon_instance.addClickListener(clickListener) + } + + override fun getContainer(pigeon_instance: CompanionAdSlot): ViewGroup { + return pigeon_instance.container + } + + override fun getHeight(pigeon_instance: CompanionAdSlot): Long { + return pigeon_instance.height.toLong() + } + + override fun getWidth(pigeon_instance: CompanionAdSlot): Long { + return pigeon_instance.width.toLong() + } + + override fun isFilled(pigeon_instance: CompanionAdSlot): Boolean { + return pigeon_instance.isFilled + } + + override fun removeClickListener(pigeon_instance: CompanionAdSlot, clickListener: ClickListener) { + pigeon_instance.removeClickListener(clickListener) + } + + override fun setContainer(pigeon_instance: CompanionAdSlot, container: ViewGroup) { + pigeon_instance.container = container + } + + override fun setSize(pigeon_instance: CompanionAdSlot, width: Long, height: Long) { + pigeon_instance.setSize(width.toInt(), height.toInt()) + } +} diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApi.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApi.kt index eaed1baabe25..00ee7a23e715 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApi.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApi.kt @@ -7,6 +7,7 @@ package dev.flutter.packages.interactive_media_ads import android.view.ViewGroup import com.google.ads.interactivemedia.v3.api.AdDisplayContainer import com.google.ads.interactivemedia.v3.api.AdsLoader +import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings import com.google.ads.interactivemedia.v3.api.AdsRequest import com.google.ads.interactivemedia.v3.api.ImaSdkFactory import com.google.ads.interactivemedia.v3.api.ImaSdkSettings @@ -46,4 +47,8 @@ class ImaSdkFactoryProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : override fun createAdsRequest(pigeon_instance: ImaSdkFactory): AdsRequest { return pigeon_instance.createAdsRequest() } + + override fun createAdsRenderingSettings(pigeon_instance: ImaSdkFactory): AdsRenderingSettings { + return pigeon_instance.createAdsRenderingSettings() + } } diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt index b42ee9d2fd5b..16e53f4c9962 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsLibrary.g.kt @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.5.0), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass", "SyntheticAccessor") @@ -567,6 +567,18 @@ abstract class InteractiveMediaAdsLibraryPigeonProxyApiRegistrar( */ abstract fun getPigeonApiAd(): PigeonApiAd + /** + * An implementation of [PigeonApiCompanionAdSlotClickListener] used to add a new Dart instance of + * `CompanionAdSlotClickListener` to the Dart `InstanceManager`. + */ + abstract fun getPigeonApiCompanionAdSlotClickListener(): PigeonApiCompanionAdSlotClickListener + + /** + * An implementation of [PigeonApiCompanionAdSlot] used to add a new Dart instance of + * `CompanionAdSlot` to the Dart `InstanceManager`. + */ + abstract fun getPigeonApiCompanionAdSlot(): PigeonApiCompanionAdSlot + fun setUp() { InteractiveMediaAdsLibraryPigeonInstanceManagerApi.setUpMessageHandlers( binaryMessenger, instanceManager) @@ -592,6 +604,9 @@ abstract class InteractiveMediaAdsLibraryPigeonProxyApiRegistrar( PigeonApiAdEventListener.setUpMessageHandlers(binaryMessenger, getPigeonApiAdEventListener()) PigeonApiAdsRenderingSettings.setUpMessageHandlers( binaryMessenger, getPigeonApiAdsRenderingSettings()) + PigeonApiCompanionAdSlotClickListener.setUpMessageHandlers( + binaryMessenger, getPigeonApiCompanionAdSlotClickListener()) + PigeonApiCompanionAdSlot.setUpMessageHandlers(binaryMessenger, getPigeonApiCompanionAdSlot()) } fun tearDown() { @@ -613,6 +628,8 @@ abstract class InteractiveMediaAdsLibraryPigeonProxyApiRegistrar( PigeonApiAdErrorListener.setUpMessageHandlers(binaryMessenger, null) PigeonApiAdEventListener.setUpMessageHandlers(binaryMessenger, null) PigeonApiAdsRenderingSettings.setUpMessageHandlers(binaryMessenger, null) + PigeonApiCompanionAdSlotClickListener.setUpMessageHandlers(binaryMessenger, null) + PigeonApiCompanionAdSlot.setUpMessageHandlers(binaryMessenger, null) } } @@ -713,6 +730,10 @@ private class InteractiveMediaAdsLibraryPigeonProxyApiBaseCodec( registrar.getPigeonApiUniversalAdId().pigeon_newInstance(value) {} } else if (value is com.google.ads.interactivemedia.v3.api.Ad) { registrar.getPigeonApiAd().pigeon_newInstance(value) {} + } else if (value is com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener) { + registrar.getPigeonApiCompanionAdSlotClickListener().pigeon_newInstance(value) {} + } else if (value is com.google.ads.interactivemedia.v3.api.CompanionAdSlot) { + registrar.getPigeonApiCompanionAdSlot().pigeon_newInstance(value) {} } when { @@ -2236,6 +2257,14 @@ abstract class PigeonApiImaSdkFactory( pigeon_instance: com.google.ads.interactivemedia.v3.api.ImaSdkFactory ): com.google.ads.interactivemedia.v3.api.AdsRequest + /** + * Creates an `AdsRenderingSettings` object to give the AdsManager parameters that control the + * rendering of ads. + */ + abstract fun createAdsRenderingSettings( + pigeon_instance: com.google.ads.interactivemedia.v3.api.ImaSdkFactory + ): com.google.ads.interactivemedia.v3.api.AdsRenderingSettings + companion object { @Suppress("LocalVariableName") fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiImaSdkFactory?) { @@ -2355,6 +2384,28 @@ abstract class PigeonApiImaSdkFactory( channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.ImaSdkFactory.createAdsRenderingSettings", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = args[0] as com.google.ads.interactivemedia.v3.api.ImaSdkFactory + val wrapped: List = + try { + listOf(api.createAdsRenderingSettings(pigeon_instanceArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } } } @@ -5266,3 +5317,402 @@ abstract class PigeonApiAd( } } } +/** + * Listener interface for click events. + * + * See + * https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/CompanionAdSlot.ClickListener.html. + */ +@Suppress("UNCHECKED_CAST") +abstract class PigeonApiCompanionAdSlotClickListener( + open val pigeonRegistrar: InteractiveMediaAdsLibraryPigeonProxyApiRegistrar +) { + abstract fun pigeon_defaultConstructor(): + com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener + + companion object { + @Suppress("LocalVariableName") + fun setUpMessageHandlers( + binaryMessenger: BinaryMessenger, + api: PigeonApiCompanionAdSlotClickListener? + ) { + val codec = api?.pigeonRegistrar?.codec ?: InteractiveMediaAdsLibraryPigeonCodec() + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlotClickListener.pigeon_defaultConstructor", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_identifierArg = args[0] as Long + val wrapped: List = + try { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + api.pigeon_defaultConstructor(), pigeon_identifierArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } + + @Suppress("LocalVariableName", "FunctionName") + /** + * Creates a Dart instance of CompanionAdSlotClickListener and attaches it to + * [pigeon_instanceArg]. + */ + fun pigeon_newInstance( + pigeon_instanceArg: com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + if (pigeonRegistrar.instanceManager.containsInstance(pigeon_instanceArg)) { + Result.success(Unit) + return + } + throw IllegalStateException( + "Attempting to create a new Dart instance of CompanionAdSlotClickListener, but the class has a nonnull callback method.") + } + + /** Respond to a click on this companion ad slot. */ + fun onCompanionAdClick( + pigeon_instanceArg: com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlotClickListener.onCompanionAdClick" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_instanceArg)) { + 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))) + } + } + } +} +/** + * A companion ad slot for which the SDK should retrieve ads. + * + * See + * https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/CompanionAdSlot.html. + */ +@Suppress("UNCHECKED_CAST") +abstract class PigeonApiCompanionAdSlot( + open val pigeonRegistrar: InteractiveMediaAdsLibraryPigeonProxyApiRegistrar +) { + /** Registers a listener for companion clicks. */ + abstract fun addClickListener( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot, + clickListener: com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener + ) + + /** Returns the ViewGroup into which the companion will be rendered. */ + abstract fun getContainer( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot + ): android.view.ViewGroup + + /** Returns the height of the companion slot. */ + abstract fun getHeight( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot + ): Long + + /** Returns the width of the companion slot. */ + abstract fun getWidth( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot + ): Long + + /** Returns true if the companion slot is filled, false otherwise. */ + abstract fun isFilled( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot + ): Boolean + + /** Removes a listener for companion clicks. */ + abstract fun removeClickListener( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot, + clickListener: com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener + ) + + /** + * Sets the ViewGroup into which the companion will be rendered. + * + * Required. + */ + abstract fun setContainer( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot, + container: android.view.ViewGroup + ) + + /** + * Sets the size of the slot. + * + * Only companions matching the slot size will be displayed in the slot. + */ + abstract fun setSize( + pigeon_instance: com.google.ads.interactivemedia.v3.api.CompanionAdSlot, + width: Long, + height: Long + ) + + companion object { + @Suppress("LocalVariableName") + fun setUpMessageHandlers(binaryMessenger: BinaryMessenger, api: PigeonApiCompanionAdSlot?) { + val codec = api?.pigeonRegistrar?.codec ?: InteractiveMediaAdsLibraryPigeonCodec() + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.addClickListener", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val clickListenerArg = + args[1] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener + val wrapped: List = + try { + api.addClickListener(pigeon_instanceArg, clickListenerArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.getContainer", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val wrapped: List = + try { + listOf(api.getContainer(pigeon_instanceArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.getHeight", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val wrapped: List = + try { + listOf(api.getHeight(pigeon_instanceArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.getWidth", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val wrapped: List = + try { + listOf(api.getWidth(pigeon_instanceArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.isFilled", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val wrapped: List = + try { + listOf(api.isFilled(pigeon_instanceArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.removeClickListener", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val clickListenerArg = + args[1] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener + val wrapped: List = + try { + api.removeClickListener(pigeon_instanceArg, clickListenerArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.setContainer", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val containerArg = args[1] as android.view.ViewGroup + val wrapped: List = + try { + api.setContainer(pigeon_instanceArg, containerArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.setSize", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as com.google.ads.interactivemedia.v3.api.CompanionAdSlot + val widthArg = args[1] as Long + val heightArg = args[2] as Long + val wrapped: List = + try { + api.setSize(pigeon_instanceArg, widthArg, heightArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } + + @Suppress("LocalVariableName", "FunctionName") + /** Creates a Dart instance of CompanionAdSlot and attaches it to [pigeon_instanceArg]. */ + fun pigeon_newInstance( + pigeon_instanceArg: com.google.ads.interactivemedia.v3.api.CompanionAdSlot, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + if (pigeonRegistrar.instanceManager.containsInstance(pigeon_instanceArg)) { + Result.success(Unit) + return + } + val pigeon_identifierArg = + pigeonRegistrar.instanceManager.addHostCreatedInstance(pigeon_instanceArg) + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = "dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.pigeon_newInstance" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_identifierArg)) { + 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))) + } + } + } +} diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ProxyApiRegistrar.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ProxyApiRegistrar.kt index d1cfda322bea..c34c6242b9fc 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ProxyApiRegistrar.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/ProxyApiRegistrar.kt @@ -145,4 +145,12 @@ open class ProxyApiRegistrar(binaryMessenger: BinaryMessenger, var context: Cont override fun getPigeonApiAd(): PigeonApiAd { return AdProxyApi(this) } + + override fun getPigeonApiCompanionAdSlotClickListener(): PigeonApiCompanionAdSlotClickListener { + return CompanionAdSlotClickListenerProxyApi(this) + } + + override fun getPigeonApiCompanionAdSlot(): PigeonApiCompanionAdSlot { + return CompanionAdSlotProxyApi(this) + } } diff --git a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotClickListenerProxyApiTest.kt b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotClickListenerProxyApiTest.kt new file mode 100644 index 000000000000..8f31f8585bbe --- /dev/null +++ b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotClickListenerProxyApiTest.kt @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package dev.flutter.packages.interactive_media_ads + +import kotlin.test.Test +import kotlin.test.assertTrue +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +class CompanionAdSlotClickListenerProxyApiTest { + @Test + fun pigeon_defaultConstructor() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlotClickListener() + + assertTrue( + api.pigeon_defaultConstructor() is CompanionAdSlotClickListenerProxyApi.ClickListenerImpl) + } + + @Test + fun onCompanionAdClick() { + val mockApi = mock() + whenever(mockApi.pigeonRegistrar).thenReturn(TestProxyApiRegistrar()) + + val instance = CompanionAdSlotClickListenerProxyApi.ClickListenerImpl(mockApi) + instance.onCompanionAdClick() + + verify(mockApi).onCompanionAdClick(eq(instance), any()) + } +} diff --git a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApiTest.kt b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApiTest.kt new file mode 100644 index 000000000000..9e85e402c144 --- /dev/null +++ b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/CompanionAdSlotProxyApiTest.kt @@ -0,0 +1,104 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package dev.flutter.packages.interactive_media_ads + +import android.view.ViewGroup +import com.google.ads.interactivemedia.v3.api.CompanionAdSlot +import com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener +import kotlin.test.Test +import kotlin.test.assertEquals +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +class CompanionAdSlotProxyApiTest { + @Test + fun addClickListener() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val clickListener = mock() + api.addClickListener(instance, clickListener) + + verify(instance).addClickListener(clickListener) + } + + @Test + fun getContainer() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val value = mock() + whenever(instance.container).thenReturn(value) + + assertEquals(value, api.getContainer(instance)) + } + + @Test + fun getHeight() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val value = 0 + whenever(instance.height).thenReturn(value) + assertEquals(value.toLong(), api.getHeight(instance)) + } + + @Test + fun getWidth() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val value = 0 + whenever(instance.width).thenReturn(value) + + assertEquals(value.toLong(), api.getWidth(instance)) + } + + @Test + fun isFilled() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val value = true + whenever(instance.isFilled).thenReturn(value) + + assertEquals(value, api.isFilled(instance)) + } + + @Test + fun removeClickListener() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val clickListener = mock() + api.removeClickListener(instance, clickListener) + + verify(instance).removeClickListener(clickListener) + } + + @Test + fun setContainer() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val container = mock() + api.setContainer(instance, container) + + verify(instance).container = container + } + + @Test + fun setSize() { + val api = TestProxyApiRegistrar().getPigeonApiCompanionAdSlot() + + val instance = mock() + val width = 0L + val height = 1L + api.setSize(instance, width, height) + + verify(instance).setSize(width.toInt(), height.toInt()) + } +} diff --git a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApiTest.kt b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApiTest.kt index 600e5e41a09e..092340d5b472 100644 --- a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApiTest.kt +++ b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/ImaSdkFactoryProxyApiTest.kt @@ -6,6 +6,7 @@ package dev.flutter.packages.interactive_media_ads import com.google.ads.interactivemedia.v3.api.AdDisplayContainer import com.google.ads.interactivemedia.v3.api.AdsLoader +import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings import com.google.ads.interactivemedia.v3.api.AdsRequest import com.google.ads.interactivemedia.v3.api.ImaSdkFactory import com.google.ads.interactivemedia.v3.api.ImaSdkSettings @@ -51,4 +52,15 @@ class ImaSdkFactoryProxyApiTest { assertEquals(mockRequest, api.createAdsRequest(instance)) } + + @Test + fun createAdsRenderingSettings() { + val api = TestProxyApiRegistrar().getPigeonApiImaSdkFactory() + + val instance = mock() + val mockSettings = mock() + whenever(instance.createAdsRenderingSettings()).thenReturn(mockSettings) + + assertEquals(mockSettings, api.createAdsRenderingSettings(instance)) + } } diff --git a/packages/interactive_media_ads/example/android/settings.gradle b/packages/interactive_media_ads/example/android/settings.gradle index 078698fe752d..885c93847df2 100644 --- a/packages/interactive_media_ads/example/android/settings.gradle +++ b/packages/interactive_media_ads/example/android/settings.gradle @@ -20,7 +20,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "8.5.1" apply false - id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "org.jetbrains.kotlin.android" version "1.9.10" apply false id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj index b37fd7b5a413..06a08b5836d1 100644 --- a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 3D282E63E10407DBACF20FD6 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 544903031C10E0FA3D89D2CE /* Pods_RunnerTests.framework */; }; 4E107A500F4C07EF26C07FFF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDCBAC040E6F614A1CDCF896 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 8F599BB12C2DD1FD0090A0DF /* AdsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F599BB02C2DD1FD0090A0DF /* AdsManagerTests.swift */; }; 8F599BB32C2DD87D0090A0DF /* AdsLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F599BB22C2DD87D0090A0DF /* AdsLoaderTests.swift */; }; 8F599BB52C2DD8EC0090A0DF /* AdsLoaderDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F599BB42C2DD8EC0090A0DF /* AdsLoaderDelegateTests.swift */; }; @@ -114,6 +115,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 4E107A500F4C07EF26C07FFF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -248,13 +250,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - F9AB3D76E1CFC93475F15A54 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -288,6 +292,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -395,23 +402,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - F9AB3D76E1CFC93475F15A54 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -793,6 +783,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8e3ca5dfe193..d795332e1b7b 100644 --- a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/lib/main.dart b/packages/interactive_media_ads/example/lib/main.dart index 01cdb603dc01..49c080f7e54c 100644 --- a/packages/interactive_media_ads/example/lib/main.dart +++ b/packages/interactive_media_ads/example/lib/main.dart @@ -100,7 +100,7 @@ class _AdExampleWidgetState extends State }, )); - manager.init(); + manager.init(settings: AdsRenderingSettings(enablePreloading: true)); }, onAdsLoadError: (AdsLoadErrorData data) { debugPrint('OnAdsLoadError: ${data.error.message}'); diff --git a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift index de4a711123da..7b00f9c7f95d 100644 --- a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift +++ b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift @@ -13,7 +13,7 @@ class AdsRequestProxyAPIDelegate: PigeonApiDelegateIMAAdsRequest { /// The current version of the `interactive_media_ads` plugin. /// /// This must match the version in pubspec.yaml. - static let pluginVersion = "0.2.2+15" + static let pluginVersion = "0.2.3+3" func pigeonDefaultConstructor( pigeonApi: PigeonApiIMAAdsRequest, adTagUrl: String, adDisplayContainer: IMAAdDisplayContainer, diff --git a/packages/interactive_media_ads/lib/interactive_media_ads.dart b/packages/interactive_media_ads/lib/interactive_media_ads.dart index 06c180248410..f8355bba41c8 100644 --- a/packages/interactive_media_ads/lib/interactive_media_ads.dart +++ b/packages/interactive_media_ads/lib/interactive_media_ads.dart @@ -5,6 +5,7 @@ export 'src/ad_display_container.dart'; export 'src/ads_loader.dart'; export 'src/ads_manager_delegate.dart'; +export 'src/ads_rendering_settings.dart'; export 'src/ads_request.dart'; export 'src/android/android_interactive_media_ads.dart' show AndroidInteractiveMediaAds; @@ -18,4 +19,5 @@ export 'src/platform_interface/platform_interface.dart' AdErrorType, AdEvent, AdEventType, + AdUIElement, AdsLoadErrorData; diff --git a/packages/interactive_media_ads/lib/src/ads_loader.dart b/packages/interactive_media_ads/lib/src/ads_loader.dart index 9863d4c01293..4d05b6db34df 100644 --- a/packages/interactive_media_ads/lib/src/ads_loader.dart +++ b/packages/interactive_media_ads/lib/src/ads_loader.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'ad_display_container.dart'; import 'ads_manager_delegate.dart'; +import 'ads_rendering_settings.dart'; import 'ads_request.dart'; import 'platform_interface/platform_interface.dart'; @@ -145,8 +146,8 @@ class AdsManager { final PlatformAdsManager platform; /// Initializes the ad experience using default rendering settings. - Future init() { - return platform.init(AdsManagerInitParams()); + Future init({AdsRenderingSettings? settings}) { + return platform.init(settings: settings?.platform); } /// Starts playing the ads. diff --git a/packages/interactive_media_ads/lib/src/ads_rendering_settings.dart b/packages/interactive_media_ads/lib/src/ads_rendering_settings.dart new file mode 100644 index 000000000000..ecae23f404ac --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ads_rendering_settings.dart @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'ads_loader.dart'; +import 'platform_interface/platform_interface.dart'; + +/// Defines parameters that control the rendering of ads. +class AdsRenderingSettings { + /// Creates an [AdsRenderingSettings]. + AdsRenderingSettings({ + int? bitrate, + bool? enablePreloading, + Duration loadVideoTimeout = const Duration(seconds: 8), + List? mimeTypes, + Duration? playAdsAfterTime, + Set? uiElements, + }) : this.fromPlatform(PlatformAdsRenderingSettings( + PlatformAdsRenderingSettingsCreationParams( + bitrate: bitrate, + enablePreloading: enablePreloading, + loadVideoTimeout: loadVideoTimeout, + mimeTypes: mimeTypes, + playAdsAfterTime: playAdsAfterTime, + uiElements: uiElements, + ), + )); + + /// Constructs an [AdsRenderingSettings] from a specific platform + /// implementation. + AdsRenderingSettings.fromPlatform(this.platform); + + /// Implementation of [PlatformAdsRenderingSettings] for the current platform. + final PlatformAdsRenderingSettings platform; + + /// Maximum recommended bitrate. + /// + /// The value is in kbit/s. + /// + /// The SDK will select media which has a bitrate below the specified max or + /// the closest bitrate if there is no media with a lower bitrate found. + /// + /// If null, the bitrate will be selected by the SDK, using the currently + /// detected network speed (cellular or Wi-Fi). + int? get bitrate => platform.params.bitrate; + + /// If set, the SDK will instruct the player to load the creative in response + /// to [AdsManager.init]. + /// + /// This allows the player to preload the ad at any point before + /// [AdsManager.start]. + /// + /// If null, the platform will decide the default value. + bool? get enablePreloading => platform.params.enablePreloading; + + /// Specifies a non-default amount of time to wait for media to load before + /// timing out. + /// + /// This only applies to the IMA client-side SDK. + Duration get loadVideoTimeout => platform.params.loadVideoTimeout; + + /// The SDK will prioritize the media with MIME type on the list. + /// + /// This only refers to the mime types of videos to be selected for linear + /// ads. + /// + /// If null, the platform will decide the default value. + List? get mimeTypes => platform.params.mimeTypes; + + /// For VMAP and ad rules playlists, only play ad breaks scheduled after this + /// time. + /// + /// This setting is strictly after the specified time. For example, setting + /// `playAdsAfterTime` to 15s will ignore an ad break scheduled to play at + /// 15s. + Duration? get playAdsAfterTime => platform.params.playAdsAfterTime; + + /// The ad UI elements to be rendered by the IMA SDK. + /// + /// Some modifications to the uiElements list may have no effect for specific + /// ads. + /// + /// If null, the platform will decide the default value. + Set? get uiElements => platform.params.uiElements; +} diff --git a/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart b/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart index fbba105e4e27..ad1953bfc786 100644 --- a/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart +++ b/packages/interactive_media_ads/lib/src/android/android_ads_manager.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; import '../platform_interface/platform_interface.dart'; +import 'android_ads_rendering_settings.dart'; import 'enum_converter_utils.dart'; import 'interactive_media_ads.g.dart' as ima; import 'interactive_media_ads_proxy.dart'; @@ -32,8 +33,15 @@ class AndroidAdsManager extends PlatformAdsManager { } @override - Future init(AdsManagerInitParams params) { - return _manager.init(null); + Future init({PlatformAdsRenderingSettings? settings}) async { + ima.AdsRenderingSettings? nativeSettings; + if (settings != null) { + nativeSettings = settings is AndroidAdsRenderingSettings + ? await settings.nativeSettings + : await AndroidAdsRenderingSettings(settings.params).nativeSettings; + } + + await _manager.init(nativeSettings); } @override diff --git a/packages/interactive_media_ads/lib/src/android/android_ads_rendering_settings.dart b/packages/interactive_media_ads/lib/src/android/android_ads_rendering_settings.dart new file mode 100644 index 000000000000..581ae3ff86cb --- /dev/null +++ b/packages/interactive_media_ads/lib/src/android/android_ads_rendering_settings.dart @@ -0,0 +1,113 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:meta/meta.dart'; + +import '../platform_interface/platform_interface.dart'; +import 'interactive_media_ads.g.dart' as ima; +import 'interactive_media_ads_proxy.dart'; + +/// Android implementation of [PlatformAdsRenderingSettingsCreationParams]. +final class AndroidAdsRenderingSettingsCreationParams + extends PlatformAdsRenderingSettingsCreationParams { + /// Constructs an [AndroidAdsRenderingSettingsCreationParams]. + const AndroidAdsRenderingSettingsCreationParams({ + super.bitrate, + super.enablePreloading, + super.loadVideoTimeout, + super.mimeTypes, + super.playAdsAfterTime, + super.uiElements, + this.enableCustomTabs = false, + @visibleForTesting InteractiveMediaAdsProxy? proxy, + }) : _proxy = proxy ?? const InteractiveMediaAdsProxy(), + super(); + + /// Creates a [AndroidAdsRenderingSettingsCreationParams] from an instance of + /// [PlatformAdsRenderingSettingsCreationParams]. + factory AndroidAdsRenderingSettingsCreationParams.fromPlatformAdsRenderingSettingsCreationParams( + PlatformAdsRenderingSettingsCreationParams params, { + bool enableCustomTabs = false, + }) { + return AndroidAdsRenderingSettingsCreationParams( + bitrate: params.bitrate, + enablePreloading: params.enablePreloading, + loadVideoTimeout: params.loadVideoTimeout, + mimeTypes: params.mimeTypes, + playAdsAfterTime: params.playAdsAfterTime, + uiElements: params.uiElements, + enableCustomTabs: enableCustomTabs, + ); + } + + final InteractiveMediaAdsProxy _proxy; + + /// Notifies the SDK whether to launch the click-through URL using Custom Tabs + /// feature. + final bool enableCustomTabs; +} + +/// Android implementation of [PlatformAdsRenderingSettings]. +base class AndroidAdsRenderingSettings extends PlatformAdsRenderingSettings { + /// Constructs an [AndroidAdsRenderingSettings]. + AndroidAdsRenderingSettings(super.params) : super.implementation() { + final Completer nativeSettingsCompleter = + Completer(); + nativeSettings = nativeSettingsCompleter.future; + + _androidParams._proxy + .instanceImaSdkFactory() + .createAdsRenderingSettings() + .then((ima.AdsRenderingSettings nativeSettings) async { + await Future.wait(>[ + if (_androidParams.bitrate != null) + nativeSettings.setBitrateKbps(params.bitrate!), + if (_androidParams.enablePreloading != null) + nativeSettings.setEnablePreloading(_androidParams.enablePreloading!), + nativeSettings.setLoadVideoTimeout( + _androidParams.loadVideoTimeout.inMilliseconds, + ), + if (_androidParams.mimeTypes != null) + nativeSettings.setMimeTypes(_androidParams.mimeTypes!), + if (_androidParams.playAdsAfterTime != null) + nativeSettings.setPlayAdsAfterTime( + _androidParams.playAdsAfterTime!.inMicroseconds / + Duration.microsecondsPerSecond, + ), + if (_androidParams.uiElements != null) + nativeSettings.setUiElements( + _androidParams.uiElements!.map( + (AdUIElement element) { + return switch (element) { + AdUIElement.adAttribution => ima.UiElement.adAttribution, + AdUIElement.countdown => ima.UiElement.countdown, + }; + }, + ).toList(), + ), + nativeSettings.setEnableCustomTabs(_androidParams.enableCustomTabs) + ]); + + nativeSettingsCompleter.complete(nativeSettings); + }); + } + + /// The native Android AdsRenderingSettings. + /// + /// The instantiation of the native AdsRenderingSettings is asynchronous, so + /// this provides access to the value after it is created and all params have + /// been set. + @internal + late final Future nativeSettings; + + late final AndroidAdsRenderingSettingsCreationParams _androidParams = + params is AndroidAdsRenderingSettingsCreationParams + ? params as AndroidAdsRenderingSettingsCreationParams + : AndroidAdsRenderingSettingsCreationParams + .fromPlatformAdsRenderingSettingsCreationParams( + params, + ); +} diff --git a/packages/interactive_media_ads/lib/src/android/android_interactive_media_ads.dart b/packages/interactive_media_ads/lib/src/android/android_interactive_media_ads.dart index 0f05362a6919..8e77520a07f6 100644 --- a/packages/interactive_media_ads/lib/src/android/android_interactive_media_ads.dart +++ b/packages/interactive_media_ads/lib/src/android/android_interactive_media_ads.dart @@ -6,10 +6,12 @@ import '../platform_interface/interactive_media_ads_platform.dart'; import '../platform_interface/platform_ad_display_container.dart'; import '../platform_interface/platform_ads_loader.dart'; import '../platform_interface/platform_ads_manager_delegate.dart'; +import '../platform_interface/platform_ads_rendering_settings.dart'; import '../platform_interface/platform_content_progress_provider.dart'; import 'android_ad_display_container.dart'; import 'android_ads_loader.dart'; import 'android_ads_manager_delegate.dart'; +import 'android_ads_rendering_settings.dart'; import 'android_content_progress_provider.dart'; /// Android implementation of [InteractiveMediaAdsPlatform]. @@ -46,4 +48,11 @@ final class AndroidInteractiveMediaAds extends InteractiveMediaAdsPlatform { ) { return AndroidContentProgressProvider(params); } + + @override + AndroidAdsRenderingSettings createPlatformAdsRenderingSettings( + PlatformAdsRenderingSettingsCreationParams params, + ) { + return AndroidAdsRenderingSettings(params); + } } diff --git a/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart b/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart index 330cf444e2f6..9291453b86a7 100644 --- a/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart +++ b/packages/interactive_media_ads/lib/src/android/interactive_media_ads.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.5.0), do not edit directly. +// Autogenerated from Pigeon (v22.6.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -198,6 +198,10 @@ class PigeonInstanceManager { UniversalAdId.pigeon_setUpMessageHandlers( pigeon_instanceManager: instanceManager); Ad.pigeon_setUpMessageHandlers(pigeon_instanceManager: instanceManager); + CompanionAdSlotClickListener.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + CompanionAdSlot.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); return instanceManager; } @@ -2482,6 +2486,40 @@ class ImaSdkFactory extends PigeonInternalProxyApiBaseClass { } } + /// Creates an `AdsRenderingSettings` object to give the AdsManager parameters + /// that control the rendering of ads. + Future createAdsRenderingSettings() async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecImaSdkFactory; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.ImaSdkFactory.createAdsRenderingSettings'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([this]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as AdsRenderingSettings?)!; + } + } + @override ImaSdkFactory pigeon_copy() { return ImaSdkFactory.pigeon_detached( @@ -6393,3 +6431,461 @@ class Ad extends PigeonInternalProxyApiBaseClass { ); } } + +/// Listener interface for click events. +/// +/// See https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/CompanionAdSlot.ClickListener.html. +class CompanionAdSlotClickListener extends PigeonInternalProxyApiBaseClass { + CompanionAdSlotClickListener({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.onCompanionAdClick, + }) { + final int pigeonVar_instanceIdentifier = + pigeon_instanceManager.addDartCreatedInstance(this); + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlotClickListener; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + () async { + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlotClickListener.pigeon_defaultConstructor'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([pigeonVar_instanceIdentifier]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + }(); + } + + /// Constructs [CompanionAdSlotClickListener] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + CompanionAdSlotClickListener.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.onCompanionAdClick, + }); + + late final _PigeonInternalProxyApiBaseCodec + _pigeonVar_codecCompanionAdSlotClickListener = + _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + + /// Respond to a click on this companion ad slot. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final CompanionAdSlotClickListener instance = CompanionAdSlotClickListener( + /// onCompanionAdClick: (CompanionAdSlotClickListener pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final void Function(CompanionAdSlotClickListener pigeon_instance) + onCompanionAdClick; + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + void Function(CompanionAdSlotClickListener pigeon_instance)? + onCompanionAdClick, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlotClickListener.onCompanionAdClick', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.interactive_media_ads.CompanionAdSlotClickListener.onCompanionAdClick was null.'); + final List args = (message as List?)!; + final CompanionAdSlotClickListener? arg_pigeon_instance = + (args[0] as CompanionAdSlotClickListener?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.interactive_media_ads.CompanionAdSlotClickListener.onCompanionAdClick was null, expected non-null CompanionAdSlotClickListener.'); + try { + (onCompanionAdClick ?? arg_pigeon_instance!.onCompanionAdClick) + .call(arg_pigeon_instance!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + @override + CompanionAdSlotClickListener pigeon_copy() { + return CompanionAdSlotClickListener.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + onCompanionAdClick: onCompanionAdClick, + ); + } +} + +/// A companion ad slot for which the SDK should retrieve ads. +/// +/// See https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/CompanionAdSlot.html. +class CompanionAdSlot extends PigeonInternalProxyApiBaseClass { + /// Constructs [CompanionAdSlot] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + CompanionAdSlot.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + }); + + late final _PigeonInternalProxyApiBaseCodec _pigeonVar_codecCompanionAdSlot = + _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + CompanionAdSlot Function()? pigeon_newInstance, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + CompanionAdSlot.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + /// Registers a listener for companion clicks. + Future addClickListener( + CompanionAdSlotClickListener clickListener) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.addClickListener'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, clickListener]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Returns the ViewGroup into which the companion will be rendered. + Future getContainer() async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.getContainer'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([this]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as ViewGroup?)!; + } + } + + /// Returns the height of the companion slot. + Future getHeight() async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.getHeight'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([this]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as int?)!; + } + } + + /// Returns the width of the companion slot. + Future getWidth() async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.getWidth'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([this]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as int?)!; + } + } + + /// Returns true if the companion slot is filled, false otherwise. + Future isFilled() async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.isFilled'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([this]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + /// Removes a listener for companion clicks. + Future removeClickListener( + CompanionAdSlotClickListener clickListener) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.removeClickListener'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, clickListener]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Sets the ViewGroup into which the companion will be rendered. + /// + /// Required. + Future setContainer(ViewGroup container) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.setContainer'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, container]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Sets the size of the slot. + /// + /// Only companions matching the slot size will be displayed in the slot. + Future setSize( + int width, + int height, + ) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecCompanionAdSlot; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.interactive_media_ads.CompanionAdSlot.setSize'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, width, height]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + @override + CompanionAdSlot pigeon_copy() { + return CompanionAdSlot.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ); + } +} diff --git a/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart b/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart index 0c51bf926f14..9d8c1c5ac4ce 100644 --- a/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart +++ b/packages/interactive_media_ads/lib/src/ios/ios_ads_manager.dart @@ -9,6 +9,7 @@ import 'package:meta/meta.dart'; import '../platform_interface/platform_interface.dart'; import 'interactive_media_ads.g.dart'; import 'ios_ads_manager_delegate.dart'; +import 'ios_ads_rendering_settings.dart'; /// Implementation of [PlatformAdsManager] for iOS. class IOSAdsManager extends PlatformAdsManager { @@ -30,8 +31,15 @@ class IOSAdsManager extends PlatformAdsManager { } @override - Future init(AdsManagerInitParams params) { - return _manager.initialize(null); + Future init({PlatformAdsRenderingSettings? settings}) { + IMAAdsRenderingSettings? nativeSettings; + if (settings != null) { + nativeSettings = settings is IOSAdsRenderingSettings + ? settings.nativeSettings + : IOSAdsRenderingSettings(settings.params).nativeSettings; + } + + return _manager.initialize(nativeSettings); } @override diff --git a/packages/interactive_media_ads/lib/src/ios/ios_ads_rendering_settings.dart b/packages/interactive_media_ads/lib/src/ios/ios_ads_rendering_settings.dart new file mode 100644 index 000000000000..6ce5048ec828 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ios/ios_ads_rendering_settings.dart @@ -0,0 +1,93 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:meta/meta.dart'; + +import '../platform_interface/platform_interface.dart'; +import 'interactive_media_ads.g.dart'; +import 'interactive_media_ads_proxy.dart'; + +/// Implementation of [PlatformAdsRenderingSettingsCreationParams] for iOS. +final class IOSAdsRenderingSettingsCreationParams + extends PlatformAdsRenderingSettingsCreationParams { + /// Constructs an [IOSAdsRenderingSettingsCreationParams]. + const IOSAdsRenderingSettingsCreationParams({ + super.bitrate, + super.enablePreloading, + super.loadVideoTimeout, + super.mimeTypes, + super.playAdsAfterTime, + super.uiElements, + @visibleForTesting InteractiveMediaAdsProxy? proxy, + }) : _proxy = proxy ?? const InteractiveMediaAdsProxy(), + super(); + + /// Creates a [IOSAdsRenderingSettingsCreationParams] from an instance of + /// [PlatformAdsRenderingSettingsCreationParams]. + factory IOSAdsRenderingSettingsCreationParams.fromPlatformAdsRenderingSettingsCreationParams( + PlatformAdsRenderingSettingsCreationParams params, + ) { + return IOSAdsRenderingSettingsCreationParams( + bitrate: params.bitrate, + enablePreloading: params.enablePreloading, + loadVideoTimeout: params.loadVideoTimeout, + mimeTypes: params.mimeTypes, + playAdsAfterTime: params.playAdsAfterTime, + uiElements: params.uiElements, + ); + } + + final InteractiveMediaAdsProxy _proxy; +} + +/// Implementation of [PlatformAdsRenderingSettings] for iOS. +base class IOSAdsRenderingSettings extends PlatformAdsRenderingSettings { + /// Constructs an [IOSAdsRenderingSettings]. + IOSAdsRenderingSettings(super.params) : super.implementation() { + if (_iosParams.bitrate != null) { + nativeSettings.setBitrate(params.bitrate!); + } + if (_iosParams.enablePreloading != null) { + nativeSettings.setEnablePreloading(_iosParams.enablePreloading!); + } + nativeSettings.setLoadVideoTimeout( + _iosParams.loadVideoTimeout.inMicroseconds / + Duration.microsecondsPerSecond, + ); + if (_iosParams.mimeTypes != null) { + nativeSettings.setMimeTypes(_iosParams.mimeTypes); + } + if (_iosParams.playAdsAfterTime != null) { + nativeSettings.setPlayAdsAfterTime( + _iosParams.playAdsAfterTime!.inMicroseconds / + Duration.microsecondsPerSecond, + ); + } + if (_iosParams.uiElements != null) { + nativeSettings.setUIElements( + _iosParams.uiElements!.map( + (AdUIElement element) { + return switch (element) { + AdUIElement.adAttribution => UIElementType.adAttribution, + AdUIElement.countdown => UIElementType.countdown, + }; + }, + ).toList(), + ); + } + } + + /// The native iOS IMAAdsRenderingSettings. + @internal + late final IMAAdsRenderingSettings nativeSettings = + _iosParams._proxy.newIMAAdsRenderingSettings(); + + late final IOSAdsRenderingSettingsCreationParams _iosParams = + params is IOSAdsRenderingSettingsCreationParams + ? params as IOSAdsRenderingSettingsCreationParams + : IOSAdsRenderingSettingsCreationParams + .fromPlatformAdsRenderingSettingsCreationParams( + params, + ); +} diff --git a/packages/interactive_media_ads/lib/src/ios/ios_interactive_media_ads.dart b/packages/interactive_media_ads/lib/src/ios/ios_interactive_media_ads.dart index ee55ecce0c98..b79928c57a06 100644 --- a/packages/interactive_media_ads/lib/src/ios/ios_interactive_media_ads.dart +++ b/packages/interactive_media_ads/lib/src/ios/ios_interactive_media_ads.dart @@ -6,10 +6,12 @@ import '../platform_interface/interactive_media_ads_platform.dart'; import '../platform_interface/platform_ad_display_container.dart'; import '../platform_interface/platform_ads_loader.dart'; import '../platform_interface/platform_ads_manager_delegate.dart'; +import '../platform_interface/platform_ads_rendering_settings.dart'; import '../platform_interface/platform_content_progress_provider.dart'; import 'ios_ad_display_container.dart'; import 'ios_ads_loader.dart'; import 'ios_ads_manager_delegate.dart'; +import 'ios_ads_rendering_settings.dart'; import 'ios_content_progress_provider.dart'; /// Implementation of [InteractiveMediaAdsPlatform] for iOS. @@ -44,4 +46,11 @@ final class IOSInteractiveMediaAds extends InteractiveMediaAdsPlatform { ) { return IOSContentProgressProvider(params); } + + @override + IOSAdsRenderingSettings createPlatformAdsRenderingSettings( + PlatformAdsRenderingSettingsCreationParams params, + ) { + return IOSAdsRenderingSettings(params); + } } diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ad_ui_element.dart b/packages/interactive_media_ads/lib/src/platform_interface/ad_ui_element.dart new file mode 100644 index 000000000000..3d7357f1ac02 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/ad_ui_element.dart @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Describes an element of the ad UI, to be requested or rendered by the SDK. +enum AdUIElement { + /// The ad attribution UI element, for example, "Ad". + adAttribution, + + /// Ad attribution is required for a countdown timer to be displayed. + countdown, +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart b/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart index fdb8593bdcb4..15b898e462c6 100644 --- a/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart +++ b/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart @@ -5,6 +5,7 @@ import 'platform_ad_display_container.dart'; import 'platform_ads_loader.dart'; import 'platform_ads_manager_delegate.dart'; +import 'platform_ads_rendering_settings.dart'; import 'platform_content_progress_provider.dart'; /// Interface for a platform implementation of the Interactive Media Ads SDKs. @@ -35,4 +36,9 @@ abstract base class InteractiveMediaAdsPlatform { PlatformContentProgressProvider createPlatformContentProgressProvider( PlatformContentProgressProviderCreationParams params, ); + + /// Creates a new [PlatformContentProgressProvider]. + PlatformAdsRenderingSettings createPlatformAdsRenderingSettings( + PlatformAdsRenderingSettingsCreationParams params, + ); } diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart index 0f3d7a7ea46f..ea73ff7268ec 100644 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart @@ -5,9 +5,7 @@ import 'package:flutter/foundation.dart'; import 'platform_ads_manager_delegate.dart'; - -/// Additional parameter passed to an [PlatformAdsManager] on initialization. -base class AdsManagerInitParams {} +import 'platform_ads_rendering_settings.dart'; /// Additional parameter passed to an [PlatformAdsManager] when starting to play /// ads. @@ -20,7 +18,7 @@ abstract class PlatformAdsManager { PlatformAdsManager(); /// Initializes the ad experience using default rendering settings. - Future init(AdsManagerInitParams params); + Future init({PlatformAdsRenderingSettings? settings}); /// Starts playing the ads. Future start(AdsManagerStartParams params); diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_rendering_settings.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_rendering_settings.dart new file mode 100644 index 000000000000..882baa4e9585 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_rendering_settings.dart @@ -0,0 +1,139 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import 'ad_ui_element.dart'; +import 'interactive_media_ads_platform.dart'; + +/// Object specifying creation parameters for creating a +/// [PlatformAdsRenderingSettings]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the +/// [PlatformAdsRenderingSettingsCreationParams] to provide additional platform +/// specific parameters. +/// +/// When extending [PlatformAdsRenderingSettingsCreationParams], additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// final class AndroidPlatformAdsRenderingSettingsCreationParams +/// extends PlatformAdsRenderingSettingsCreationParams { +/// const AndroidPlatformAdsRenderingSettingsCreationParams({ +/// super.bitrate, +/// this.enableCustomTabs, +/// }) : super(); +/// +/// factory AndroidPlatformAdsRenderingSettingsCreationParams.fromPlatformAdsRenderingSettingsCreationParams( +/// PlatformAdsRenderingSettingsCreationParams params, { +/// bool? enableCustomTabs, +/// }) { +/// return AndroidPlatformAdsRenderingSettingsCreationParams( +/// bitrate: params.bitrate, +/// enableCustomTabs: enableCustomTabs, +/// ); +/// } +/// +/// final bool? enableCustomTabs; +/// } +/// ``` +@immutable +base class PlatformAdsRenderingSettingsCreationParams { + /// Used by the platform implementation to create a new + /// [PlatformAdsRenderingSettings]. + const PlatformAdsRenderingSettingsCreationParams({ + this.bitrate, + this.enablePreloading, + this.loadVideoTimeout = const Duration(seconds: 8), + this.mimeTypes, + this.playAdsAfterTime, + this.uiElements, + }); + + /// Maximum recommended bitrate. + /// + /// The value is in kbit/s. + /// + /// The SDK will select media which has a bitrate below the specified max or + /// the closest bitrate if there is no media with a lower bitrate found. + /// + /// If null, the bitrate will be selected by the SDK, using the currently + /// detected network speed (cellular or Wi-Fi). + final int? bitrate; + + /// If set, the SDK will instruct the player to load the creative in response + /// initializing the ads manager. + /// + /// This allows the player to preload the ad at any point before starting the + /// ads manager. + /// + /// If null, the platform will decide the default value. + final bool? enablePreloading; + + /// Specifies a non-default amount of time to wait for media to load before + /// timing out. + /// + /// This only applies to the IMA client-side SDK. + final Duration loadVideoTimeout; + + /// The SDK will prioritize the media with MIME type on the list. + /// + /// This only refers to the mime types of videos to be selected for linear + /// ads. + /// + /// If null, the platform will decide the default value. + final List? mimeTypes; + + /// For VMAP and ad rules playlists, only play ad breaks scheduled after this + /// time. + /// + /// This setting is strictly after the specified time. For example, setting + /// `playAdsAfterTime` to 15s will ignore an ad break scheduled to play at + /// 15s. + final Duration? playAdsAfterTime; + + /// Sets the ad UI elements to be rendered by the IMA SDK. + /// + /// Some modifications to the uiElements set may have no effect for specific + /// ads. + /// + /// If null, the platform will decide the default value. + final Set? uiElements; +} + +/// Defines parameters that control the rendering of ads. +abstract base class PlatformAdsRenderingSettings { + /// Creates a new [PlatformAdsRenderingSettings] + factory PlatformAdsRenderingSettings( + PlatformAdsRenderingSettingsCreationParams params, + ) { + assert( + InteractiveMediaAdsPlatform.instance != null, + 'A platform implementation for `interactive_media_ads` has not been set. ' + 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' + 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' + 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' + 'your own test implementation.', + ); + final PlatformAdsRenderingSettings implementation = + InteractiveMediaAdsPlatform.instance! + .createPlatformAdsRenderingSettings(params); + return implementation; + } + + /// Used by the platform implementation to create a new + /// [PlatformAdsRenderingSettings]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformAdsRenderingSettings.implementation(this.params); + + /// The parameters used to initialize the [PlatformAdsRenderingSettings]. + final PlatformAdsRenderingSettingsCreationParams params; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart index fad06fe592f5..f2b60b7618fc 100644 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart @@ -4,10 +4,12 @@ export 'ad_error.dart'; export 'ad_event.dart'; +export 'ad_ui_element.dart'; export 'interactive_media_ads_platform.dart'; export 'platform_ad_display_container.dart'; export 'platform_ads_loader.dart'; export 'platform_ads_manager.dart'; export 'platform_ads_manager_delegate.dart'; +export 'platform_ads_rendering_settings.dart'; export 'platform_ads_request.dart'; export 'platform_content_progress_provider.dart'; diff --git a/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart b/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart index 24318a2c6fc1..46db8d3e2d22 100644 --- a/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart +++ b/packages/interactive_media_ads/pigeons/interactive_media_ads_android.dart @@ -469,6 +469,10 @@ abstract class ImaSdkFactory { /// Creates an AdsRequest object to contain the data used to request ads. AdsRequest createAdsRequest(); + + /// Creates an `AdsRenderingSettings` object to give the AdsManager parameters + /// that control the rendering of ads. + AdsRenderingSettings createAdsRenderingSettings(); } /// Defines general SDK settings that are used when creating an `AdsLoader`. @@ -1026,3 +1030,57 @@ abstract class Ad { /// Indicates whether the ad can be skipped by the user. late final bool isSkippable; } + +/// Listener interface for click events. +/// +/// See https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/CompanionAdSlot.ClickListener.html. +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: + 'com.google.ads.interactivemedia.v3.api.CompanionAdSlot.ClickListener', + ), +) +abstract class CompanionAdSlotClickListener { + CompanionAdSlotClickListener(); + + /// Respond to a click on this companion ad slot. + late final void Function() onCompanionAdClick; +} + +/// A companion ad slot for which the SDK should retrieve ads. +/// +/// See https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/api/reference/com/google/ads/interactivemedia/v3/api/CompanionAdSlot.html. +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'com.google.ads.interactivemedia.v3.api.CompanionAdSlot', + ), +) +abstract class CompanionAdSlot { + /// Registers a listener for companion clicks. + void addClickListener(CompanionAdSlotClickListener clickListener); + + /// Returns the ViewGroup into which the companion will be rendered. + ViewGroup getContainer(); + + /// Returns the height of the companion slot. + int getHeight(); + + /// Returns the width of the companion slot. + int getWidth(); + + /// Returns true if the companion slot is filled, false otherwise. + bool isFilled(); + + /// Removes a listener for companion clicks. + void removeClickListener(CompanionAdSlotClickListener clickListener); + + /// Sets the ViewGroup into which the companion will be rendered. + /// + /// Required. + void setContainer(ViewGroup container); + + /// Sets the size of the slot. + /// + /// Only companions matching the slot size will be displayed in the slot. + void setSize(int width, int height); +} diff --git a/packages/interactive_media_ads/pubspec.yaml b/packages/interactive_media_ads/pubspec.yaml index 88e8c6fc8489..4fa1d3743c21 100644 --- a/packages/interactive_media_ads/pubspec.yaml +++ b/packages/interactive_media_ads/pubspec.yaml @@ -2,7 +2,7 @@ name: interactive_media_ads description: A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. repository: https://github.com/flutter/packages/tree/main/packages/interactive_media_ads issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+interactive_media_ads%22 -version: 0.2.2+15 # This must match the version in +version: 0.2.3+3 # This must match the version in # `android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt` and # `ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift` @@ -31,7 +31,7 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.4.4 - pigeon: ^22.5.0 + pigeon: ^22.6.1 topics: - ads diff --git a/packages/interactive_media_ads/test/ads_manager_test.dart b/packages/interactive_media_ads/test/ads_manager_test.dart index f27f37aa82c1..38591913171a 100644 --- a/packages/interactive_media_ads/test/ads_manager_test.dart +++ b/packages/interactive_media_ads/test/ads_manager_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:flutter/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:interactive_media_ads/interactive_media_ads.dart'; @@ -11,12 +13,25 @@ import 'test_stubs.dart'; void main() { test('init', () async { + final AdsRenderingSettings adsRenderingSettings = + AdsRenderingSettings.fromPlatform( + TestAdsRenderingSettings( + const PlatformAdsRenderingSettingsCreationParams(), + ), + ); + + final Completer settingsCompleter = + Completer(); + final TestAdsManager platformManager = TestAdsManager( - onInit: expectAsync1((_) async {}), + onInit: ({PlatformAdsRenderingSettings? settings}) async { + settingsCompleter.complete(settings); + }, ); final AdsManager manager = createAdsManager(platformManager); - await manager.init(); + await manager.init(settings: adsRenderingSettings); + expect(await settingsCompleter.future, adsRenderingSettings.platform); }); test('start', () async { diff --git a/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart b/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart index fc6fe08a4000..d1d3d9f26a8c 100644 --- a/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart +++ b/packages/interactive_media_ads/test/android/ads_loader_test.mocks.dart @@ -153,8 +153,9 @@ class _FakeImaSdkSettings_11 extends _i1.SmartFake ); } -class _FakeImaSdkFactory_12 extends _i1.SmartFake implements _i2.ImaSdkFactory { - _FakeImaSdkFactory_12( +class _FakeAdsRenderingSettings_12 extends _i1.SmartFake + implements _i2.AdsRenderingSettings { + _FakeAdsRenderingSettings_12( Object parent, Invocation parentInvocation, ) : super( @@ -163,8 +164,8 @@ class _FakeImaSdkFactory_12 extends _i1.SmartFake implements _i2.ImaSdkFactory { ); } -class _FakeVideoAdPlayer_13 extends _i1.SmartFake implements _i2.VideoAdPlayer { - _FakeVideoAdPlayer_13( +class _FakeImaSdkFactory_13 extends _i1.SmartFake implements _i2.ImaSdkFactory { + _FakeImaSdkFactory_13( Object parent, Invocation parentInvocation, ) : super( @@ -173,9 +174,19 @@ class _FakeVideoAdPlayer_13 extends _i1.SmartFake implements _i2.VideoAdPlayer { ); } -class _FakeVideoAdPlayerCallback_14 extends _i1.SmartFake +class _FakeVideoAdPlayer_14 extends _i1.SmartFake implements _i2.VideoAdPlayer { + _FakeVideoAdPlayer_14( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeVideoAdPlayerCallback_15 extends _i1.SmartFake implements _i2.VideoAdPlayerCallback { - _FakeVideoAdPlayerCallback_14( + _FakeVideoAdPlayerCallback_15( Object parent, Invocation parentInvocation, ) : super( @@ -184,8 +195,8 @@ class _FakeVideoAdPlayerCallback_14 extends _i1.SmartFake ); } -class _FakeVideoView_15 extends _i1.SmartFake implements _i2.VideoView { - _FakeVideoView_15( +class _FakeVideoView_16 extends _i1.SmartFake implements _i2.VideoView { + _FakeVideoView_16( Object parent, Invocation parentInvocation, ) : super( @@ -194,8 +205,8 @@ class _FakeVideoView_15 extends _i1.SmartFake implements _i2.VideoView { ); } -class _FakeOffset_16 extends _i1.SmartFake implements _i3.Offset { - _FakeOffset_16( +class _FakeOffset_17 extends _i1.SmartFake implements _i3.Offset { + _FakeOffset_17( Object parent, Invocation parentInvocation, ) : super( @@ -204,8 +215,8 @@ class _FakeOffset_16 extends _i1.SmartFake implements _i3.Offset { ); } -class _FakeSize_17 extends _i1.SmartFake implements _i3.Size { - _FakeSize_17( +class _FakeSize_18 extends _i1.SmartFake implements _i3.Size { + _FakeSize_18( Object parent, Invocation parentInvocation, ) : super( @@ -214,9 +225,9 @@ class _FakeSize_17 extends _i1.SmartFake implements _i3.Size { ); } -class _FakeExpensiveAndroidViewController_18 extends _i1.SmartFake +class _FakeExpensiveAndroidViewController_19 extends _i1.SmartFake implements _i4.ExpensiveAndroidViewController { - _FakeExpensiveAndroidViewController_18( + _FakeExpensiveAndroidViewController_19( Object parent, Invocation parentInvocation, ) : super( @@ -225,9 +236,9 @@ class _FakeExpensiveAndroidViewController_18 extends _i1.SmartFake ); } -class _FakeSurfaceAndroidViewController_19 extends _i1.SmartFake +class _FakeSurfaceAndroidViewController_20 extends _i1.SmartFake implements _i4.SurfaceAndroidViewController { - _FakeSurfaceAndroidViewController_19( + _FakeSurfaceAndroidViewController_20( Object parent, Invocation parentInvocation, ) : super( @@ -1066,20 +1077,45 @@ class MockImaSdkFactory extends _i1.Mock implements _i2.ImaSdkFactory { )), ) as _i6.Future<_i2.AdsRequest>); + @override + _i6.Future<_i2.AdsRenderingSettings> createAdsRenderingSettings() => + (super.noSuchMethod( + Invocation.method( + #createAdsRenderingSettings, + [], + ), + returnValue: _i6.Future<_i2.AdsRenderingSettings>.value( + _FakeAdsRenderingSettings_12( + this, + Invocation.method( + #createAdsRenderingSettings, + [], + ), + )), + returnValueForMissingStub: _i6.Future<_i2.AdsRenderingSettings>.value( + _FakeAdsRenderingSettings_12( + this, + Invocation.method( + #createAdsRenderingSettings, + [], + ), + )), + ) as _i6.Future<_i2.AdsRenderingSettings>); + @override _i2.ImaSdkFactory pigeon_copy() => (super.noSuchMethod( Invocation.method( #pigeon_copy, [], ), - returnValue: _FakeImaSdkFactory_12( + returnValue: _FakeImaSdkFactory_13( this, Invocation.method( #pigeon_copy, [], ), ), - returnValueForMissingStub: _FakeImaSdkFactory_12( + returnValueForMissingStub: _FakeImaSdkFactory_13( this, Invocation.method( #pigeon_copy, @@ -1298,14 +1334,14 @@ class MockVideoAdPlayer extends _i1.Mock implements _i2.VideoAdPlayer { #pigeon_copy, [], ), - returnValue: _FakeVideoAdPlayer_13( + returnValue: _FakeVideoAdPlayer_14( this, Invocation.method( #pigeon_copy, [], ), ), - returnValueForMissingStub: _FakeVideoAdPlayer_13( + returnValueForMissingStub: _FakeVideoAdPlayer_14( this, Invocation.method( #pigeon_copy, @@ -1456,14 +1492,14 @@ class MockVideoAdPlayerCallback extends _i1.Mock #pigeon_copy, [], ), - returnValue: _FakeVideoAdPlayerCallback_14( + returnValue: _FakeVideoAdPlayerCallback_15( this, Invocation.method( #pigeon_copy, [], ), ), - returnValueForMissingStub: _FakeVideoAdPlayerCallback_14( + returnValueForMissingStub: _FakeVideoAdPlayerCallback_15( this, Invocation.method( #pigeon_copy, @@ -1543,14 +1579,14 @@ class MockVideoView extends _i1.Mock implements _i2.VideoView { #pigeon_copy, [], ), - returnValue: _FakeVideoView_15( + returnValue: _FakeVideoView_16( this, Invocation.method( #pigeon_copy, [], ), ), - returnValueForMissingStub: _FakeVideoView_15( + returnValueForMissingStub: _FakeVideoView_16( this, Invocation.method( #pigeon_copy, @@ -1589,11 +1625,11 @@ class MockSurfaceAndroidViewController extends _i1.Mock @override _i4.PointTransformer get pointTransformer => (super.noSuchMethod( Invocation.getter(#pointTransformer), - returnValue: (_i3.Offset position) => _FakeOffset_16( + returnValue: (_i3.Offset position) => _FakeOffset_17( this, Invocation.getter(#pointTransformer), ), - returnValueForMissingStub: (_i3.Offset position) => _FakeOffset_16( + returnValueForMissingStub: (_i3.Offset position) => _FakeOffset_17( this, Invocation.getter(#pointTransformer), ), @@ -1657,14 +1693,14 @@ class MockSurfaceAndroidViewController extends _i1.Mock #setSize, [size], ), - returnValue: _i6.Future<_i3.Size>.value(_FakeSize_17( + returnValue: _i6.Future<_i3.Size>.value(_FakeSize_18( this, Invocation.method( #setSize, [size], ), )), - returnValueForMissingStub: _i6.Future<_i3.Size>.value(_FakeSize_17( + returnValueForMissingStub: _i6.Future<_i3.Size>.value(_FakeSize_18( this, Invocation.method( #setSize, @@ -1777,7 +1813,7 @@ class MockPlatformViewsServiceProxy extends _i1.Mock #onFocus: onFocus, }, ), - returnValue: _FakeExpensiveAndroidViewController_18( + returnValue: _FakeExpensiveAndroidViewController_19( this, Invocation.method( #initExpensiveAndroidView, @@ -1792,7 +1828,7 @@ class MockPlatformViewsServiceProxy extends _i1.Mock }, ), ), - returnValueForMissingStub: _FakeExpensiveAndroidViewController_18( + returnValueForMissingStub: _FakeExpensiveAndroidViewController_19( this, Invocation.method( #initExpensiveAndroidView, @@ -1831,7 +1867,7 @@ class MockPlatformViewsServiceProxy extends _i1.Mock #onFocus: onFocus, }, ), - returnValue: _FakeSurfaceAndroidViewController_19( + returnValue: _FakeSurfaceAndroidViewController_20( this, Invocation.method( #initSurfaceAndroidView, @@ -1846,7 +1882,7 @@ class MockPlatformViewsServiceProxy extends _i1.Mock }, ), ), - returnValueForMissingStub: _FakeSurfaceAndroidViewController_19( + returnValueForMissingStub: _FakeSurfaceAndroidViewController_20( this, Invocation.method( #initSurfaceAndroidView, diff --git a/packages/interactive_media_ads/test/android/ads_manager_test.dart b/packages/interactive_media_ads/test/android/ads_manager_test.dart index 0b94d6a7d753..90e2ed9ad5f3 100644 --- a/packages/interactive_media_ads/test/android/ads_manager_test.dart +++ b/packages/interactive_media_ads/test/android/ads_manager_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:interactive_media_ads/src/android/android_ads_manager.dart'; import 'package:interactive_media_ads/src/android/android_ads_manager_delegate.dart'; +import 'package:interactive_media_ads/src/android/android_ads_rendering_settings.dart'; import 'package:interactive_media_ads/src/android/interactive_media_ads.g.dart' as ima; import 'package:interactive_media_ads/src/android/interactive_media_ads_proxy.dart'; @@ -21,6 +22,8 @@ import 'ads_manager_test.mocks.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), + MockSpec(), ]) void main() { group('AndroidAdsManager', () { @@ -32,12 +35,46 @@ void main() { verify(mockAdsManager.destroy()); }); - test('init', () { + test('init', () async { final MockAdsManager mockAdsManager = MockAdsManager(); + + final MockImaSdkFactory mockImaSdkFactory = MockImaSdkFactory(); + final MockAdsRenderingSettings mockAdsRenderingSettings = + MockAdsRenderingSettings(); + when(mockImaSdkFactory.createAdsRenderingSettings()).thenAnswer( + (_) => Future.value(mockAdsRenderingSettings), + ); + final AndroidAdsManager adsManager = AndroidAdsManager(mockAdsManager); - adsManager.init(AdsManagerInitParams()); - verify(mockAdsManager.init(null)); + final AndroidAdsRenderingSettings settings = AndroidAdsRenderingSettings( + AndroidAdsRenderingSettingsCreationParams( + bitrate: 1000, + enablePreloading: false, + loadVideoTimeout: const Duration(seconds: 2), + mimeTypes: const ['value'], + playAdsAfterTime: const Duration(seconds: 5), + uiElements: const {AdUIElement.countdown}, + enableCustomTabs: true, + proxy: InteractiveMediaAdsProxy( + instanceImaSdkFactory: () => mockImaSdkFactory, + ), + ), + ); + await adsManager.init(settings: settings); + + verifyInOrder(>[ + mockAdsRenderingSettings.setBitrateKbps(1000), + mockAdsRenderingSettings.setEnablePreloading(false), + mockAdsRenderingSettings.setLoadVideoTimeout(2000), + mockAdsRenderingSettings.setMimeTypes(['value']), + mockAdsRenderingSettings.setPlayAdsAfterTime(5.0), + mockAdsRenderingSettings.setUiElements( + [ima.UiElement.countdown], + ), + mockAdsRenderingSettings.setEnableCustomTabs(true), + mockAdsManager.init(mockAdsRenderingSettings), + ]); }); test('start', () { diff --git a/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart b/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart index 6006ba48f18a..4181bca8d7f3 100644 --- a/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart +++ b/packages/interactive_media_ads/test/android/ads_manager_test.mocks.dart @@ -96,6 +96,58 @@ class _FakeAdsManager_6 extends _i1.SmartFake implements _i2.AdsManager { ); } +class _FakeAdsRenderingSettings_7 extends _i1.SmartFake + implements _i2.AdsRenderingSettings { + _FakeAdsRenderingSettings_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeImaSdkSettings_8 extends _i1.SmartFake + implements _i2.ImaSdkSettings { + _FakeImaSdkSettings_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeAdsLoader_9 extends _i1.SmartFake implements _i2.AdsLoader { + _FakeAdsLoader_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeAdsRequest_10 extends _i1.SmartFake implements _i2.AdsRequest { + _FakeAdsRequest_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeImaSdkFactory_11 extends _i1.SmartFake implements _i2.ImaSdkFactory { + _FakeImaSdkFactory_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [AdError]. /// /// See the documentation for Mockito's code generation for more information. @@ -583,3 +635,327 @@ class MockAdsManager extends _i1.Mock implements _i2.AdsManager { returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); } + +/// A class which mocks [AdsRenderingSettings]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockAdsRenderingSettings extends _i1.Mock + implements _i2.AdsRenderingSettings { + @override + _i2.PigeonInstanceManager get pigeon_instanceManager => (super.noSuchMethod( + Invocation.getter(#pigeon_instanceManager), + returnValue: _FakePigeonInstanceManager_0( + this, + Invocation.getter(#pigeon_instanceManager), + ), + returnValueForMissingStub: _FakePigeonInstanceManager_0( + this, + Invocation.getter(#pigeon_instanceManager), + ), + ) as _i2.PigeonInstanceManager); + + @override + _i4.Future getBitrateKbps() => (super.noSuchMethod( + Invocation.method( + #getBitrateKbps, + [], + ), + returnValue: _i4.Future.value(0), + returnValueForMissingStub: _i4.Future.value(0), + ) as _i4.Future); + + @override + _i4.Future getEnableCustomTabs() => (super.noSuchMethod( + Invocation.method( + #getEnableCustomTabs, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future getEnablePreloading() => (super.noSuchMethod( + Invocation.method( + #getEnablePreloading, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future getFocusSkipButtonWhenAvailable() => (super.noSuchMethod( + Invocation.method( + #getFocusSkipButtonWhenAvailable, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future> getMimeTypes() => (super.noSuchMethod( + Invocation.method( + #getMimeTypes, + [], + ), + returnValue: _i4.Future>.value([]), + returnValueForMissingStub: _i4.Future>.value([]), + ) as _i4.Future>); + + @override + _i4.Future setBitrateKbps(int? bitrate) => (super.noSuchMethod( + Invocation.method( + #setBitrateKbps, + [bitrate], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setEnableCustomTabs(bool? enableCustomTabs) => + (super.noSuchMethod( + Invocation.method( + #setEnableCustomTabs, + [enableCustomTabs], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setEnablePreloading(bool? enablePreloading) => + (super.noSuchMethod( + Invocation.method( + #setEnablePreloading, + [enablePreloading], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setFocusSkipButtonWhenAvailable( + bool? enableFocusSkipButton) => + (super.noSuchMethod( + Invocation.method( + #setFocusSkipButtonWhenAvailable, + [enableFocusSkipButton], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setLoadVideoTimeout(int? loadVideoTimeout) => + (super.noSuchMethod( + Invocation.method( + #setLoadVideoTimeout, + [loadVideoTimeout], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setMimeTypes(List? mimeTypes) => (super.noSuchMethod( + Invocation.method( + #setMimeTypes, + [mimeTypes], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setPlayAdsAfterTime(double? time) => (super.noSuchMethod( + Invocation.method( + #setPlayAdsAfterTime, + [time], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setUiElements(List<_i2.UiElement>? uiElements) => + (super.noSuchMethod( + Invocation.method( + #setUiElements, + [uiElements], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.AdsRenderingSettings pigeon_copy() => (super.noSuchMethod( + Invocation.method( + #pigeon_copy, + [], + ), + returnValue: _FakeAdsRenderingSettings_7( + this, + Invocation.method( + #pigeon_copy, + [], + ), + ), + returnValueForMissingStub: _FakeAdsRenderingSettings_7( + this, + Invocation.method( + #pigeon_copy, + [], + ), + ), + ) as _i2.AdsRenderingSettings); +} + +/// A class which mocks [ImaSdkFactory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockImaSdkFactory extends _i1.Mock implements _i2.ImaSdkFactory { + @override + _i2.PigeonInstanceManager get pigeon_instanceManager => (super.noSuchMethod( + Invocation.getter(#pigeon_instanceManager), + returnValue: _FakePigeonInstanceManager_0( + this, + Invocation.getter(#pigeon_instanceManager), + ), + returnValueForMissingStub: _FakePigeonInstanceManager_0( + this, + Invocation.getter(#pigeon_instanceManager), + ), + ) as _i2.PigeonInstanceManager); + + @override + _i4.Future<_i2.ImaSdkSettings> createImaSdkSettings() => (super.noSuchMethod( + Invocation.method( + #createImaSdkSettings, + [], + ), + returnValue: _i4.Future<_i2.ImaSdkSettings>.value(_FakeImaSdkSettings_8( + this, + Invocation.method( + #createImaSdkSettings, + [], + ), + )), + returnValueForMissingStub: + _i4.Future<_i2.ImaSdkSettings>.value(_FakeImaSdkSettings_8( + this, + Invocation.method( + #createImaSdkSettings, + [], + ), + )), + ) as _i4.Future<_i2.ImaSdkSettings>); + + @override + _i4.Future<_i2.AdsLoader> createAdsLoader( + _i2.ImaSdkSettings? settings, + _i2.AdDisplayContainer? container, + ) => + (super.noSuchMethod( + Invocation.method( + #createAdsLoader, + [ + settings, + container, + ], + ), + returnValue: _i4.Future<_i2.AdsLoader>.value(_FakeAdsLoader_9( + this, + Invocation.method( + #createAdsLoader, + [ + settings, + container, + ], + ), + )), + returnValueForMissingStub: + _i4.Future<_i2.AdsLoader>.value(_FakeAdsLoader_9( + this, + Invocation.method( + #createAdsLoader, + [ + settings, + container, + ], + ), + )), + ) as _i4.Future<_i2.AdsLoader>); + + @override + _i4.Future<_i2.AdsRequest> createAdsRequest() => (super.noSuchMethod( + Invocation.method( + #createAdsRequest, + [], + ), + returnValue: _i4.Future<_i2.AdsRequest>.value(_FakeAdsRequest_10( + this, + Invocation.method( + #createAdsRequest, + [], + ), + )), + returnValueForMissingStub: + _i4.Future<_i2.AdsRequest>.value(_FakeAdsRequest_10( + this, + Invocation.method( + #createAdsRequest, + [], + ), + )), + ) as _i4.Future<_i2.AdsRequest>); + + @override + _i4.Future<_i2.AdsRenderingSettings> createAdsRenderingSettings() => + (super.noSuchMethod( + Invocation.method( + #createAdsRenderingSettings, + [], + ), + returnValue: _i4.Future<_i2.AdsRenderingSettings>.value( + _FakeAdsRenderingSettings_7( + this, + Invocation.method( + #createAdsRenderingSettings, + [], + ), + )), + returnValueForMissingStub: _i4.Future<_i2.AdsRenderingSettings>.value( + _FakeAdsRenderingSettings_7( + this, + Invocation.method( + #createAdsRenderingSettings, + [], + ), + )), + ) as _i4.Future<_i2.AdsRenderingSettings>); + + @override + _i2.ImaSdkFactory pigeon_copy() => (super.noSuchMethod( + Invocation.method( + #pigeon_copy, + [], + ), + returnValue: _FakeImaSdkFactory_11( + this, + Invocation.method( + #pigeon_copy, + [], + ), + ), + returnValueForMissingStub: _FakeImaSdkFactory_11( + this, + Invocation.method( + #pigeon_copy, + [], + ), + ), + ) as _i2.ImaSdkFactory); +} diff --git a/packages/interactive_media_ads/test/ios/ads_manager_test.dart b/packages/interactive_media_ads/test/ios/ads_manager_test.dart index 06d1e1dab51e..ff4094bc804f 100644 --- a/packages/interactive_media_ads/test/ios/ads_manager_test.dart +++ b/packages/interactive_media_ads/test/ios/ads_manager_test.dart @@ -8,13 +8,17 @@ import 'package:interactive_media_ads/src/ios/interactive_media_ads.g.dart' import 'package:interactive_media_ads/src/ios/interactive_media_ads_proxy.dart'; import 'package:interactive_media_ads/src/ios/ios_ads_manager.dart'; import 'package:interactive_media_ads/src/ios/ios_ads_manager_delegate.dart'; +import 'package:interactive_media_ads/src/ios/ios_ads_rendering_settings.dart'; import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'ads_manager_test.mocks.dart'; -@GenerateNiceMocks(>[MockSpec()]) +@GenerateNiceMocks(>[ + MockSpec(), + MockSpec(), +]) void main() { group('IOSAdsManager', () { test('destroy', () { @@ -25,12 +29,40 @@ void main() { verify(mockAdsManager.destroy()); }); - test('init', () { + test('init', () async { final MockIMAAdsManager mockAdsManager = MockIMAAdsManager(); + + final MockIMAAdsRenderingSettings mockAdsRenderingSettings = + MockIMAAdsRenderingSettings(); + final IOSAdsManager adsManager = IOSAdsManager(mockAdsManager); - adsManager.init(AdsManagerInitParams()); - verify(mockAdsManager.initialize(null)); + final IOSAdsRenderingSettings settings = IOSAdsRenderingSettings( + IOSAdsRenderingSettingsCreationParams( + bitrate: 1000, + enablePreloading: false, + loadVideoTimeout: const Duration(seconds: 9), + mimeTypes: const ['value'], + playAdsAfterTime: const Duration(seconds: 5), + uiElements: const {AdUIElement.countdown}, + proxy: InteractiveMediaAdsProxy( + newIMAAdsRenderingSettings: () => mockAdsRenderingSettings, + ), + ), + ); + await adsManager.init(settings: settings); + + verifyInOrder(>[ + mockAdsRenderingSettings.setBitrate(1000), + mockAdsRenderingSettings.setEnablePreloading(false), + mockAdsRenderingSettings.setLoadVideoTimeout(9.0), + mockAdsRenderingSettings.setMimeTypes(['value']), + mockAdsRenderingSettings.setPlayAdsAfterTime(5.0), + mockAdsRenderingSettings.setUIElements( + [ima.UIElementType.countdown], + ), + mockAdsManager.initialize(mockAdsRenderingSettings), + ]); }); test('start', () { diff --git a/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart b/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart index 9e891eb299f2..7214de1d0a55 100644 --- a/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart +++ b/packages/interactive_media_ads/test/ios/ads_manager_test.mocks.dart @@ -43,6 +43,17 @@ class _FakeIMAAdsManager_1 extends _i1.SmartFake implements _i2.IMAAdsManager { ); } +class _FakeIMAAdsRenderingSettings_2 extends _i1.SmartFake + implements _i2.IMAAdsRenderingSettings { + _FakeIMAAdsRenderingSettings_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [IMAAdsManager]. /// /// See the documentation for Mockito's code generation for more information. @@ -165,3 +176,117 @@ class MockIMAAdsManager extends _i1.Mock implements _i2.IMAAdsManager { ), ) as _i2.IMAAdsManager); } + +/// A class which mocks [IMAAdsRenderingSettings]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIMAAdsRenderingSettings extends _i1.Mock + implements _i2.IMAAdsRenderingSettings { + @override + _i2.PigeonInstanceManager get pigeon_instanceManager => (super.noSuchMethod( + Invocation.getter(#pigeon_instanceManager), + returnValue: _FakePigeonInstanceManager_0( + this, + Invocation.getter(#pigeon_instanceManager), + ), + returnValueForMissingStub: _FakePigeonInstanceManager_0( + this, + Invocation.getter(#pigeon_instanceManager), + ), + ) as _i2.PigeonInstanceManager); + + @override + _i3.Future setMimeTypes(List? types) => (super.noSuchMethod( + Invocation.method( + #setMimeTypes, + [types], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setBitrate(int? bitrate) => (super.noSuchMethod( + Invocation.method( + #setBitrate, + [bitrate], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setLoadVideoTimeout(double? seconds) => (super.noSuchMethod( + Invocation.method( + #setLoadVideoTimeout, + [seconds], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setPlayAdsAfterTime(double? seconds) => (super.noSuchMethod( + Invocation.method( + #setPlayAdsAfterTime, + [seconds], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setUIElements(List<_i2.UIElementType>? types) => + (super.noSuchMethod( + Invocation.method( + #setUIElements, + [types], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setEnablePreloading(bool? enable) => (super.noSuchMethod( + Invocation.method( + #setEnablePreloading, + [enable], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setLinkOpenerPresentingController( + _i2.UIViewController? controller) => + (super.noSuchMethod( + Invocation.method( + #setLinkOpenerPresentingController, + [controller], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i2.IMAAdsRenderingSettings pigeon_copy() => (super.noSuchMethod( + Invocation.method( + #pigeon_copy, + [], + ), + returnValue: _FakeIMAAdsRenderingSettings_2( + this, + Invocation.method( + #pigeon_copy, + [], + ), + ), + returnValueForMissingStub: _FakeIMAAdsRenderingSettings_2( + this, + Invocation.method( + #pigeon_copy, + [], + ), + ), + ) as _i2.IMAAdsRenderingSettings); +} diff --git a/packages/interactive_media_ads/test/test_stubs.dart b/packages/interactive_media_ads/test/test_stubs.dart index 328f58f46995..ff3ae4512155 100644 --- a/packages/interactive_media_ads/test/test_stubs.dart +++ b/packages/interactive_media_ads/test/test_stubs.dart @@ -12,6 +12,7 @@ final class TestInteractiveMediaAdsPlatform required this.onCreatePlatformAdsManagerDelegate, required this.onCreatePlatformAdDisplayContainer, required this.onCreatePlatformContentProgressProvider, + this.onCreatePlatformAdsRenderingSettings, }); PlatformAdsLoader Function(PlatformAdsLoaderCreationParams params) @@ -29,6 +30,10 @@ final class TestInteractiveMediaAdsPlatform PlatformContentProgressProviderCreationParams params, ) onCreatePlatformContentProgressProvider; + PlatformAdsRenderingSettings Function( + PlatformAdsRenderingSettingsCreationParams params, + )? onCreatePlatformAdsRenderingSettings; + @override PlatformAdsLoader createPlatformAdsLoader( PlatformAdsLoaderCreationParams params, @@ -56,6 +61,14 @@ final class TestInteractiveMediaAdsPlatform ) { return onCreatePlatformContentProgressProvider(params); } + + @override + PlatformAdsRenderingSettings createPlatformAdsRenderingSettings( + PlatformAdsRenderingSettingsCreationParams params, + ) { + return onCreatePlatformAdsRenderingSettings?.call(params) ?? + TestAdsRenderingSettings(params); + } } final class TestPlatformAdDisplayContainer extends PlatformAdDisplayContainer { @@ -110,7 +123,7 @@ class TestAdsManager extends PlatformAdsManager { this.onSkip, }); - Future Function(AdsManagerInitParams params)? onInit; + Future Function({PlatformAdsRenderingSettings? settings})? onInit; Future Function(PlatformAdsManagerDelegate delegate)? onSetAdsManagerDelegate; @@ -128,8 +141,8 @@ class TestAdsManager extends PlatformAdsManager { Future Function()? onDestroy; @override - Future init(AdsManagerInitParams params) async { - return onInit?.call(params); + Future init({PlatformAdsRenderingSettings? settings}) async { + return onInit?.call(settings: settings); } @override @@ -189,3 +202,7 @@ class TestContentProgressProvider extends PlatformContentProgressProvider { return onSetProgress?.call(progress: progress, duration: duration); } } + +final class TestAdsRenderingSettings extends PlatformAdsRenderingSettings { + TestAdsRenderingSettings(super.params) : super.implementation(); +} diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 61e3b597f50b..2dda63366d01 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.4+1 +* Updates to Pigeon v22. * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 0.2.4 diff --git a/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj b/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj index d230aef2644e..f0a95635c593 100644 --- a/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 31DC4A212AA8CC9300781E88 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 937254B9D43BF2078EE3DE65 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A144F620445D38CD0FEB7B8E /* Pods_RunnerTests.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -78,6 +79,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 101538FD877087FE0BE2EA00 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -187,13 +189,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - B747922D12365ABBBA69BEA8 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -223,7 +227,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -248,6 +252,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -334,26 +341,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - B747922D12365ABBBA69BEA8 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", - "${BUILT_PRODUCTS_DIR}/ios_platform_images/ios_platform_images.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ios_platform_images.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; C102F13F37851E08F0608EE5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -788,6 +775,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 42695972f4c0..45ed0664535b 100644 --- a/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/ios_platform_images/ios/ios_platform_images/Sources/ios_platform_images/messages.g.swift b/packages/ios_platform_images/ios/ios_platform_images/Sources/ios_platform_images/messages.g.swift index 30e14c742423..761f5082bc6e 100644 --- a/packages/ios_platform_images/ios/ios_platform_images/Sources/ios_platform_images/messages.g.swift +++ b/packages/ios_platform_images/ios/ios_platform_images/Sources/ios_platform_images/messages.g.swift @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -14,8 +14,22 @@ import Foundation #error("Unsupported platform.") #endif -private func isNullish(_ value: Any?) -> Bool { - return value is NSNull || value == nil +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } } private func wrapResult(_ result: Any?) -> [Any?] { @@ -23,6 +37,13 @@ private func wrapResult(_ result: Any?) -> [Any?] { } private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } if let flutterError = error as? FlutterError { return [ flutterError.code, @@ -37,6 +58,10 @@ private func wrapError(_ error: Any) -> [Any?] { ] } +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } return value as! T? @@ -51,9 +76,10 @@ struct PlatformImageData { /// The image's scale factor. var scale: Double - static func fromList(_ list: [Any?]) -> PlatformImageData? { - let data = list[0] as! FlutterStandardTypedData - let scale = list[1] as! Double + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> PlatformImageData? { + let data = pigeonVar_list[0] as! FlutterStandardTypedData + let scale = pigeonVar_list[1] as! Double return PlatformImageData( data: data, @@ -67,10 +93,11 @@ struct PlatformImageData { ] } } -private class PlatformImagesApiCodecReader: FlutterStandardReader { + +private class messagesPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { - case 128: + case 129: return PlatformImageData.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -78,10 +105,10 @@ private class PlatformImagesApiCodecReader: FlutterStandardReader { } } -private class PlatformImagesApiCodecWriter: FlutterStandardWriter { +private class messagesPigeonCodecWriter: FlutterStandardWriter { override func writeValue(_ value: Any) { if let value = value as? PlatformImageData { - super.writeByte(128) + super.writeByte(129) super.writeValue(value.toList()) } else { super.writeValue(value) @@ -89,18 +116,18 @@ private class PlatformImagesApiCodecWriter: FlutterStandardWriter { } } -private class PlatformImagesApiCodecReaderWriter: FlutterStandardReaderWriter { +private class messagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { override func reader(with data: Data) -> FlutterStandardReader { - return PlatformImagesApiCodecReader(data: data) + return messagesPigeonCodecReader(data: data) } override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return PlatformImagesApiCodecWriter(data: data) + return messagesPigeonCodecWriter(data: data) } } -class PlatformImagesApiCodec: FlutterStandardMessageCodec { - static let shared = PlatformImagesApiCodec(readerWriter: PlatformImagesApiCodecReaderWriter()) +class messagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = messagesPigeonCodec(readerWriter: messagesPigeonCodecReaderWriter()) } /// Generated protocol from Pigeon that represents a handler of messages from Flutter. @@ -115,14 +142,17 @@ protocol PlatformImagesApi { /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. class PlatformImagesApiSetup { - /// The codec used by PlatformImagesApi. - static var codec: FlutterStandardMessageCodec { PlatformImagesApiCodec.shared } + static var codec: FlutterStandardMessageCodec { messagesPigeonCodec.shared } /// Sets up an instance of `PlatformImagesApi` to handle messages through the `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: PlatformImagesApi?) { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: PlatformImagesApi?, + messageChannelSuffix: String = "" + ) { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" /// Returns the URL for the given resource, or null if no such resource is /// found. let resolveUrlChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.resolveUrl", + name: "dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.resolveUrl\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { resolveUrlChannel.setMessageHandler { message, reply in @@ -142,7 +172,7 @@ class PlatformImagesApiSetup { /// Returns the data for the image resource with the given name, or null if /// no such resource is found. let loadImageChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.loadImage", + name: "dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.loadImage\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { loadImageChannel.setMessageHandler { message, reply in diff --git a/packages/ios_platform_images/lib/src/messages.g.dart b/packages/ios_platform_images/lib/src/messages.g.dart index a3a5ab43350e..523c70d7895b 100644 --- a/packages/ios_platform_images/lib/src/messages.g.dart +++ b/packages/ios_platform_images/lib/src/messages.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v11.0.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -11,6 +11,13 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + /// A serialization of a platform image's data. class PlatformImageData { PlatformImageData({ @@ -40,12 +47,15 @@ class PlatformImageData { } } -class _PlatformImagesApiCodec extends StandardMessageCodec { - const _PlatformImagesApiCodec(); +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is PlatformImageData) { - buffer.putUint8(128); + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is PlatformImageData) { + buffer.putUint8(129); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -55,7 +65,7 @@ class _PlatformImagesApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 129: return PlatformImageData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -67,60 +77,66 @@ class PlatformImagesApi { /// Constructor for [PlatformImagesApi]. 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. - PlatformImagesApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + PlatformImagesApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - static const MessageCodec codec = _PlatformImagesApiCodec(); + final String pigeonVar_messageChannelSuffix; /// Returns the URL for the given resource, or null if no such resource is /// found. - Future resolveUrl( - String arg_resourceName, String? arg_extension) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.resolveUrl', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_resourceName, arg_extension]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future resolveUrl(String resourceName, String? extension) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.resolveUrl$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([resourceName, extension]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { - return (replyList[0] as String?); + return (pigeonVar_replyList[0] as String?); } } /// Returns the data for the image resource with the given name, or null if /// no such resource is found. - Future loadImage(String arg_name) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.loadImage', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_name]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future loadImage(String name) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.loadImage$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([name]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { - return (replyList[0] as PlatformImageData?); + return (pigeonVar_replyList[0] as PlatformImageData?); } } } diff --git a/packages/ios_platform_images/pigeons/messages.dart b/packages/ios_platform_images/pigeons/messages.dart index f45a914e19ac..ce04b8f4058e 100644 --- a/packages/ios_platform_images/pigeons/messages.dart +++ b/packages/ios_platform_images/pigeons/messages.dart @@ -7,7 +7,7 @@ import 'package:pigeon/pigeon.dart'; @ConfigurePigeon(PigeonOptions( dartOut: 'lib/src/messages.g.dart', swiftOut: - 'ios/ios_platform_images/Sources/ios_platform_messages/messages.g.swift', + 'ios/ios_platform_images/Sources/ios_platform_images/messages.g.swift', copyrightHeader: 'pigeons/copyright.txt', )) diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 3d2fb1cb064f..dc5925b42f7d 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/packages/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.4 +version: 0.2.4+1 environment: sdk: ^3.3.0 @@ -21,7 +21,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^11.0.0 + pigeon: ^22.6.4 topics: - image diff --git a/packages/ios_platform_images/test/ios_platform_images_test.dart b/packages/ios_platform_images/test/ios_platform_images_test.dart index 04605c1e5cba..9a873688c312 100644 --- a/packages/ios_platform_images/test/ios_platform_images_test.dart +++ b/packages/ios_platform_images/test/ios_platform_images_test.dart @@ -64,4 +64,12 @@ class FakePlatformImagesApi implements PlatformImagesApi { passedExtension = extension; return resolutionResult; } + + @override + // ignore: non_constant_identifier_names + BinaryMessenger? get pigeonVar_binaryMessenger => null; + + @override + // ignore: non_constant_identifier_names + String get pigeonVar_messageChannelSuffix => ''; } diff --git a/packages/local_auth/local_auth/example/android/app/build.gradle b/packages/local_auth/local_auth/example/android/app/build.gradle index c564af855289..3a4d94f4146f 100644 --- a/packages/local_auth/local_auth/example/android/app/build.gradle +++ b/packages/local_auth/local_auth/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.localauthexample' compileSdk flutter.compileSdkVersion @@ -32,7 +30,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.localauthexample" minSdkVersion flutter.minSdkVersion - targetSdkVersion 28 + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml index 4acc4eb87ed6..d9298b189402 100644 --- a/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml +++ b/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ android:theme="@style/Theme.AppCompat.Light" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize"> diff --git a/packages/local_auth/local_auth/example/android/build.gradle b/packages/local_auth/local_auth/example/android/build.gradle index 0bed8906c094..b9db5700753a 100644 --- a/packages/local_auth/local_auth/example/android/build.gradle +++ b/packages/local_auth/local_auth/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.2' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/local_auth/local_auth/example/android/settings.gradle b/packages/local_auth/local_auth/example/android/settings.gradle index e54a7e1fd6e5..d216116c644f 100644 --- a/packages/local_auth/local_auth/example/android/settings.gradle +++ b/packages/local_auth/local_auth/example/android/settings.gradle @@ -1,28 +1,28 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.2" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" + } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj index 307ddabcd414..0bafe94e58b6 100644 --- a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -60,6 +61,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -157,13 +159,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 8E09B0587F7B0B3390DD34B1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -174,7 +178,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -191,6 +195,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -231,24 +238,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 8E09B0587F7B0B3390DD34B1 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/local_auth_ios/local_auth_ios_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/local_auth_ios_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -493,6 +482,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b17d9a05d491..52fbc6fd946b 100644 --- a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/project.pbxproj index f0ddcb528e43..bd43df3d0713 100644 --- a/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -84,6 +85,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 1BC2DC09A8E94B377F42CF98 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -191,7 +193,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - BC39F33EA96004E670EDC9C7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -199,6 +200,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* example.app */; productType = "com.apple.product-type.application"; @@ -239,6 +243,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -322,23 +329,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - BC39F33EA96004E670EDC9C7 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -645,6 +635,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 15368eccb25a..ee5d00fc0aa9 100644 --- a/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/local_auth/local_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/local_auth/local_auth/example/macos/Runner/AppDelegate.swift b/packages/local_auth/local_auth/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/local_auth/local_auth/example/macos/Runner/AppDelegate.swift +++ b/packages/local_auth/local_auth/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index b5bc8bc9545a..cb6b8e976adf 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.47 + +* Adds compatibility with `intl` 0.20.0. + ## 1.0.46 * Updates Java compatibility version to 11. diff --git a/packages/local_auth/local_auth_android/example/android/app/build.gradle b/packages/local_auth/local_auth_android/example/android/app/build.gradle index 7db5363be38d..e1ce52328c73 100644 --- a/packages/local_auth/local_auth_android/example/android/app/build.gradle +++ b/packages/local_auth/local_auth_android/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.localauthexample' compileSdk flutter.compileSdkVersion diff --git a/packages/local_auth/local_auth_android/example/android/build.gradle b/packages/local_auth/local_auth_android/example/android/build.gradle index 1c47c2c444a5..5eee7f5e4fbd 100644 --- a/packages/local_auth/local_auth_android/example/android/build.gradle +++ b/packages/local_auth/local_auth_android/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.1' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/local_auth/local_auth_android/example/android/settings.gradle b/packages/local_auth/local_auth_android/example/android/settings.gradle index e54a7e1fd6e5..e3b0c2297977 100644 --- a/packages/local_auth/local_auth_android/example/android/settings.gradle +++ b/packages/local_auth/local_auth_android/example/android/settings.gradle @@ -1,28 +1,28 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.1" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" + } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index b75c2440797e..34bc0a143dad 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.46 +version: 1.0.47 environment: sdk: ^3.5.0 @@ -21,7 +21,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - intl: ">=0.17.0 <0.20.0" + intl: ">=0.17.0 <0.21.0" local_auth_platform_interface: ^1.0.1 dev_dependencies: diff --git a/packages/local_auth/local_auth_darwin/CHANGELOG.md b/packages/local_auth/local_auth_darwin/CHANGELOG.md index 1002a874e2f3..3caab70bf454 100644 --- a/packages/local_auth/local_auth_darwin/CHANGELOG.md +++ b/packages/local_auth/local_auth_darwin/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.2 + +* Adds compatibility with `intl` 0.20.0. + ## 1.4.1 * Updates to the current version of Pigeon. diff --git a/packages/local_auth/local_auth_darwin/example/.gitignore b/packages/local_auth/local_auth_darwin/example/.gitignore index 29a3a5017f04..79c113f9b501 100644 --- a/packages/local_auth/local_auth_darwin/example/.gitignore +++ b/packages/local_auth/local_auth_darwin/example/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/project.pbxproj index 8645071ff164..17c15e3b32b7 100644 --- a/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 60; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -12,6 +12,7 @@ 338A5F9D2BFBA45B00DF0C4E /* FLALocalAuthPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338A5F9C2BFBA45B00DF0C4E /* FLALocalAuthPluginTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 691CB38B382734AF80FBCA4C /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBFA21B380E07A3A585383D /* libPods-RunnerTests.a */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -83,6 +84,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -210,13 +212,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - A372AFBD9C2ABEAB0C7ACAC4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -250,6 +254,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -353,24 +360,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - A372AFBD9C2ABEAB0C7ACAC4 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/local_auth_darwin/local_auth_darwin_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/local_auth_darwin_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -667,6 +656,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 7585dfca24d0..52fbc6fd946b 100644 --- a/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/local_auth/local_auth_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/project.pbxproj index 8392afcc7297..98b0370506b0 100644 --- a/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 3A858B24A41D64C4BF302405 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB20D00B3AAB343668A80A59 /* Pods_RunnerTests.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; E62C10892C07DA2A000E3CCC /* FLALocalAuthPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E62C10882C07DA2A000E3CCC /* FLALocalAuthPluginTests.swift */; }; /* End PBXBuildFile section */ @@ -103,6 +104,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 274905EC52005E05DF633ACA /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -218,7 +220,6 @@ 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, - 4A22C8F8819194F5DFFCC074 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -240,7 +241,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 42F7693C713BF5CD10521737 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -248,6 +248,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* example.app */; productType = "com.apple.product-type.application"; @@ -293,6 +296,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -406,40 +412,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 42F7693C713BF5CD10521737 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 4A22C8F8819194F5DFFCC074 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -815,6 +787,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 15368eccb25a..ee5d00fc0aa9 100644 --- a/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/local_auth/local_auth_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/local_auth/local_auth_darwin/example/macos/Runner/AppDelegate.swift b/packages/local_auth/local_auth_darwin/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/local_auth/local_auth_darwin/example/macos/Runner/AppDelegate.swift +++ b/packages/local_auth/local_auth_darwin/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/local_auth/local_auth_darwin/pubspec.yaml b/packages/local_auth/local_auth_darwin/pubspec.yaml index 1ed3b6563041..4ee718f645cf 100644 --- a/packages/local_auth/local_auth_darwin/pubspec.yaml +++ b/packages/local_auth/local_auth_darwin/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_darwin description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth_darwin issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.4.1 +version: 1.4.2 environment: sdk: ^3.3.0 @@ -24,7 +24,7 @@ flutter: dependencies: flutter: sdk: flutter - intl: ">=0.17.0 <0.20.0" + intl: ">=0.17.0 <0.21.0" local_auth_platform_interface: ^1.0.1 dev_dependencies: diff --git a/packages/palette_generator/example/ios/Runner/AppDelegate.swift b/packages/palette_generator/example/ios/Runner/AppDelegate.swift index d83c0ff0beea..4580c8e76da4 100644 --- a/packages/palette_generator/example/ios/Runner/AppDelegate.swift +++ b/packages/palette_generator/example/ios/Runner/AppDelegate.swift @@ -5,7 +5,7 @@ import Flutter import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/packages/palette_generator/example/macos/Runner/AppDelegate.swift b/packages/palette_generator/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/palette_generator/example/macos/Runner/AppDelegate.swift +++ b/packages/palette_generator/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/project.pbxproj b/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/project.pbxproj index 2d557859639d..6dbbf97d7293 100644 --- a/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 2D9222481EC32A19007564B0 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D9222471EC32A19007564B0 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 60774162343BF6F19B3D65CE /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E2EF24BBF807F7F7B95F2B9 /* libPods-RunnerTests.a */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 85DDFCF6BBDEE02B9D9F8138 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0EE60090AA5F3AAAF2175B6 /* libPods-Runner.a */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -73,6 +74,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 85DDFCF6BBDEE02B9D9F8138 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -187,13 +189,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 6744BEA73651400D4EF53301 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -223,7 +227,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -245,6 +249,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -315,24 +322,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 6744BEA73651400D4EF53301 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation/path_provider_foundation_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/path_provider_foundation_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -638,6 +627,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 05067cc02d60..a5f4aba28ab2 100644 --- a/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/path_provider/path_provider/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/project.pbxproj b/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/project.pbxproj index 6f03fab76ce9..6758fe7160aa 100644 --- a/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -80,6 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 23F6FAA3AF82DFCF2B7DD79A /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -184,7 +186,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 7413A74A1ECFDFE67CD0521B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -192,6 +193,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* path_provider_example.app */; productType = "com.apple.product-type.application"; @@ -203,7 +207,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -231,6 +235,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -292,24 +299,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; }; - 7413A74A1ECFDFE67CD0521B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 82C3ED26F2C350499338A54B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -641,6 +630,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 6ed688ef37b1..b7e14dd0ae3c 100644 --- a/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/path_provider/path_provider/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/path_provider/path_provider/example/macos/Runner/AppDelegate.swift b/packages/path_provider/path_provider/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/path_provider/path_provider/example/macos/Runner/AppDelegate.swift +++ b/packages/path_provider/path_provider/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 352bad5b6cb7..b5e2335b3685 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.2.15 + +* Removes unnecessary native code. + +## 2.2.14 + +* Updates annotations lib to 1.9.1. + ## 2.2.13 * Updates annotations lib to 1.9.0. diff --git a/packages/path_provider/path_provider_android/android/build.gradle b/packages/path_provider/path_provider_android/android/build.gradle index 69a189f90d45..e325947e077e 100644 --- a/packages/path_provider/path_provider_android/android/build.gradle +++ b/packages/path_provider/path_provider_android/android/build.gradle @@ -54,6 +54,6 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.9.0' + implementation 'androidx.annotation:annotation:1.9.1' testImplementation 'junit:junit:4.13.2' } diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 1140875fd2bc..b7e0825e2e50 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -8,6 +8,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.pathprovider.Messages.PathProviderApi; @@ -44,17 +45,17 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public @Nullable String getTemporaryPath() { - return getPathProviderTemporaryDirectory(); + return context.getCacheDir().getPath(); } @Override public @Nullable String getApplicationSupportPath() { - return getApplicationSupportDirectory(); + return PathUtils.getFilesDir(context); } @Override public @Nullable String getApplicationDocumentsPath() { - return getPathProviderApplicationDocumentsDirectory(); + return PathUtils.getDataDirectory(context); } @Override @@ -64,33 +65,6 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public @Nullable String getExternalStoragePath() { - return getPathProviderStorageDirectory(); - } - - @Override - public @NonNull List getExternalCachePaths() { - return getPathProviderExternalCacheDirectories(); - } - - @Override - public @NonNull List getExternalStoragePaths( - @NonNull Messages.StorageDirectory directory) { - return getPathProviderExternalStorageDirectories(directory); - } - - private String getPathProviderTemporaryDirectory() { - return context.getCacheDir().getPath(); - } - - private String getApplicationSupportDirectory() { - return PathUtils.getFilesDir(context); - } - - private String getPathProviderApplicationDocumentsDirectory() { - return PathUtils.getDataDirectory(context); - } - - private String getPathProviderStorageDirectory() { final File dir = context.getExternalFilesDir(null); if (dir == null) { return null; @@ -98,19 +72,31 @@ private String getPathProviderStorageDirectory() { return dir.getAbsolutePath(); } - private List getPathProviderExternalCacheDirectories() { + @Override + public @NonNull List getExternalCachePaths() { final List paths = new ArrayList<>(); - for (File dir : context.getExternalCacheDirs()) { if (dir != null) { paths.add(dir.getAbsolutePath()); } } + return paths; + } + @Override + public @NonNull List getExternalStoragePaths( + @NonNull Messages.StorageDirectory directory) { + final List paths = new ArrayList<>(); + for (File dir : context.getExternalFilesDirs(getStorageDirectoryString(directory))) { + if (dir != null) { + paths.add(dir.getAbsolutePath()); + } + } return paths; } - private String getStorageDirectoryString(@NonNull Messages.StorageDirectory directory) { + @VisibleForTesting + String getStorageDirectoryString(@NonNull Messages.StorageDirectory directory) { switch (directory) { case ROOT: return null; @@ -138,17 +124,4 @@ private String getStorageDirectoryString(@NonNull Messages.StorageDirectory dire throw new RuntimeException("Unrecognized directory: " + directory); } } - - private List getPathProviderExternalStorageDirectories( - @NonNull Messages.StorageDirectory directory) { - final List paths = new ArrayList<>(); - - for (File dir : context.getExternalFilesDirs(getStorageDirectoryString(directory))) { - if (dir != null) { - paths.add(dir.getAbsolutePath()); - } - } - - return paths; - } } diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/StorageDirectoryMapper.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/StorageDirectoryMapper.java deleted file mode 100644 index 4cc12ddf5a97..000000000000 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/StorageDirectoryMapper.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins.pathprovider; - -import android.os.Environment; - -/** Helps to map the Dart `StorageDirectory` enum to a Android system constant. */ -class StorageDirectoryMapper { - /** - * Return a Android Environment constant for a Dart Index. - * - * @return The correct Android Environment constant or null, if the index is null. - * @throws IllegalArgumentException If `dartIndex` is not null but also not matches any known - * index. - */ - static String androidType(Integer dartIndex) throws IllegalArgumentException { - if (dartIndex == null) { - return null; - } - - switch (dartIndex) { - case 0: - return Environment.DIRECTORY_MUSIC; - case 1: - return Environment.DIRECTORY_PODCASTS; - case 2: - return Environment.DIRECTORY_RINGTONES; - case 3: - return Environment.DIRECTORY_ALARMS; - case 4: - return Environment.DIRECTORY_NOTIFICATIONS; - case 5: - return Environment.DIRECTORY_PICTURES; - case 6: - return Environment.DIRECTORY_MOVIES; - case 7: - return Environment.DIRECTORY_DOWNLOADS; - case 8: - return Environment.DIRECTORY_DCIM; - case 9: - return Environment.DIRECTORY_DOCUMENTS; - default: - throw new IllegalArgumentException("Unknown index: " + dartIndex); - } - } -} diff --git a/packages/path_provider/path_provider_android/android/src/test/java/io/flutter/plugins/pathprovider/PathProviderPluginTest.java b/packages/path_provider/path_provider_android/android/src/test/java/io/flutter/plugins/pathprovider/PathProviderPluginTest.java new file mode 100644 index 000000000000..d19b7cab6a80 --- /dev/null +++ b/packages/path_provider/path_provider_android/android/src/test/java/io/flutter/plugins/pathprovider/PathProviderPluginTest.java @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.pathprovider; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class PathProviderPluginTest { + @org.junit.Test + public void testStorageDirectoryTypeTranslation() { + final PathProviderPlugin plugin = new PathProviderPlugin(); + assertNull(plugin.getStorageDirectoryString(Messages.StorageDirectory.ROOT)); + assertEquals("music", plugin.getStorageDirectoryString(Messages.StorageDirectory.MUSIC)); + assertEquals("podcasts", plugin.getStorageDirectoryString(Messages.StorageDirectory.PODCASTS)); + assertEquals( + "ringtones", plugin.getStorageDirectoryString(Messages.StorageDirectory.RINGTONES)); + assertEquals("alarms", plugin.getStorageDirectoryString(Messages.StorageDirectory.ALARMS)); + assertEquals( + "notifications", plugin.getStorageDirectoryString(Messages.StorageDirectory.NOTIFICATIONS)); + assertEquals("pictures", plugin.getStorageDirectoryString(Messages.StorageDirectory.PICTURES)); + assertEquals("movies", plugin.getStorageDirectoryString(Messages.StorageDirectory.MOVIES)); + assertEquals( + "downloads", plugin.getStorageDirectoryString(Messages.StorageDirectory.DOWNLOADS)); + assertEquals("dcim", plugin.getStorageDirectoryString(Messages.StorageDirectory.DCIM)); + } +} diff --git a/packages/path_provider/path_provider_android/android/src/test/java/io/flutter/plugins/pathprovider/StorageDirectoryMapperTest.java b/packages/path_provider/path_provider_android/android/src/test/java/io/flutter/plugins/pathprovider/StorageDirectoryMapperTest.java deleted file mode 100644 index 7469c545b817..000000000000 --- a/packages/path_provider/path_provider_android/android/src/test/java/io/flutter/plugins/pathprovider/StorageDirectoryMapperTest.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins.pathprovider; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import android.os.Environment; -import org.junit.Test; - -public class StorageDirectoryMapperTest { - @org.junit.Test - public void testAndroidType_null() { - assertNull(StorageDirectoryMapper.androidType(null)); - } - - @org.junit.Test - public void testAndroidType_valid() { - assertEquals(Environment.DIRECTORY_MUSIC, StorageDirectoryMapper.androidType(0)); - assertEquals(Environment.DIRECTORY_PODCASTS, StorageDirectoryMapper.androidType(1)); - assertEquals(Environment.DIRECTORY_RINGTONES, StorageDirectoryMapper.androidType(2)); - assertEquals(Environment.DIRECTORY_ALARMS, StorageDirectoryMapper.androidType(3)); - assertEquals(Environment.DIRECTORY_NOTIFICATIONS, StorageDirectoryMapper.androidType(4)); - assertEquals(Environment.DIRECTORY_PICTURES, StorageDirectoryMapper.androidType(5)); - assertEquals(Environment.DIRECTORY_MOVIES, StorageDirectoryMapper.androidType(6)); - assertEquals(Environment.DIRECTORY_DOWNLOADS, StorageDirectoryMapper.androidType(7)); - assertEquals(Environment.DIRECTORY_DCIM, StorageDirectoryMapper.androidType(8)); - } - - @Test - public void testAndroidType_invalid() { - try { - assertEquals(Environment.DIRECTORY_DCIM, StorageDirectoryMapper.androidType(10)); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("Unknown index: " + 10, e.getMessage()); - } - } -} diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index d46959f9cd75..9674e6a17036 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.2.13 +version: 2.2.15 environment: sdk: ^3.5.0 diff --git a/packages/path_provider/path_provider_foundation/CHANGELOG.md b/packages/path_provider/path_provider_foundation/CHANGELOG.md index 8846f5503ae5..39374d87dcb6 100644 --- a/packages/path_provider/path_provider_foundation/CHANGELOG.md +++ b/packages/path_provider/path_provider_foundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.1 +* Updates to Pigeon v22. * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 2.4.0 diff --git a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/messages.g.swift b/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/messages.g.swift index 4af5ac366630..c12242270baf 100644 --- a/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/messages.g.swift +++ b/packages/path_provider/path_provider_foundation/darwin/path_provider_foundation/Sources/path_provider_foundation/messages.g.swift @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.3), do not edit directly. +// Autogenerated from Pigeon (v22.6.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -14,11 +14,36 @@ import Foundation #error("Unsupported platform.") #endif +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + private func wrapResult(_ result: Any?) -> [Any?] { return [result] } private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } if let flutterError = error as? FlutterError { return [ flutterError.code, @@ -33,6 +58,10 @@ private func wrapError(_ error: Any) -> [Any?] { ] } +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } return value as! T? @@ -46,6 +75,47 @@ enum DirectoryType: Int { case temp = 4 case applicationCache = 5 } + +private class messagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return DirectoryType(rawValue: enumResultAsInt) + } + return nil + default: + return super.readValue(ofType: type) + } + } +} + +private class messagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? DirectoryType { + super.writeByte(129) + super.writeValue(value.rawValue) + } else { + super.writeValue(value) + } + } +} + +private class messagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return messagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return messagesPigeonCodecWriter(data: data) + } +} + +class messagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = messagesPigeonCodec(readerWriter: messagesPigeonCodecReaderWriter()) +} + /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol PathProviderApi { func getDirectoryPath(type: DirectoryType) throws -> String? @@ -54,15 +124,21 @@ protocol PathProviderApi { /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. class PathProviderApiSetup { - /// The codec used by PathProviderApi. + static var codec: FlutterStandardMessageCodec { messagesPigeonCodec.shared } /// Sets up an instance of `PathProviderApi` to handle messages through the `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: PathProviderApi?) { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: PathProviderApi?, + messageChannelSuffix: String = "" + ) { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let getDirectoryPathChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.PathProviderApi.getDirectoryPath", binaryMessenger: binaryMessenger) + name: + "dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getDirectoryPath\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { getDirectoryPathChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let typeArg = DirectoryType(rawValue: args[0] as! Int)! + let typeArg = args[0] as! DirectoryType do { let result = try api.getDirectoryPath(type: typeArg) reply(wrapResult(result)) @@ -74,7 +150,9 @@ class PathProviderApiSetup { getDirectoryPathChannel.setMessageHandler(nil) } let getContainerPathChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.PathProviderApi.getContainerPath", binaryMessenger: binaryMessenger) + name: + "dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getContainerPath\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { getContainerPathChannel.setMessageHandler { message, reply in let args = message as! [Any?] diff --git a/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/project.pbxproj index 28279be7ecbf..7d739ae8b36f 100644 --- a/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 569E86265D93B926F433B2DF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 479D5DD53D431F6BBABA2E43 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -81,6 +82,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 569E86265D93B926F433B2DF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -199,13 +201,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 246FA3B3BBF06301555F5A51 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -217,7 +221,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1400; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 3380327329784D96002D32AE = { @@ -239,6 +243,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -271,23 +278,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 246FA3B3BBF06301555F5A51 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -471,7 +461,6 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - // DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -646,7 +635,6 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - // DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -670,7 +658,6 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - // DEVELOPMENT_TEAM = S8QB4VV633; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -719,6 +706,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 33be10b23a2b..3ea14b590776 100644 --- a/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/path_provider/path_provider_foundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/path_provider/path_provider_foundation/example/ios/Runner/AppDelegate.swift b/packages/path_provider/path_provider_foundation/example/ios/Runner/AppDelegate.swift index d83c0ff0beea..4580c8e76da4 100644 --- a/packages/path_provider/path_provider_foundation/example/ios/Runner/AppDelegate.swift +++ b/packages/path_provider/path_provider_foundation/example/ios/Runner/AppDelegate.swift @@ -5,7 +5,7 @@ import Flutter import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/project.pbxproj b/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/project.pbxproj index e6803a68b24b..e4e9d6d23c33 100644 --- a/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 33EBD3AA26728EA70013E557 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33EBD3A926728EA70013E557 /* RunnerTests.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; FEE1C654F5DF2F210CC17B17 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA0C143378C83246316BE4F7 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ @@ -96,6 +97,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 23F6FAA3AF82DFCF2B7DD79A /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -222,7 +224,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 7413A74A1ECFDFE67CD0521B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -230,6 +231,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* path_provider_example.app */; productType = "com.apple.product-type.application"; @@ -260,7 +264,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1250; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -292,6 +296,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -361,24 +368,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; }; - 7413A74A1ECFDFE67CD0521B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 74960BD2BEA7516F537D0F92 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -812,6 +801,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c35107a8e2d8..11f7b660f6ec 100644 --- a/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/path_provider/path_provider_foundation/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/path_provider/path_provider_foundation/example/macos/Runner/AppDelegate.swift b/packages/path_provider/path_provider_foundation/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/path_provider/path_provider_foundation/example/macos/Runner/AppDelegate.swift +++ b/packages/path_provider/path_provider_foundation/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/path_provider/path_provider_foundation/lib/messages.g.dart b/packages/path_provider/path_provider_foundation/lib/messages.g.dart index 3fdbadd5ae25..dbe046b89ad2 100644 --- a/packages/path_provider/path_provider_foundation/lib/messages.g.dart +++ b/packages/path_provider/path_provider_foundation/lib/messages.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.3), do not edit directly. +// Autogenerated from Pigeon (v22.6.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -11,6 +11,24 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + enum DirectoryType { applicationDocuments, applicationSupport, @@ -20,57 +38,93 @@ enum DirectoryType { applicationCache, } +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is DirectoryType) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : DirectoryType.values[value]; + default: + return super.readValueOfType(type, buffer); + } + } +} + class PathProviderApi { /// Constructor for [PathProviderApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - PathProviderApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + PathProviderApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec codec = StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - Future getDirectoryPath(DirectoryType arg_type) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PathProviderApi.getDirectoryPath', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_type.index]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + final String pigeonVar_messageChannelSuffix; + + Future getDirectoryPath(DirectoryType type) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getDirectoryPath$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([type]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { - return (replyList[0] as String?); + return (pigeonVar_replyList[0] as String?); } } - Future getContainerPath(String arg_appGroupIdentifier) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PathProviderApi.getContainerPath', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_appGroupIdentifier]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future getContainerPath(String appGroupIdentifier) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getContainerPath$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([appGroupIdentifier]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { - return (replyList[0] as String?); + return (pigeonVar_replyList[0] as String?); } } } diff --git a/packages/path_provider/path_provider_foundation/pubspec.yaml b/packages/path_provider/path_provider_foundation/pubspec.yaml index cfeac45f6cd4..02a4dcbdb4af 100644 --- a/packages/path_provider/path_provider_foundation/pubspec.yaml +++ b/packages/path_provider/path_provider_foundation/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_foundation description: iOS and macOS implementation of the path_provider plugin repository: https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.4.0 +version: 2.4.1 environment: sdk: ^3.3.0 @@ -32,7 +32,7 @@ dev_dependencies: sdk: flutter mockito: ^5.4.4 path: ^1.8.0 - pigeon: ^10.1.3 + pigeon: ^22.6.4 topics: - files diff --git a/packages/path_provider/path_provider_foundation/test/messages_test.g.dart b/packages/path_provider/path_provider_foundation/test/messages_test.g.dart index c1477dfe4157..36b84bc1f94a 100644 --- a/packages/path_provider/path_provider_foundation/test/messages_test.g.dart +++ b/packages/path_provider/path_provider_foundation/test/messages_test.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.3), do not edit directly. +// Autogenerated from Pigeon (v22.6.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -13,59 +13,111 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider_foundation/messages.g.dart'; +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is DirectoryType) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : DirectoryType.values[value]; + default: + return super.readValueOfType(type, buffer); + } + } +} + abstract class TestPathProviderApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec codec = StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); String? getDirectoryPath(DirectoryType type); String? getContainerPath(String appGroupIdentifier); - static void setup(TestPathProviderApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setUp( + TestPathProviderApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PathProviderApi.getDirectoryPath', codec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getDirectoryPath$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.PathProviderApi.getDirectoryPath was null.'); + 'Argument for dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getDirectoryPath was null.'); final List args = (message as List?)!; - final DirectoryType? arg_type = - args[0] == null ? null : DirectoryType.values[args[0] as int]; + final DirectoryType? arg_type = (args[0] as DirectoryType?); assert(arg_type != null, - 'Argument for dev.flutter.pigeon.PathProviderApi.getDirectoryPath was null, expected non-null DirectoryType.'); - final String? output = api.getDirectoryPath(arg_type!); - return [output]; + 'Argument for dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getDirectoryPath was null, expected non-null DirectoryType.'); + try { + final String? output = api.getDirectoryPath(arg_type!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.PathProviderApi.getContainerPath', codec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getContainerPath$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.PathProviderApi.getContainerPath was null.'); + 'Argument for dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getContainerPath was null.'); final List args = (message as List?)!; final String? arg_appGroupIdentifier = (args[0] as String?); assert(arg_appGroupIdentifier != null, - 'Argument for dev.flutter.pigeon.PathProviderApi.getContainerPath was null, expected non-null String.'); - final String? output = api.getContainerPath(arg_appGroupIdentifier!); - return [output]; + 'Argument for dev.flutter.pigeon.path_provider_foundation.PathProviderApi.getContainerPath was null, expected non-null String.'); + try { + final String? output = + api.getContainerPath(arg_appGroupIdentifier!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } diff --git a/packages/path_provider/path_provider_foundation/test/path_provider_foundation_test.dart b/packages/path_provider/path_provider_foundation/test/path_provider_foundation_test.dart index e13e182a4132..4006af774f51 100644 --- a/packages/path_provider/path_provider_foundation/test/path_provider_foundation_test.dart +++ b/packages/path_provider/path_provider_foundation/test/path_provider_foundation_test.dart @@ -28,7 +28,7 @@ void main() { setUp(() async { testRoot = Directory.systemTemp.createTempSync(); mockApi = MockTestPathProviderApi(); - TestPathProviderApi.setup(mockApi); + TestPathProviderApi.setUp(mockApi); }); tearDown(() { diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index d93dd1bb1a3a..f7157e5867c4 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,22 @@ +## 22.7.0 + +* [swift, kotlin] Adds event channel support. +* [swift, kotlin] Adds `sealed` class inheritance support. +* [swift] Updates codec class names to be upper camel case. + +## 22.6.4 + +* [swift] Fixes the channel names of the named constructors of ProxyApis. + +## 22.6.3 + +* Replaces deprecated collection method usage. + +## 22.6.2 + +* Removes the `@protected` annotation from the InstanceManager field of the + `PigeonInternalProxyApiBaseClass`. + ## 22.6.1 * [gobject] Moves class declarations to the header to work around a bug in some diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index cc375f5e2faa..39df85d65f41 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -26,6 +26,8 @@ Pigeon uses the `StandardMessageCodec` so it supports Custom classes, nested datatypes, and enums are also supported. +Basic inheritance with empty `sealed` parent classes is allowed only in the Swift, Kotlin, and Dart generators. + Nullable enums in Objective-C generated code will be wrapped in a class to allow for nullability. By default, custom classes in Swift are defined as structs. @@ -104,9 +106,10 @@ to the api to allow for multiple instances to be created and operate in parallel 1) Method declarations on the API classes should have arguments and a return value whose types are defined in the file, are supported datatypes, or are `void`. -1) Generics are supported, but can currently only be used with nullable types - (example: `List`). -1) Objc and Swift have special naming conventions that can be utilized with the +1) Event channels are supported only on the Swift, Kotlin, and Dart generators. +1) Event channel methods should be wrapped in an `abstract class` with the metadata `@EventChannelApi`. +1) Event channel definitions should not include the `Stream` return type, just the type that is being streamed. +1) Objective-C and Swift have special naming conventions that can be utilized with the `@ObjCSelector` and `@SwiftFunction` respectively. ### Flutter calling into iOS steps diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index bd5e4bd116af..d885e7e304b7 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -185,7 +185,7 @@ class PigeonApiImplementation : public ExampleHostApi { } void SendMessage(const MessageData& message, std::function reply)> result) { - if (message.code == Code.kOne) { + if (message.code() == Code::kOne) { result(FlutterError("code", "message", "details")); return; } @@ -281,10 +281,10 @@ private class PigeonFlutterApi { } func callFlutterMethod( - aString aStringArg: String?, completion: @escaping (Result) -> Void + aString aStringArg: String?, completion: @escaping (Result) -> Void ) { flutterAPI.flutterMethod(aString: aStringArg) { - completion(.success($0)) + completion($0) } } } @@ -294,16 +294,15 @@ private class PigeonFlutterApi { ```kotlin -private class PigeonFlutterApi { - +private class PigeonFlutterApi(binding: FlutterPlugin.FlutterPluginBinding) { var flutterApi: MessageFlutterApi? = null - constructor(binding: FlutterPlugin.FlutterPluginBinding) { - flutterApi = MessageFlutterApi(binding.getBinaryMessenger()) + init { + flutterApi = MessageFlutterApi(binding.binaryMessenger) } fun callFlutterMethod(aString: String, callback: (Result) -> Unit) { - flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo)) } + flutterApi!!.flutterMethod(aString) { echo -> callback(echo) } } } ``` @@ -312,12 +311,22 @@ private class PigeonFlutterApi { ```c++ -void TestPlugin::CallFlutterMethod( - String aString, std::function reply)> result) { - MessageFlutterApi->FlutterMethod( - aString, [result](String echo) { result(echo); }, - [result](const FlutterError& error) { result(error); }); -} +class PigeonFlutterApi { + public: + PigeonFlutterApi(flutter::BinaryMessenger* messenger) + : flutterApi_(std::make_unique(messenger)) {} + + void CallFlutterMethod( + const std::string& a_string, + std::function reply)> result) { + flutterApi_->FlutterMethod( + &a_string, [result](const std::string& echo) { result(echo); }, + [result](const FlutterError& error) { result(error); }); + } + + private: + std::unique_ptr flutterApi_; +}; ``` ### GObject @@ -351,6 +360,119 @@ pigeon_example_package_message_flutter_api_flutter_method( self->flutter_api, "hello", nullptr, flutter_method_cb, self); ``` +## Event Channel Example + +This example gives a basic overview of how to use Pigeon to set up an event channel. + +### Dart input + + +```dart +@EventChannelApi() +abstract class EventChannelMethods { + PlatformEvent streamEvents(); +} +``` + +### Dart + +The generated Dart code will include a method that returns a `Stream` when invoked. + + +```dart +Stream getEventStream() async* { + final Stream events = streamEvents(); + await for (final PlatformEvent event in events) { + switch (event) { + case IntEvent(): + final int intData = event.data; + yield '$intData, '; + case StringEvent(): + final String stringData = event.data; + yield '$stringData, '; + } + } +} +``` + +### Swift + +Define the stream handler class that will handle the events. + + +```swift +class EventListener: StreamEventsStreamHandler { + var eventSink: PigeonEventSink? + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + eventSink = sink + } + + func onIntEvent(event: Int64) { + if let eventSink = eventSink { + eventSink.success(IntEvent(data: event)) + } + } + + func onStringEvent(event: String) { + if let eventSink = eventSink { + eventSink.success(StringEvent(data: event)) + } + } + + func onEventsDone() { + eventSink?.endOfStream() + eventSink = nil + } +} +``` + +Register the handler with the generated method. + + +```swift +let eventListener = EventListener() +StreamEventsStreamHandler.register( + with: controller.binaryMessenger, streamHandler: eventListener) +``` + +### Kotlin + +Define the stream handler class that will handle the events. + + +```kotlin +class EventListener : StreamEventsStreamHandler() { + private var eventSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + eventSink = sink + } + + fun onIntEvent(event: Long) { + eventSink?.success(IntEvent(data = event)) + } + + fun onStringEvent(event: String) { + eventSink?.success(StringEvent(data = event)) + } + + fun onEventsDone() { + eventSink?.endOfStream() + eventSink = null + } +} +``` + + +Register the handler with the generated method. + + +```kotlin +val eventListener = EventListener() +StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, eventListener) +``` + ## Swift / Kotlin Plugin Example A downloadable example of using Pigeon to create a Flutter Plugin with Swift and diff --git a/packages/pigeon/example/app/.gitignore b/packages/pigeon/example/app/.gitignore index 24476c5d1eb5..6c319542b342 100644 --- a/packages/pigeon/example/app/.gitignore +++ b/packages/pigeon/example/app/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt new file mode 100644 index 000000000000..ead5fe00f1b2 --- /dev/null +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt @@ -0,0 +1,134 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +/** + * Generated class from Pigeon that represents data sent in messages. This class should not be + * extended by any user class outside of the generated file. + */ +sealed class PlatformEvent +/** Generated class from Pigeon that represents data sent in messages. */ +data class IntEvent(val data: Long) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): IntEvent { + val data = pigeonVar_list[0] as Long + return IntEvent(data) + } + } + + fun toList(): List { + return listOf( + data, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class StringEvent(val data: String) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): StringEvent { + val data = pigeonVar_list[0] as String + return StringEvent(data) + } + } + + fun toList(): List { + return listOf( + data, + ) + } +} + +private open class EventChannelMessagesPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { IntEvent.fromList(it) } + } + 130.toByte() -> { + return (readValue(buffer) as? List)?.let { StringEvent.fromList(it) } + } + else -> super.readValueOfType(type, buffer) + } + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is IntEvent -> { + stream.write(129) + writeValue(stream, value.toList()) + } + is StringEvent -> { + stream.write(130) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +val EventChannelMessagesPigeonMethodCodec = StandardMethodCodec(EventChannelMessagesPigeonCodec()) + +private class PigeonStreamHandler(val wrapper: PigeonEventChannelWrapper) : + EventChannel.StreamHandler { + var pigeonSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + pigeonSink = PigeonEventSink(sink) + wrapper.onListen(p0, pigeonSink!!) + } + + override fun onCancel(p0: Any?) { + pigeonSink = null + wrapper.onCancel(p0) + } +} + +interface PigeonEventChannelWrapper { + open fun onListen(p0: Any?, sink: PigeonEventSink) {} + + open fun onCancel(p0: Any?) {} +} + +class PigeonEventSink(private val sink: EventChannel.EventSink) { + fun success(value: T) { + sink.success(value) + } + + fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + sink.error(errorCode, errorMessage, errorDetails) + } + + fun endOfStream() { + sink.endOfStream() + } +} + +abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { + companion object { + fun register( + messenger: BinaryMessenger, + streamHandler: StreamEventsStreamHandler, + instanceName: String = "" + ) { + var channelName: String = + "dev.flutter.pigeon.pigeon_example_package.EventChannelMethods.streamEvents" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val internalStreamHandler = PigeonStreamHandler(streamHandler) + EventChannel(messenger, channelName, EventChannelMessagesPigeonMethodCodec) + .setStreamHandler(internalStreamHandler) + } + } +} diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt index 6882861350b8..0b9e4a1c534d 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt @@ -6,9 +6,15 @@ package dev.flutter.pigeon_example_app import ExampleHostApi import FlutterError +import IntEvent import MessageData import MessageFlutterApi -import androidx.annotation.NonNull +import PigeonEventSink +import PlatformEvent +import StreamEventsStreamHandler +import StringEvent +import android.os.Handler +import android.os.Looper import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.plugins.FlutterPlugin @@ -37,25 +43,79 @@ private class PigeonApiImplementation : ExampleHostApi { // #enddocregion kotlin-class // #docregion kotlin-class-flutter -private class PigeonFlutterApi { - +private class PigeonFlutterApi(binding: FlutterPlugin.FlutterPluginBinding) { var flutterApi: MessageFlutterApi? = null - constructor(binding: FlutterPlugin.FlutterPluginBinding) { - flutterApi = MessageFlutterApi(binding.getBinaryMessenger()) + init { + flutterApi = MessageFlutterApi(binding.binaryMessenger) } fun callFlutterMethod(aString: String, callback: (Result) -> Unit) { - flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo)) } + flutterApi!!.flutterMethod(aString) { echo -> callback(echo) } } } // #enddocregion kotlin-class-flutter +// #docregion kotlin-class-event +class EventListener : StreamEventsStreamHandler() { + private var eventSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + eventSink = sink + } + + fun onIntEvent(event: Long) { + eventSink?.success(IntEvent(data = event)) + } + + fun onStringEvent(event: String) { + eventSink?.success(StringEvent(data = event)) + } + + fun onEventsDone() { + eventSink?.endOfStream() + eventSink = null + } +} +// #enddocregion kotlin-class-event + +fun sendEvents(eventListener: EventListener) { + val handler = Handler(Looper.getMainLooper()) + var count: Int = 0 + val r: Runnable = + object : Runnable { + override fun run() { + if (count >= 100) { + handler.post { eventListener.onEventsDone() } + } else { + if (count % 2 == 0) { + handler.post { + eventListener.onIntEvent(count.toLong()) + count++ + } + } else { + handler.post { + eventListener.onStringEvent(count.toString()) + count++ + } + } + handler.postDelayed(this, 1000) + } + } + } + handler.post(r) +} + class MainActivity : FlutterActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) val api = PigeonApiImplementation() ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api) + // #docregion kotlin-init-event + val eventListener = EventListener() + StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, eventListener) + // #enddocregion kotlin-init-event + sendEvents(eventListener) } } diff --git a/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist b/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist index 9625e105df39..7c5696400627 100644 --- a/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/packages/pigeon/example/app/ios/Podfile b/packages/pigeon/example/app/ios/Podfile index 5fbdfa333224..01d4aa611bb9 100644 --- a/packages/pigeon/example/app/ios/Podfile +++ b/packages/pigeon/example/app/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj index 6541f88ab296..121307f17ef8 100644 --- a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj @@ -3,14 +3,17 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3368472729F02D040090029A /* Messages.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3368472629F02D040090029A /* Messages.g.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3EE8794C275F32088AD591EB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A650BDD6F68BD8FFBF3F1780 /* Pods_Runner.framework */; }; + 474BFAAB2D01312700CB80BA /* EventChannelMessages.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = 474BFAAA2D01312700CB80BA /* EventChannelMessages.g.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -32,8 +35,10 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 27CAC22A75533A4A7E343992 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 3368472629F02D040090029A /* Messages.g.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Messages.g.swift; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 474BFAAA2D01312700CB80BA /* EventChannelMessages.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventChannelMessages.g.swift; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -44,6 +49,9 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A650BDD6F68BD8FFBF3F1780 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AE98FBE14AEFBBA591D3392B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + C9FCCD56B6FEFE59650B7D41 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,12 +59,32 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + 3EE8794C275F32088AD591EB /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2DE1FB11A4049DEDD8609D3D /* Frameworks */ = { + isa = PBXGroup; + children = ( + A650BDD6F68BD8FFBF3F1780 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5AC2660E9756DE131CECB642 /* Pods */ = { + isa = PBXGroup; + children = ( + AE98FBE14AEFBBA591D3392B /* Pods-Runner.debug.xcconfig */, + 27CAC22A75533A4A7E343992 /* Pods-Runner.release.xcconfig */, + C9FCCD56B6FEFE59650B7D41 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -74,6 +102,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 5AC2660E9756DE131CECB642 /* Pods */, + 2DE1FB11A4049DEDD8609D3D /* Frameworks */, ); sourceTree = ""; }; @@ -97,6 +127,7 @@ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 3368472629F02D040090029A /* Messages.g.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + 474BFAAA2D01312700CB80BA /* EventChannelMessages.g.swift */, ); path = Runner; sourceTree = ""; @@ -108,6 +139,7 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 11874A3E3724772837B0A747 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -120,6 +152,9 @@ dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -130,7 +165,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -148,6 +183,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -172,6 +210,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 11874A3E3724772837B0A747 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -213,6 +273,7 @@ 3368472729F02D040090029A /* Messages.g.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 474BFAAB2D01312700CB80BA /* EventChannelMessages.g.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -279,7 +340,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -356,7 +417,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -405,7 +466,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -483,6 +544,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/pigeon/example/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pigeon/example/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e42adcb34c2d..d795332e1b7b 100644 --- a/packages/pigeon/example/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/pigeon/example/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/pigeon/example/app/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/pigeon/example/app/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16ed0f..21a3cc14c74e 100644 --- a/packages/pigeon/example/app/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/pigeon/example/app/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index 51119e23fa38..7948edfdd150 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -37,16 +37,63 @@ private class PigeonFlutterApi { } func callFlutterMethod( - aString aStringArg: String?, completion: @escaping (Result) -> Void + aString aStringArg: String?, completion: @escaping (Result) -> Void ) { flutterAPI.flutterMethod(aString: aStringArg) { - completion(.success($0)) + completion($0) } } } // #enddocregion swift-class-flutter -@UIApplicationMain +// #docregion swift-class-event +class EventListener: StreamEventsStreamHandler { + var eventSink: PigeonEventSink? + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + eventSink = sink + } + + func onIntEvent(event: Int64) { + if let eventSink = eventSink { + eventSink.success(IntEvent(data: event)) + } + } + + func onStringEvent(event: String) { + if let eventSink = eventSink { + eventSink.success(StringEvent(data: event)) + } + } + + func onEventsDone() { + eventSink?.endOfStream() + eventSink = nil + } +} +// #enddocregion swift-class-event + +func sendEvents(_ eventListener: EventListener) { + var timer: Timer? + var count: Int64 = 0 + timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in + DispatchQueue.main.async { + if count >= 100 { + eventListener.onEventsDone() + timer?.invalidate() + } else { + if (count % 2) == 0 { + eventListener.onIntEvent(event: Int64(count)) + } else { + eventListener.onStringEvent(event: String(count)) + } + count += 1 + } + } + } +} + +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, @@ -57,6 +104,12 @@ private class PigeonFlutterApi { let controller = window?.rootViewController as! FlutterViewController let api = PigeonApiImplementation() ExampleHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api) + // #docregion swift-init-event + let eventListener = EventListener() + StreamEventsStreamHandler.register( + with: controller.binaryMessenger, streamHandler: eventListener) + // #enddocregion swift-init-event + sendEvents(eventListener) return super.application(application, didFinishLaunchingWithOptions: launchOptions) diff --git a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift new file mode 100644 index 000000000000..16d1ab990486 --- /dev/null +++ b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift @@ -0,0 +1,179 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +/// Generated class from Pigeon that represents data sent in messages. +/// This protocol should not be extended by any user class outside of the generated file. +protocol PlatformEvent { + +} + +/// Generated class from Pigeon that represents data sent in messages. +struct IntEvent: PlatformEvent { + var data: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> IntEvent? { + let data = pigeonVar_list[0] as! Int64 + + return IntEvent( + data: data + ) + } + func toList() -> [Any?] { + return [ + data + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct StringEvent: PlatformEvent { + var data: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> StringEvent? { + let data = pigeonVar_list[0] as! String + + return StringEvent( + data: data + ) + } + func toList() -> [Any?] { + return [ + data + ] + } +} + +private class EventChannelMessagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return IntEvent.fromList(self.readValue() as! [Any?]) + case 130: + return StringEvent.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class EventChannelMessagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? IntEvent { + super.writeByte(129) + super.writeValue(value.toList()) + } else if let value = value as? StringEvent { + super.writeByte(130) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class EventChannelMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return EventChannelMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return EventChannelMessagesPigeonCodecWriter(data: data) + } +} + +class EventChannelMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = EventChannelMessagesPigeonCodec( + readerWriter: EventChannelMessagesPigeonCodecReaderWriter()) +} + +var eventChannelMessagesPigeonMethodCodec = FlutterStandardMethodCodec( + readerWriter: EventChannelMessagesPigeonCodecReaderWriter()) + +private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } +} + +class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} +} + +class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + +} + +class StreamEventsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: StreamEventsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_example_package.EventChannelMethods.streamEvents" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelMessagesPigeonMethodCodec) + channel.setStreamHandler(internalStreamHandler) + } +} diff --git a/packages/pigeon/example/app/lib/main.dart b/packages/pigeon/example/app/lib/main.dart index cc158abe0c1f..8c84cd7b906b 100644 --- a/packages/pigeon/example/app/lib/main.dart +++ b/packages/pigeon/example/app/lib/main.dart @@ -4,9 +4,12 @@ // ignore_for_file: public_member_api_docs +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'src/event_channel_messages.g.dart'; import 'src/messages.g.dart'; // #docregion main-dart-flutter @@ -85,6 +88,22 @@ class _MyHomePageState extends State { } // #enddocregion main-dart + // #docregion main-dart-event + Stream getEventStream() async* { + final Stream events = streamEvents(); + await for (final PlatformEvent event in events) { + switch (event) { + case IntEvent(): + final int intData = event.data; + yield '$intData, '; + case StringEvent(): + final String stringData = event.data; + yield '$stringData, '; + } + } + } + // #enddocregion main-dart-event + @override void initState() { super.initState(); @@ -114,6 +133,20 @@ class _MyHomePageState extends State { _hostCallResult ?? 'Waiting for host language...', ), if (_hostCallResult == null) const CircularProgressIndicator(), + if (Platform.isAndroid || Platform.isIOS) + StreamBuilder( + stream: getEventStream(), + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return Text(snapshot.data ?? ''); + } else { + return const CircularProgressIndicator(); + } + }, + ) + else + const Text('event channels are not supported on this platform') ], ), ), diff --git a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart new file mode 100644 index 000000000000..c180a9d742ac --- /dev/null +++ b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart @@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +sealed class PlatformEvent {} + +class IntEvent extends PlatformEvent { + IntEvent({ + required this.data, + }); + + int data; + + Object encode() { + return [ + data, + ]; + } + + static IntEvent decode(Object result) { + result as List; + return IntEvent( + data: result[0]! as int, + ); + } +} + +class StringEvent extends PlatformEvent { + StringEvent({ + required this.data, + }); + + String data; + + Object encode() { + return [ + data, + ]; + } + + static StringEvent decode(Object result) { + result as List; + return StringEvent( + data: result[0]! as String, + ); + } +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is IntEvent) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is StringEvent) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return IntEvent.decode(readValue(buffer)!); + case 130: + return StringEvent.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +const StandardMethodCodec pigeonMethodCodec = + StandardMethodCodec(_PigeonCodec()); + +Stream streamEvents({String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.$instanceName'; + } + const EventChannel streamEventsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_example_package.EventChannelMethods.streamEvents', + pigeonMethodCodec); + return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { + return event as PlatformEvent; + }); +} diff --git a/packages/pigeon/example/app/macos/Runner.xcodeproj/project.pbxproj b/packages/pigeon/example/app/macos/Runner.xcodeproj/project.pbxproj index 3f2cecaa30e6..ac0c3358e4c3 100644 --- a/packages/pigeon/example/app/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/pigeon/example/app/macos/Runner.xcodeproj/project.pbxproj @@ -205,7 +205,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/packages/pigeon/example/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pigeon/example/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e7dea9660a74..56a40bab13cc 100644 --- a/packages/pigeon/example/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/pigeon/example/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/packages/pigeon/example/app/macos/Runner/AppDelegate.swift b/packages/pigeon/example/app/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/pigeon/example/app/macos/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/pigeon/example/app/macos/Runner/MainFlutterWindow.swift b/packages/pigeon/example/app/macos/Runner/MainFlutterWindow.swift index 5e5096a2d2ae..1aa7a4305a35 100644 --- a/packages/pigeon/example/app/macos/Runner/MainFlutterWindow.swift +++ b/packages/pigeon/example/app/macos/Runner/MainFlutterWindow.swift @@ -9,6 +9,21 @@ private class PigeonApiImplementation: ExampleHostApi { func getHostLanguage() throws -> String { return "Swift" } + + func add(_ a: Int64, to b: Int64) throws -> Int64 { + if a < 0 || b < 0 { + throw PigeonError(code: "code", message: "message", details: "details") + } + return a + b + } + + func sendMessage(message: MessageData, completion: @escaping (Result) -> Void) { + if message.code == Code.one { + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) + return + } + completion(.success(true)) + } } class MainFlutterWindow: NSWindow { diff --git a/packages/pigeon/example/app/pigeons/event_channel_messages.dart b/packages/pigeon/example/app/pigeons/event_channel_messages.dart new file mode 100644 index 000000000000..c0b33292235f --- /dev/null +++ b/packages/pigeon/example/app/pigeons/event_channel_messages.dart @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/event_channel_messages.g.dart', + dartOptions: DartOptions(), + cppOptions: CppOptions(namespace: 'pigeon_example'), + kotlinOut: + 'android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt', + kotlinOptions: KotlinOptions( + includeErrorClass: false, + ), + swiftOut: 'ios/Runner/EventChannelMessages.g.swift', + swiftOptions: SwiftOptions( + includeErrorClass: false, + ), + copyrightHeader: 'pigeons/copyright.txt', + dartPackageName: 'pigeon_example_package', +)) + +// #docregion sealed-definitions +sealed class PlatformEvent {} + +class IntEvent extends PlatformEvent { + IntEvent(this.data); + int data; +} + +class StringEvent extends PlatformEvent { + StringEvent(this.data); + String data; +} +// #enddocregion sealed-definitions + +// #docregion event-definitions +@EventChannelApi() +abstract class EventChannelMethods { + PlatformEvent streamEvents(); +} +// #enddocregion event-definitions diff --git a/packages/pigeon/example/app/pigeons/messages.dart b/packages/pigeon/example/app/pigeons/messages.dart index 2112fed96371..421e6ce1c9f8 100644 --- a/packages/pigeon/example/app/pigeons/messages.dart +++ b/packages/pigeon/example/app/pigeons/messages.dart @@ -30,8 +30,6 @@ import 'package:pigeon/pigeon.dart'; )) // #enddocregion config -// This file and ./messages_test.dart must be identical below this line. - // #docregion host-definitions enum Code { one, two } diff --git a/packages/pigeon/example/app/windows/runner/flutter_window.cpp b/packages/pigeon/example/app/windows/runner/flutter_window.cpp index b7f7ab03595b..5918d55d0ec6 100644 --- a/packages/pigeon/example/app/windows/runner/flutter_window.cpp +++ b/packages/pigeon/example/app/windows/runner/flutter_window.cpp @@ -4,6 +4,8 @@ #include "flutter_window.h" +#include + #include #include @@ -11,8 +13,12 @@ #include "messages.g.h" namespace { +using pigeon_example::Code; using pigeon_example::ErrorOr; using pigeon_example::ExampleHostApi; +using pigeon_example::FlutterError; +using pigeon_example::MessageData; +using pigeon_example::MessageFlutterApi; // #docregion cpp-class class PigeonApiImplementation : public ExampleHostApi { @@ -29,7 +35,7 @@ class PigeonApiImplementation : public ExampleHostApi { } void SendMessage(const MessageData& message, std::function reply)> result) { - if (message.code == Code.kOne) { + if (message.code() == Code::kOne) { result(FlutterError("code", "message", "details")); return; } @@ -37,6 +43,26 @@ class PigeonApiImplementation : public ExampleHostApi { } }; // #enddocregion cpp-class + +// #docregion cpp-method-flutter +class PigeonFlutterApi { + public: + PigeonFlutterApi(flutter::BinaryMessenger* messenger) + : flutterApi_(std::make_unique(messenger)) {} + + void CallFlutterMethod( + const std::string& a_string, + std::function reply)> result) { + flutterApi_->FlutterMethod( + &a_string, [result](const std::string& echo) { result(echo); }, + [result](const FlutterError& error) { result(error); }); + } + + private: + std::unique_ptr flutterApi_; +}; +// #enddocregion cpp-method-flutter + } // namespace FlutterWindow::FlutterWindow(const flutter::DartProject& project) @@ -49,15 +75,6 @@ bool FlutterWindow::OnCreate() { return false; } - // #docregion cpp-method-flutter - void TestPlugin::CallFlutterMethod( - String aString, std::function reply)> result) { - MessageFlutterApi->FlutterMethod( - aString, [result](String echo) { result(echo); }, - [result](const FlutterError& error) { result(error); }); - } - // #enddocregion cpp-method-flutter - RECT frame = GetClientArea(); // The size here must match the window dimensions to avoid unnecessary surface diff --git a/packages/pigeon/example/pubspec.yaml b/packages/pigeon/example/pubspec.yaml deleted file mode 100644 index 4356a3636b1c..000000000000 --- a/packages/pigeon/example/pubspec.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: pigeon_example -description: example app to show basic usage of pigeon. -publish_to: none - -environment: - sdk: ^3.3.0 - -dependencies: - -dev_dependencies: - build_runner: ^2.1.10 diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart index 1c93d4630e30..5e65f31c98ac 100644 --- a/packages/pigeon/lib/ast.dart +++ b/packages/pigeon/lib/ast.dart @@ -341,6 +341,21 @@ class AstProxyApi extends Api { } } +/// Represents a collection of [Method]s that are wrappers for Event +class AstEventChannelApi extends Api { + /// Parametric constructor for [AstEventChannelApi]. + AstEventChannelApi({ + required super.name, + required super.methods, + super.documentationComments = const [], + }); + + @override + String toString() { + return '(EventChannelApi name:$name methods:$methods documentationComments:$documentationComments)'; + } +} + /// Represents a constructor for an API. class Constructor extends Method { /// Parametric constructor for [Constructor]. @@ -680,6 +695,9 @@ class Class extends Node { Class({ required this.name, required this.fields, + this.superClassName, + this.superClass, + this.isSealed = false, this.isReferenced = true, this.isSwiftClass = false, this.documentationComments = const [], @@ -691,6 +709,20 @@ class Class extends Node { /// All the fields contained in the class. List fields; + /// Name of parent class, will be empty when there is no super class. + String? superClassName; + + /// The definition of the parent class. + Class? superClass; + + /// List of class definitions of children. + /// + /// This is only meant to be used by sealed classes used in event channel methods. + List children = []; + + /// Whether the class is sealed. + bool isSealed; + /// Whether the class is referenced in any API. bool isReferenced; @@ -709,7 +741,7 @@ class Class extends Node { @override String toString() { - return '(Class name:$name fields:$fields documentationComments:$documentationComments)'; + return '(Class name:$name fields:$fields superClass:$superClassName children:$children isSealed:$isSealed isReferenced:$isReferenced documentationComments:$documentationComments)'; } } @@ -772,6 +804,10 @@ class Root extends Node { required this.classes, required this.apis, required this.enums, + this.containsHostApi = false, + this.containsFlutterApi = false, + this.containsProxyApi = false, + this.containsEventChannel = false, }); /// Factory function for generating an empty root, usually used when early errors are encountered. @@ -788,10 +824,25 @@ class Root extends Node { /// All of the enums contained in the AST. List enums; + /// Whether the root has any Host API definitions. + bool containsHostApi; + + /// Whether the root has any Flutter API definitions. + bool containsFlutterApi; + + /// Whether the root has any Proxy API definitions. + bool containsProxyApi; + + /// Whether the root has any event channel definitions. + bool containsEventChannel; + /// Returns true if the number of custom types would exceed the available enumerations /// on the standard codec. bool get requiresOverflowClass => - classes.length + enums.length >= totalCustomCodecKeysAllowed; + classes.length - _numberOfSealedClasses() + enums.length >= + totalCustomCodecKeysAllowed; + + int _numberOfSealedClasses() => classes.where((Class c) => c.isSealed).length; @override String toString() { diff --git a/packages/pigeon/lib/cpp_generator.dart b/packages/pigeon/lib/cpp_generator.dart index 96579cc23f9a..d911da474220 100644 --- a/packages/pigeon/lib/cpp_generator.dart +++ b/packages/pigeon/lib/cpp_generator.dart @@ -213,15 +213,8 @@ class CppHeaderGenerator extends StructuredGenerator { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - _writeFlutterError(indent); - if (hasHostApi) { + if (root.containsHostApi) { _writeErrorOr( indent, friends: root.apis @@ -229,9 +222,6 @@ class CppHeaderGenerator extends StructuredGenerator { .map((Api api) => api.name), ); } - if (hasFlutterApi) { - // Nothing yet. - } } @override @@ -1002,7 +992,7 @@ EncodableValue $_overflowClassName::FromEncodableList( required String dartPackageName, }) { final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); indent.newln(); if (root.requiresOverflowClass) { _writeCodecOverflowUtilities( diff --git a/packages/pigeon/lib/dart/templates.dart b/packages/pigeon/lib/dart/templates.dart index c78c4f566d22..942e9f00ddb9 100644 --- a/packages/pigeon/lib/dart/templates.dart +++ b/packages/pigeon/lib/dart/templates.dart @@ -255,7 +255,6 @@ abstract class $proxyApiBaseClassName { final BinaryMessenger? $_proxyApiBaseClassMessengerVarName; /// Maintains instances stored to communicate with native language objects. - @protected final $dartInstanceManagerClassName $_proxyApiBaseClassInstanceManagerVarName; /// Instantiates and returns a functionally identical object to oneself. diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index e8c7550cd708..abacf3dea31c 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -30,7 +30,10 @@ const DocumentCommentSpecification _docCommentSpec = DocumentCommentSpecification(_docCommentPrefix); /// The custom codec used for all pigeon APIs. -const String _pigeonCodec = '_PigeonCodec'; +const String _pigeonMessageCodec = '_PigeonCodec'; + +/// Name of field used for host API codec. +const String _pigeonMethodChannelCodec = 'pigeonMethodCodec'; const String _overflowClassName = '_PigeonCodecOverflow'; @@ -118,11 +121,10 @@ class DartGenerator extends StructuredGenerator { ); indent.newln(); - final bool hasProxyApi = root.apis.any((Api api) => api is AstProxyApi); indent.writeln( - "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer${hasProxyApi ? ', immutable, protected' : ''};"); + "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer${root.containsProxyApi ? ', immutable, protected' : ''};"); indent.writeln("import 'package:flutter/services.dart';"); - if (hasProxyApi) { + if (root.containsProxyApi) { indent.writeln( "import 'package:flutter/widgets.dart' show WidgetsFlutterBinding;", ); @@ -161,9 +163,16 @@ class DartGenerator extends StructuredGenerator { indent.newln(); addDocumentationComments( indent, classDefinition.documentationComments, _docCommentSpec); + final String sealed = classDefinition.isSealed ? 'sealed ' : ''; + final String implements = classDefinition.superClassName != null + ? 'extends ${classDefinition.superClassName} ' + : ''; - indent.write('class ${classDefinition.name} '); + indent.write('${sealed}class ${classDefinition.name} $implements'); indent.addScoped('{', '}', () { + if (classDefinition.fields.isEmpty) { + return; + } _writeConstructor(indent, classDefinition); indent.newln(); for (final NamedType field @@ -285,10 +294,12 @@ class DartGenerator extends StructuredGenerator { Indent indent, { required String dartPackageName, }) { - void writeEncodeLogic(EnumeratedType customType) { + void writeEncodeLogic( + EnumeratedType customType, int nonSerializedClassCount) { indent.writeScoped('else if (value is ${customType.name}) {', '}', () { - if (customType.enumeration < maximumCodecFieldKey) { - indent.writeln('buffer.putUint8(${customType.enumeration});'); + if (customType.offset(nonSerializedClassCount) < maximumCodecFieldKey) { + indent.writeln( + 'buffer.putUint8(${customType.offset(nonSerializedClassCount)});'); if (customType.type == CustomTypes.customClass) { indent.writeln('writeValue(buffer, value.encode());'); } else if (customType.type == CustomTypes.customEnum) { @@ -299,18 +310,20 @@ class DartGenerator extends StructuredGenerator { ? '.encode()' : '.index'; indent.writeln( - 'final $_overflowClassName wrap = $_overflowClassName(type: ${customType.enumeration - maximumCodecFieldKey}, wrapped: value$encodeString);'); + 'final $_overflowClassName wrap = $_overflowClassName(type: ${customType.offset(nonSerializedClassCount) - maximumCodecFieldKey}, wrapped: value$encodeString);'); indent.writeln('buffer.putUint8($maximumCodecFieldKey);'); indent.writeln('writeValue(buffer, wrap.encode());'); } }, addTrailingNewline: false); } - void writeDecodeLogic(EnumeratedType customType) { - indent.writeln('case ${customType.enumeration}: '); + void writeDecodeLogic( + EnumeratedType customType, int nonSerializedClassCount) { + indent.writeln('case ${customType.offset(nonSerializedClassCount)}: '); indent.nest(1, () { if (customType.type == CustomTypes.customClass) { - if (customType.enumeration == maximumCodecFieldKey) { + if (customType.offset(nonSerializedClassCount) == + maximumCodecFieldKey) { indent.writeln( 'final ${customType.name} wrapper = ${customType.name}.decode(readValue(buffer)!);'); indent.writeln('return wrapper.unwrap();'); @@ -331,14 +344,14 @@ class DartGenerator extends StructuredGenerator { indent.newln(); final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); if (root.requiresOverflowClass) { _writeCodecOverflowUtilities(indent, enumeratedTypes); } indent.newln(); - indent.write('class $_pigeonCodec extends StandardMessageCodec'); + indent.write('class $_pigeonMessageCodec extends StandardMessageCodec'); indent.addScoped(' {', '}', () { - indent.writeln('const $_pigeonCodec();'); + indent.writeln('const $_pigeonMessageCodec();'); indent.writeln('@override'); indent.write('void writeValue(WriteBuffer buffer, Object? value) '); indent.addScoped('{', '}', () { @@ -346,10 +359,14 @@ class DartGenerator extends StructuredGenerator { indent.writeln('buffer.putUint8(4);'); indent.writeln('buffer.putInt64(value);'); }, addTrailingNewline: false); - + int nonSerializedClassCount = 0; enumerate(enumeratedTypes, (int index, final EnumeratedType customType) { - writeEncodeLogic(customType); + if (customType.associatedClass?.isSealed ?? false) { + nonSerializedClassCount += 1; + return; + } + writeEncodeLogic(customType, nonSerializedClassCount); }); indent.addScoped(' else {', '}', () { indent.writeln('super.writeValue(buffer, value);'); @@ -361,13 +378,17 @@ class DartGenerator extends StructuredGenerator { indent.addScoped('{', '}', () { indent.write('switch (type) '); indent.addScoped('{', '}', () { + int nonSerializedClassCount = 0; for (final EnumeratedType customType in enumeratedTypes) { - if (customType.enumeration < maximumCodecFieldKey) { - writeDecodeLogic(customType); + if (customType.associatedClass?.isSealed ?? false) { + nonSerializedClassCount++; + } else if (customType.offset(nonSerializedClassCount) < + maximumCodecFieldKey) { + writeDecodeLogic(customType, nonSerializedClassCount); } } if (root.requiresOverflowClass) { - writeDecodeLogic(overflowClass); + writeDecodeLogic(overflowClass, 0); } indent.writeln('default:'); indent.nest(1, () { @@ -376,6 +397,11 @@ class DartGenerator extends StructuredGenerator { }); }); }); + if (root.containsEventChannel) { + indent.newln(); + indent.writeln( + 'const StandardMethodCodec $_pigeonMethodChannelCodec = StandardMethodCodec($_pigeonMessageCodec());'); + } } /// Writes the code for host [Api], [api]. @@ -408,7 +434,7 @@ class DartGenerator extends StructuredGenerator { 'static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance;'); } indent.writeln( - 'static const MessageCodec $_pigeonChannelCodec = $_pigeonCodec();'); + 'static const MessageCodec $_pigeonChannelCodec = $_pigeonMessageCodec();'); indent.newln(); for (final Method func in api.methods) { addDocumentationComments( @@ -489,7 +515,7 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; '''); indent.writeln( - 'static const MessageCodec $_pigeonChannelCodec = $_pigeonCodec();'); + 'static const MessageCodec $_pigeonChannelCodec = $_pigeonMessageCodec();'); indent.newln(); indent.writeln('final String $_suffixVarName;'); indent.newln(); @@ -512,6 +538,33 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; }); } + @override + void writeEventChannelApi( + DartOptions generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) { + indent.newln(); + addDocumentationComments( + indent, api.documentationComments, _docCommentSpec); + for (final Method func in api.methods) { + indent.format(''' + Stream<${func.returnType.baseName}> ${func.name}(${_getMethodParameterSignature(func.parameters, addTrailingComma: true)} {String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.\$instanceName'; + } + const EventChannel ${func.name}Channel = + EventChannel('${makeChannelName(api, func, dartPackageName)}', $_pigeonMethodChannelCodec); + return ${func.name}Channel.receiveBroadcastStream().map((dynamic event) { + return event as ${func.returnType.baseName}; + }); + } + '''); + } + } + @override void writeInstanceManager( DartOptions generatorOptions, @@ -582,7 +635,7 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; ..type = cb.refer('MessageCodec') ..static = true ..modifier = cb.FieldModifier.constant - ..assignment = const cb.Code('$_pigeonCodec()'); + ..assignment = const cb.Code('$_pigeonMessageCodec()'); }, ) ], @@ -851,7 +904,7 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; /// Generates Dart source code for test support libraries based on the given AST /// represented by [root], outputting the code to [sink]. [sourceOutPath] is the - /// path of the generated dart code to be tested. [testOutPath] is where the + /// path of the generated Dart code to be tested. [testOutPath] is where the /// test code will be generated. void generateTest( DartOptions generatorOptions, @@ -938,22 +991,12 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; Indent indent, { required String dartPackageName, }) { - final bool hasHostMethod = root.apis - .whereType() - .any((AstHostApi api) => api.methods.isNotEmpty) || - root.apis.whereType().any((AstProxyApi api) => - api.constructors.isNotEmpty || - api.attachedFields.isNotEmpty || - api.hostMethods.isNotEmpty); - final bool hasFlutterMethod = root.apis - .whereType() - .any((AstFlutterApi api) => api.methods.isNotEmpty) || - root.apis.any((Api api) => api is AstProxyApi); - - if (hasHostMethod) { + if (root.containsHostApi || root.containsProxyApi) { _writeCreateConnectionError(indent); } - if (hasFlutterMethod || generatorOptions.testOutPath != null) { + if (root.containsFlutterApi || + root.containsProxyApi || + generatorOptions.testOutPath != null) { _writeWrapResponse(generatorOptions, root, indent); } } @@ -1015,16 +1058,22 @@ if (wrapped == null) { } '''); indent.writeScoped('switch (type) {', '}', () { + int nonSerializedClassCount = 0; for (int i = totalCustomCodecKeysAllowed; i < types.length; i++) { - indent.writeScoped('case ${i - totalCustomCodecKeysAllowed}:', '', - () { - if (types[i].type == CustomTypes.customClass) { - indent.writeln('return ${types[i].name}.decode(wrapped!);'); - } else if (types[i].type == CustomTypes.customEnum) { - indent.writeln( - 'return ${types[i].name}.values[wrapped! as int];'); - } - }); + if (types[i].associatedClass?.isSealed ?? false) { + nonSerializedClassCount++; + } else { + indent.writeScoped( + 'case ${i - nonSerializedClassCount - totalCustomCodecKeysAllowed}:', + '', () { + if (types[i].type == CustomTypes.customClass) { + indent.writeln('return ${types[i].name}.decode(wrapped!);'); + } else if (types[i].type == CustomTypes.customEnum) { + indent.writeln( + 'return ${types[i].name}.values[wrapped! as int];'); + } + }); + } } }); indent.writeln('return null;'); @@ -2095,7 +2144,10 @@ String _getParameterName(int count, NamedType field) => /// Generates the parameters code for [func] /// Example: (func, _getParameterName) -> 'String? foo, int bar' -String _getMethodParameterSignature(Iterable parameters) { +String _getMethodParameterSignature( + Iterable parameters, { + bool addTrailingComma = false, +}) { String signature = ''; if (parameters.isEmpty) { return signature; @@ -2145,8 +2197,10 @@ String _getMethodParameterSignature(Iterable parameters) { return '$baseParams[$optionalParameterString$trailingComma]'; } if (namedParams.isNotEmpty) { - final String trailingComma = - requiredPositionalParams.length + namedParams.length > 2 ? ',' : ''; + final String trailingComma = addTrailingComma || + requiredPositionalParams.length + namedParams.length > 2 + ? ', ' + : ''; return '$baseParams{$namedParameterString$trailingComma}'; } return signature; diff --git a/packages/pigeon/lib/generator.dart b/packages/pigeon/lib/generator.dart index 952b80e89872..fb4e2408beb6 100644 --- a/packages/pigeon/lib/generator.dart +++ b/packages/pigeon/lib/generator.dart @@ -282,6 +282,14 @@ abstract class StructuredGenerator extends Generator { api, dartPackageName: dartPackageName, ); + case AstEventChannelApi(): + writeEventChannelApi( + generatorOptions, + root, + indent, + api, + dartPackageName: dartPackageName, + ); } } } @@ -345,4 +353,13 @@ abstract class StructuredGenerator extends Generator { AstProxyApi api, { required String dartPackageName, }) {} + + /// Writes a single event channel Api to [indent]. + void writeEventChannelApi( + T generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) {} } diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 8349285f1021..e5a943bdcb96 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -14,7 +14,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '22.6.1'; +const String pigeonVersion = '22.7.0'; /// Read all the content from [stdin] to a String. String readStdin() { @@ -432,6 +432,9 @@ class EnumeratedType { /// The associated Enum that is represented by the [EnumeratedType]. final Enum? associatedEnum; + + /// Returns the offset of the enumeration. + int offset(int offset) => enumeration - offset; } /// Supported basic datatypes. @@ -605,7 +608,10 @@ enum CustomTypes { /// Return the enumerated types that must exist in the codec /// where the enumeration should be the key used in the buffer. -Iterable getEnumeratedTypes(Root root) sync* { +Iterable getEnumeratedTypes( + Root root, { + bool excludeSealedClasses = false, +}) sync* { int index = 0; for (final Enum customEnum in root.enums) { @@ -619,13 +625,15 @@ Iterable getEnumeratedTypes(Root root) sync* { } for (final Class customClass in root.classes) { - yield EnumeratedType( - customClass.name, - index + minimumCodecFieldKey, - CustomTypes.customClass, - associatedClass: customClass, - ); - index += 1; + if (!excludeSealedClasses || !customClass.isSealed) { + yield EnumeratedType( + customClass.name, + index + minimumCodecFieldKey, + CustomTypes.customClass, + associatedClass: customClass, + ); + index += 1; + } } } @@ -656,7 +664,7 @@ class DocumentCommentSpecification { /// Formats documentation comments and adds them to current Indent. /// -/// The [comments] list is meant for comments written in the input dart file. +/// The [comments] list is meant for comments written in the input Dart file. /// The [generatorComments] list is meant for comments added by the generators. /// Include white space for all tokens when called, no assumptions are made. void addDocumentationComments( @@ -674,7 +682,7 @@ void addDocumentationComments( /// Formats documentation comments and adds them to current Indent. /// -/// The [comments] list is meant for comments written in the input dart file. +/// The [comments] list is meant for comments written in the input Dart file. /// The [generatorComments] list is meant for comments added by the generators. /// Include white space for all tokens when called, no assumptions are made. Iterable asDocumentationComments( @@ -801,6 +809,22 @@ String toUpperCamelCase(String text) { }).join(); } +/// Converts strings to Lower Camel Case. +String toLowerCamelCase(String text) { + final RegExp separatorPattern = RegExp(r'[ _-]'); + bool firstWord = true; + return text.split(separatorPattern).map((String word) { + if (word.isEmpty) { + return ''; + } + if (firstWord) { + firstWord = false; + return word.substring(0, 1).toLowerCase() + word.substring(1); + } + return word.substring(0, 1).toUpperCase() + word.substring(1); + }).join(); +} + /// Converts string to SCREAMING_SNAKE_CASE. String toScreamingSnakeCase(String string) { return string diff --git a/packages/pigeon/lib/gobject_generator.dart b/packages/pigeon/lib/gobject_generator.dart index 04f4bd1afa8d..486b6143986e 100644 --- a/packages/pigeon/lib/gobject_generator.dart +++ b/packages/pigeon/lib/gobject_generator.dart @@ -959,7 +959,8 @@ class GObjectSourceGenerator extends StructuredGenerator { final String codecClassName = _getClassName(module, _codecBaseName); final String codecMethodPrefix = _getMethodPrefix(module, _codecBaseName); - final Iterable customTypes = getEnumeratedTypes(root); + final Iterable customTypes = + getEnumeratedTypes(root, excludeSealedClasses: true); indent.newln(); _writeObjectStruct(indent, module, _codecBaseName, () {}, @@ -2012,7 +2013,7 @@ String _referenceValue(String module, TypeDeclaration type, String variableName, } int _getTypeEnumeration(Root root, TypeDeclaration type) { - return getEnumeratedTypes(root) + return getEnumeratedTypes(root, excludeSealedClasses: true) .firstWhere((EnumeratedType t) => (type.isClass && t.associatedClass == type.associatedClass) || (type.isEnum && t.associatedEnum == type.associatedEnum)) diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart index 27a1a5915e78..938b20c2a057 100644 --- a/packages/pigeon/lib/java_generator.dart +++ b/packages/pigeon/lib/java_generator.dart @@ -458,7 +458,7 @@ class JavaGenerator extends StructuredGenerator { required String dartPackageName, }) { final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); void writeEncodeLogic(EnumeratedType customType) { final String encodeString = @@ -1117,20 +1117,13 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - indent.newln(); _writeErrorClass(indent); - if (hasHostApi) { + if (root.containsHostApi) { indent.newln(); _writeWrapError(indent); } - if (hasFlutterApi) { + if (root.containsFlutterApi) { indent.newln(); _writeCreateConnectionError(indent); } diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart index 3b5ff62aa822..f0a185cdcc72 100644 --- a/packages/pigeon/lib/kotlin_generator.dart +++ b/packages/pigeon/lib/kotlin_generator.dart @@ -30,6 +30,9 @@ const DocumentCommentSpecification _docCommentSpec = String _codecName = 'PigeonCodec'; +/// Name of field used for host API codec. +const String _pigeonMethodChannelCodec = 'PigeonMethodCodec'; + const String _overflowClassName = '${classNamePrefix}CodecOverflow'; /// Options that control how Kotlin code will be generated. @@ -147,7 +150,9 @@ class KotlinGenerator extends StructuredGenerator { indent.writeln('import android.util.Log'); indent.writeln('import io.flutter.plugin.common.BasicMessageChannel'); indent.writeln('import io.flutter.plugin.common.BinaryMessenger'); + indent.writeln('import io.flutter.plugin.common.EventChannel'); indent.writeln('import io.flutter.plugin.common.MessageCodec'); + indent.writeln('import io.flutter.plugin.common.StandardMethodCodec'); indent.writeln('import io.flutter.plugin.common.StandardMessageCodec'); indent.writeln('import java.io.ByteArrayOutputStream'); indent.writeln('import java.nio.ByteBuffer'); @@ -197,14 +202,21 @@ class KotlinGenerator extends StructuredGenerator { Class classDefinition, { required String dartPackageName, }) { - const List generatedMessages = [ + final List generatedMessages = [ ' Generated class from Pigeon that represents data sent in messages.' ]; + if (classDefinition.isSealed) { + generatedMessages.add( + ' This class should not be extended by any user class outside of the generated file.'); + } indent.newln(); addDocumentationComments( indent, classDefinition.documentationComments, _docCommentSpec, generatorComments: generatedMessages); _writeDataClassSignature(indent, classDefinition); + if (classDefinition.isSealed) { + return; + } indent.addScoped(' {', '}', () { writeClassDecode( generatorOptions, @@ -228,9 +240,16 @@ class KotlinGenerator extends StructuredGenerator { Class classDefinition, { bool private = false, }) { - indent.write( - '${private ? 'private ' : ''}data class ${classDefinition.name} '); - indent.addScoped('(', ')', () { + final String privateString = private ? 'private ' : ''; + final String classType = classDefinition.isSealed ? 'sealed' : 'data'; + final String inheritance = classDefinition.superClass != null + ? ' : ${classDefinition.superClassName}()' + : ''; + indent.write('$privateString$classType class ${classDefinition.name} '); + if (classDefinition.isSealed) { + return; + } + indent.addScoped('(', ')$inheritance', () { for (final NamedType element in getFieldsInSerializationOrder(classDefinition)) { _writeClassField(indent, element); @@ -334,7 +353,7 @@ class KotlinGenerator extends StructuredGenerator { required String dartPackageName, }) { final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); void writeEncodeLogic(EnumeratedType customType) { final String encodeString = @@ -427,6 +446,11 @@ class KotlinGenerator extends StructuredGenerator { }); }); indent.newln(); + if (root.containsEventChannel) { + indent.writeln( + 'val ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec = StandardMethodCodec(${generatorOptions.fileSpecificClassNameComponent}$_codecName());'); + indent.newln(); + } } void _writeCodecOverflowUtilities( @@ -984,6 +1008,72 @@ if (wrapped == null) { ); } + @override + void writeEventChannelApi( + KotlinOptions generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) { + indent.newln(); + indent.format(''' + private class PigeonStreamHandler( + val wrapper: PigeonEventChannelWrapper + ) : EventChannel.StreamHandler { + var pigeonSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + pigeonSink = PigeonEventSink(sink) + wrapper.onListen(p0, pigeonSink!!) + } + + override fun onCancel(p0: Any?) { + pigeonSink = null + wrapper.onCancel(p0) + } + } + + interface PigeonEventChannelWrapper { + open fun onListen(p0: Any?, sink: PigeonEventSink) {} + + open fun onCancel(p0: Any?) {} + } + + class PigeonEventSink(private val sink: EventChannel.EventSink) { + fun success(value: T) { + sink.success(value) + } + + fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + sink.error(errorCode, errorMessage, errorDetails) + } + + fun endOfStream() { + sink.endOfStream() + } + } + '''); + addDocumentationComments( + indent, api.documentationComments, _docCommentSpec); + for (final Method func in api.methods) { + indent.format(''' + abstract class ${toUpperCamelCase(func.name)}StreamHandler : PigeonEventChannelWrapper<${_kotlinTypeForDartType(func.returnType)}> { + companion object { + fun register(messenger: BinaryMessenger, streamHandler: ${toUpperCamelCase(func.name)}StreamHandler, instanceName: String = "") { + var channelName: String = "${makeChannelName(api, func, dartPackageName)}" + if (instanceName.isNotEmpty()) { + channelName += ".\$instanceName" + } + val internalStreamHandler = PigeonStreamHandler<${_kotlinTypeForDartType(func.returnType)}>(streamHandler) + EventChannel(messenger, channelName, ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec).setStreamHandler(internalStreamHandler) + } + } + } + '''); + } + } + void _writeWrapResult(Indent indent) { indent.newln(); indent.write('private fun wrapResult(result: Any?): List '); @@ -1054,19 +1144,11 @@ if (wrapped == null) { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasProxyApi = root.apis.any((Api api) => api is AstProxyApi); - - if (hasHostApi || hasProxyApi) { + if (root.containsHostApi || root.containsProxyApi) { _writeWrapResult(indent); _writeWrapError(generatorOptions, indent); } - if (hasFlutterApi || hasProxyApi) { + if (root.containsFlutterApi || root.containsProxyApi) { _writeCreateConnectionError(generatorOptions, indent); } if (generatorOptions.includeErrorClass) { diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart index d0e0e99ee293..ed6bd37ac760 100644 --- a/packages/pigeon/lib/objc_generator.dart +++ b/packages/pigeon/lib/objc_generator.dart @@ -695,7 +695,7 @@ if (self.wrapped == nil) { }) { const String codecName = 'PigeonCodec'; final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); final String readerWriterName = '${generatorOptions.prefix}${toUpperCamelCase(generatorOptions.fileSpecificClassNameComponent ?? '')}${codecName}ReaderWriter'; final String readerName = @@ -901,23 +901,16 @@ if (self.wrapped == nil) { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - - if (hasHostApi) { + if (root.containsHostApi) { _writeWrapError(indent); indent.newln(); } - if (hasFlutterApi) { + if (root.containsFlutterApi) { _writeCreateConnectionError(indent); indent.newln(); } - if (hasHostApi || hasFlutterApi) { + if (root.containsHostApi || root.containsFlutterApi) { _writeGetNullableObjectAtIndex(indent); } diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 075049d98298..4a4501e32508 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -160,6 +160,18 @@ class ProxyApi { final KotlinProxyApiOptions? kotlinOptions; } +/// Metadata to annotate a pigeon API that contains event channels. +/// +/// This class is a tool to designate a set of event channel methods, +/// the class itself will not be generated. +/// +/// All methods contained within the class will return a `Stream` of the +/// defined return type of the method definition. +class EventChannelApi { + /// Constructor. + const EventChannelApi(); +} + /// Metadata to annotation methods to control the selector used for objc output. /// The number of components in the provided selector must match the number of /// arguments in the annotated method. @@ -278,10 +290,10 @@ class PigeonOptions { /// Path to the file which will be processed. final String? input; - /// Path to the dart file that will be generated. + /// Path to the Dart file that will be generated. final String? dartOut; - /// Path to the dart file that will be generated for test support classes. + /// Path to the Dart file that will be generated for test support classes. final String? dartTestOut; /// Path to the ".h" Objective-C file will be generated. @@ -537,6 +549,29 @@ DartOptions _dartOptionsWithCopyrightHeader( )); } +void _errorOnEventChannelApi(List errors, String generator, Root root) { + if (root.containsEventChannel) { + errors.add(Error(message: '$generator does not support event channels')); + } +} + +void _errorOnSealedClass(List errors, String generator, Root root) { + if (root.classes.any( + (Class element) => element.isSealed, + )) { + errors.add(Error(message: '$generator does not support sealed classes')); + } +} + +void _errorOnInheritedClass(List errors, String generator, Root root) { + if (root.classes.any( + (Class element) => element.superClass != null, + )) { + errors.add( + Error(message: '$generator does not support inheritance in classes')); + } +} + /// A [GeneratorAdapter] that generates the AST. class AstGeneratorAdapter implements GeneratorAdapter { /// Constructor for [AstGeneratorAdapter]. @@ -564,6 +599,9 @@ class DartGeneratorAdapter implements GeneratorAdapter { /// Constructor for [DartGeneratorAdapter]. DartGeneratorAdapter(); + /// A string representing the name of the language being generated. + String languageString = 'Dart'; + @override List fileTypeList = const [FileType.na]; @@ -644,6 +682,9 @@ class ObjcGeneratorAdapter implements GeneratorAdapter { ObjcGeneratorAdapter( {this.fileTypeList = const [FileType.header, FileType.source]}); + /// A string representing the name of the language being generated. + String languageString = 'Objective-C'; + @override List fileTypeList; @@ -685,7 +726,13 @@ class ObjcGeneratorAdapter implements GeneratorAdapter { } @override - List validate(PigeonOptions options, Root root) => []; + List validate(PigeonOptions options, Root root) { + final List errors = []; + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; + } } /// A [GeneratorAdapter] that generates Java source code. @@ -693,6 +740,9 @@ class JavaGeneratorAdapter implements GeneratorAdapter { /// Constructor for [JavaGeneratorAdapter]. JavaGeneratorAdapter(); + /// A string representing the name of the language being generated. + String languageString = 'Java'; + @override List fileTypeList = const [FileType.na]; @@ -722,7 +772,13 @@ class JavaGeneratorAdapter implements GeneratorAdapter { _openSink(options.javaOut, basePath: options.basePath ?? ''); @override - List validate(PigeonOptions options, Root root) => []; + List validate(PigeonOptions options, Root root) { + final List errors = []; + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; + } } /// A [GeneratorAdapter] that generates Swift source code. @@ -730,6 +786,9 @@ class SwiftGeneratorAdapter implements GeneratorAdapter { /// Constructor for [SwiftGeneratorAdapter]. SwiftGeneratorAdapter(); + /// A string representing the name of the language being generated. + String languageString = 'Swift'; + @override List fileTypeList = const [FileType.na]; @@ -770,6 +829,9 @@ class CppGeneratorAdapter implements GeneratorAdapter { CppGeneratorAdapter( {this.fileTypeList = const [FileType.header, FileType.source]}); + /// A string representing the name of the language being generated. + String languageString = 'C++'; + @override List fileTypeList; @@ -805,7 +867,13 @@ class CppGeneratorAdapter implements GeneratorAdapter { } @override - List validate(PigeonOptions options, Root root) => []; + List validate(PigeonOptions options, Root root) { + final List errors = []; + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; + } } /// A [GeneratorAdapter] that generates GObject source code. @@ -814,6 +882,9 @@ class GObjectGeneratorAdapter implements GeneratorAdapter { GObjectGeneratorAdapter( {this.fileTypeList = const [FileType.header, FileType.source]}); + /// A string representing the name of the language being generated. + String languageString = 'GObject'; + @override List fileTypeList; @@ -862,6 +933,10 @@ class GObjectGeneratorAdapter implements GeneratorAdapter { message: 'GObject generator does not yet support more than $totalCustomCodecKeysAllowed custom types.')); } + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; } } @@ -986,6 +1061,24 @@ List _validateAst(Root root, String source) { lineNumber: _calculateLineNumberNullable(source, field.offset), )); } + if (classDefinition.isSealed) { + if (classDefinition.fields.isNotEmpty) { + result.add(Error( + message: + 'Sealed class: "${classDefinition.name}" must not contain fields.', + lineNumber: _calculateLineNumberNullable(source, field.offset), + )); + } + } + if (classDefinition.superClass != null) { + if (!classDefinition.superClass!.isSealed) { + result.add(Error( + message: + 'Child class: "${classDefinition.name}" must extend a sealed class.', + lineNumber: _calculateLineNumberNullable(source, field.offset), + )); + } + } } } @@ -1020,6 +1113,13 @@ List _validateAst(Root root, String source) { lineNumber: _calculateLineNumberNullable(source, method.offset), )); } + if (api is AstEventChannelApi && method.parameters.isNotEmpty) { + result.add(Error( + message: + 'event channel methods must not be contain parameters, in method "${method.name}" in API: "${api.name}"', + lineNumber: _calculateLineNumberNullable(source, method.offset), + )); + } for (final Parameter param in method.parameters) { if (param.type.baseName.isEmpty) { result.add(Error( @@ -1387,20 +1487,43 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { getReferencedTypes(_apis, _classes); final Set referencedTypeNames = referencedTypes.keys.map((TypeDeclaration e) => e.baseName).toSet(); - final List nonReferencedClasses = List.from(_classes); - nonReferencedClasses + final List nonReferencedTypes = List.from(_classes); + nonReferencedTypes .removeWhere((Class x) => referencedTypeNames.contains(x.name)); - for (final Class x in nonReferencedClasses) { + for (final Class x in nonReferencedTypes) { x.isReferenced = false; } final List referencedEnums = List.from(_enums); - final Root completeRoot = - Root(apis: _apis, classes: _classes, enums: referencedEnums); + bool containsHostApi = false; + bool containsFlutterApi = false; + bool containsProxyApi = false; + bool containsEventChannel = false; + + for (final Api api in _apis) { + switch (api) { + case AstHostApi(): + containsHostApi = true; + case AstFlutterApi(): + containsFlutterApi = true; + case AstProxyApi(): + containsProxyApi = true; + case AstEventChannelApi(): + containsEventChannel = true; + } + } + + final Root completeRoot = Root( + apis: _apis, + classes: _classes, + enums: referencedEnums, + containsHostApi: containsHostApi, + containsFlutterApi: containsFlutterApi, + containsProxyApi: containsProxyApi, + containsEventChannel: containsEventChannel, + ); - final List validateErrors = _validateAst(completeRoot, source); final List totalErrors = List.from(_errors); - totalErrors.addAll(validateErrors); for (final MapEntry> element in referencedTypes.entries) { @@ -1429,6 +1552,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { classDefinition.fields = _attachAssociatedDefinitions( classDefinition.fields, ); + classDefinition.superClass = _attachSuperClass(classDefinition); } for (final Api api in _apis) { @@ -1456,6 +1580,8 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { api.interfaces = newInterfaceSet; } } + final List validateErrors = _validateAst(completeRoot, source); + totalErrors.addAll(validateErrors); return ParseResults( root: totalErrors.isEmpty @@ -1503,6 +1629,20 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { return result; } + Class? _attachSuperClass(Class childClass) { + if (childClass.superClassName == null) { + return null; + } + + for (final Class parentClass in _classes) { + if (parentClass.name == childClass.superClassName) { + parentClass.children.add(childClass); + return parentClass; + } + } + return null; + } + Object _expressionToMap(dart_ast.Expression expression) { if (expression is dart_ast.MethodInvocation) { final Map result = {}; @@ -1599,6 +1739,14 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { _storeCurrentClass(); if (node.abstractKeyword != null) { + if (node.metadata.length > 2 || + (node.metadata.length > 1 && + !_hasMetadata(node.metadata, 'ConfigurePigeon'))) { + _errors.add(Error( + message: + 'API "${node.name.lexeme}" can only have one API annotation but contains: ${node.metadata}', + lineNumber: _calculateLineNumber(source, node.offset))); + } if (_hasMetadata(node.metadata, 'HostApi')) { final dart_ast.Annotation hostApi = node.metadata.firstWhere( (dart_ast.Annotation element) => element.name.name == 'HostApi'); @@ -1736,11 +1884,22 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { documentationComments: _documentationCommentsParser(node.documentationComment?.tokens), ); + } else if (_hasMetadata(node.metadata, 'EventChannelApi')) { + _currentApi = AstEventChannelApi( + name: node.name.lexeme, + methods: [], + documentationComments: + _documentationCommentsParser(node.documentationComment?.tokens), + ); } } else { _currentClass = Class( name: node.name.lexeme, fields: [], + superClassName: + node.implementsClause?.interfaces.first.name2.toString() ?? + node.extendsClause?.superclass.name2.toString(), + isSealed: node.sealedKeyword != null, isSwiftClass: _hasMetadata(node.metadata, 'SwiftClass'), documentationComments: _documentationCommentsParser(node.documentationComment?.tokens), @@ -1889,6 +2048,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { AstHostApi() => ApiLocation.host, AstProxyApi() => ApiLocation.host, AstFlutterApi() => ApiLocation.flutter, + AstEventChannelApi() => ApiLocation.host, }, isAsynchronous: isAsynchronous, objcSelector: objcSelector, diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 289c50dbd887..4962774c9a04 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:collection/collection.dart' as collection; import 'package:graphs/graphs.dart'; import 'package:pub_semver/pub_semver.dart'; @@ -158,7 +157,7 @@ class SwiftGenerator extends StructuredGenerator { final Iterable proxyApiImports = root.apis .whereType() .map((AstProxyApi proxyApi) => proxyApi.swiftOptions?.import) - .whereNotNull() + .nonNulls .toSet(); for (final String import in proxyApiImports) { indent.writeln('import $import'); @@ -204,13 +203,13 @@ class SwiftGenerator extends StructuredGenerator { Indent indent, { required String dartPackageName, }) { - final String codecName = _getCodecName(generatorOptions); + final String codecName = _getMessageCodecName(generatorOptions); final String readerWriterName = '${codecName}ReaderWriter'; final String readerName = '${codecName}Reader'; final String writerName = '${codecName}Writer'; final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); void writeDecodeLogic(EnumeratedType customType) { indent.writeln('case ${customType.enumeration}:'); @@ -332,6 +331,11 @@ class SwiftGenerator extends StructuredGenerator { 'static let shared = $codecName(readerWriter: $readerWriterName())'); }); indent.newln(); + if (root.containsEventChannel) { + indent.writeln( + 'var ${_getMethodCodecVarName(generatorOptions)} = FlutterStandardMethodCodec(readerWriter: $readerWriterName());'); + indent.newln(); + } } void _writeDataClassSignature( @@ -340,10 +344,17 @@ class SwiftGenerator extends StructuredGenerator { bool private = false, }) { final String privateString = private ? 'private ' : ''; + final String extendsString = classDefinition.superClass != null + ? ': ${classDefinition.superClass!.name}' + : ''; if (classDefinition.isSwiftClass) { - indent.write('${privateString}class ${classDefinition.name} '); + indent.write( + '${privateString}class ${classDefinition.name}$extendsString '); + } else if (classDefinition.isSealed) { + indent.write('protocol ${classDefinition.name} '); } else { - indent.write('${privateString}struct ${classDefinition.name} '); + indent.write( + '${privateString}struct ${classDefinition.name}$extendsString '); } indent.addScoped('{', '', () { @@ -361,7 +372,7 @@ class SwiftGenerator extends StructuredGenerator { _writeClassField(indent, field, addNil: !classDefinition.isSwiftClass); indent.newln(); } - }); + }, addTrailingNewline: false); } void _writeCodecOverflowUtilities( @@ -404,7 +415,7 @@ static func fromList(_ ${varNamePrefix}list: [Any?]) -> Any? { type: type, wrapped: wrapped ) - + return wrapper.unwrap() } '''); @@ -444,15 +455,22 @@ if (wrapped == nil) { Class classDefinition, { required String dartPackageName, }) { - const List generatedComments = [ + final List generatedComments = [ ' Generated class from Pigeon that represents data sent in messages.' ]; + if (classDefinition.isSealed) { + generatedComments.add( + ' This protocol should not be extended by any user class outside of the generated file.'); + } indent.newln(); addDocumentationComments( indent, classDefinition.documentationComments, _docCommentSpec, generatorComments: generatedComments); _writeDataClassSignature(indent, classDefinition); indent.writeScoped('', '}', () { + if (classDefinition.isSealed) { + return; + } indent.newln(); writeClassDecode( generatorOptions, @@ -639,7 +657,7 @@ if (wrapped == nil) { indent.writeln( r'self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""'); }); - final String codecName = _getCodecName(generatorOptions); + final String codecName = _getMessageCodecName(generatorOptions); indent.write('var codec: $codecName '); indent.addScoped('{', '}', () { indent.writeln('return $codecName.shared'); @@ -705,7 +723,7 @@ if (wrapped == nil) { indent.write('class ${apiName}Setup '); indent.addScoped('{', '}', () { indent.writeln( - 'static var codec: FlutterStandardMessageCodec { ${_getCodecName(generatorOptions)}.shared }'); + 'static var codec: FlutterStandardMessageCodec { ${_getMessageCodecName(generatorOptions)}.shared }'); indent.writeln( '$_docCommentPrefix Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.'); indent.write( @@ -764,7 +782,7 @@ if (wrapped == nil) { _docCommentSpec, ); indent.writeln( - 'var codec: FlutterStandardMessageCodec { ${_getCodecName(generatorOptions)}.shared }', + 'var codec: FlutterStandardMessageCodec { ${_getMessageCodecName(generatorOptions)}.shared }', ); indent.newln(); @@ -797,7 +815,7 @@ if (wrapped == nil) { '}', () { indent.writeln( - 'let codec = ${_getCodecName(generatorOptions)}.shared', + 'let codec = ${_getMessageCodecName(generatorOptions)}.shared', ); const String setHandlerCondition = 'let instanceManager = instanceManager'; @@ -899,7 +917,7 @@ if (wrapped == nil) { indent.newln(); indent.writeScoped( - 'private class $filePrefix${classNamePrefix}ProxyApiCodecReader: ${_getCodecName(generatorOptions)}Reader {', + 'private class $filePrefix${classNamePrefix}ProxyApiCodecReader: ${_getMessageCodecName(generatorOptions)}Reader {', '}', () { indent.writeln('unowned let pigeonRegistrar: $registrarName'); @@ -938,7 +956,7 @@ if (wrapped == nil) { indent.newln(); indent.writeScoped( - 'private class $filePrefix${classNamePrefix}ProxyApiCodecWriter: ${_getCodecName(generatorOptions)}Writer {', + 'private class $filePrefix${classNamePrefix}ProxyApiCodecWriter: ${_getMessageCodecName(generatorOptions)}Writer {', '}', () { indent.writeln( @@ -1304,23 +1322,15 @@ private func nilOrValue(_ value: Any?) -> T? { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasProxyApi = root.apis.any((Api api) => api is AstProxyApi); - if (generatorOptions.includeErrorClass) { _writePigeonError(generatorOptions, indent); } - if (hasHostApi || hasProxyApi) { + if (root.containsHostApi || root.containsProxyApi) { _writeWrapResult(indent); _writeWrapError(generatorOptions, indent); } - if (hasFlutterApi || hasProxyApi) { + if (root.containsFlutterApi || root.containsProxyApi) { _writeCreateConnectionError(generatorOptions, indent); } @@ -1328,6 +1338,86 @@ private func nilOrValue(_ value: Any?) -> T? { _writeNilOrValue(indent); } + @override + void writeEventChannelApi( + SwiftOptions generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) { + indent.newln(); + indent.format(''' + private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } + } + + class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} + } + + class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + + } + '''); + addDocumentationComments( + indent, api.documentationComments, _docCommentSpec); + for (final Method func in api.methods) { + indent.format(''' + class ${toUpperCamelCase(func.name)}StreamHandler: PigeonEventChannelWrapper<${_swiftTypeForDartType(func.returnType)}> { + static func register(with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: ${toUpperCamelCase(func.name)}StreamHandler) { + var channelName = "${makeChannelName(api, func, dartPackageName)}" + if !instanceName.isEmpty { + channelName += ".\\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(wrapper: streamHandler) + let channel = FlutterEventChannel(name: channelName, binaryMessenger: messenger, codec: ${_getMethodCodecVarName(generatorOptions)}) + channel.setStreamHandler(internalStreamHandler) + } + } + '''); + } + } + void _writeFlutterMethod( Indent indent, { required SwiftOptions generatorOptions, @@ -2013,7 +2103,9 @@ private func nilOrValue(_ value: Any?) -> T? { : 'pigeonDefaultConstructor'; final String channelName = makeChannelNameWithStrings( apiName: api.name, - methodName: '${classMemberNamePrefix}defaultConstructor', + methodName: constructor.name.isNotEmpty + ? constructor.name + : '${classMemberNamePrefix}defaultConstructor', dartPackageName: dartPackageName, ); writeWithApiCheckIfNecessary( @@ -2491,8 +2583,14 @@ String? _tryGetUnsupportedPlatformsCondition(Iterable types) { } /// Calculates the name of the codec that will be generated for [api]. -String _getCodecName(SwiftOptions options) { - return '${options.fileSpecificClassNameComponent}PigeonCodec'; +String _getMessageCodecName(SwiftOptions options) { + return toUpperCamelCase( + '${options.fileSpecificClassNameComponent}PigeonCodec'); +} + +/// Calculates the name of the codec that will be generated for [api]. +String _getMethodCodecVarName(SwiftOptions options) { + return '${toLowerCamelCase(options.fileSpecificClassNameComponent ?? '')}PigeonMethodCodec'; } String _getErrorClassName(SwiftOptions generatorOptions) { diff --git a/packages/pigeon/pigeons/event_channel_tests.dart b/packages/pigeon/pigeons/event_channel_tests.dart new file mode 100644 index 000000000000..d925a320dfe3 --- /dev/null +++ b/packages/pigeon/pigeons/event_channel_tests.dart @@ -0,0 +1,144 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +enum EventEnum { + one, + two, + three, + fortyTwo, + fourHundredTwentyTwo, +} + +// Enums require special logic, having multiple ensures that the logic can be +// replicated without collision. +enum AnotherEventEnum { + justInCase, +} + +/// A class containing all supported nullable types. +@SwiftClass() +class EventAllNullableTypes { + EventAllNullableTypes( + this.aNullableBool, + this.aNullableInt, + this.aNullableInt64, + this.aNullableDouble, + this.aNullableByteArray, + this.aNullable4ByteArray, + this.aNullable8ByteArray, + this.aNullableFloatArray, + this.aNullableEnum, + this.anotherNullableEnum, + this.aNullableString, + this.aNullableObject, + this.allNullableTypes, + + // Lists + // This name is in a different format than the others to ensure that name + // collision with the word 'list' doesn't occur in the generated files. + this.list, + this.stringList, + this.intList, + this.doubleList, + this.boolList, + this.enumList, + this.objectList, + this.listList, + this.mapList, + this.recursiveClassList, + + // Maps + this.map, + this.stringMap, + this.intMap, + this.enumMap, + this.objectMap, + this.listMap, + this.mapMap, + this.recursiveClassMap, + ); + + bool? aNullableBool; + int? aNullableInt; + int? aNullableInt64; + double? aNullableDouble; + Uint8List? aNullableByteArray; + Int32List? aNullable4ByteArray; + Int64List? aNullable8ByteArray; + Float64List? aNullableFloatArray; + EventEnum? aNullableEnum; + AnotherEventEnum? anotherNullableEnum; + String? aNullableString; + Object? aNullableObject; + EventAllNullableTypes? allNullableTypes; + + // Lists + // ignore: strict_raw_type, always_specify_types + List? list; + List? stringList; + List? intList; + List? doubleList; + List? boolList; + List? enumList; + List? objectList; + List?>? listList; + List?>? mapList; + List? recursiveClassList; + + // Maps + // ignore: strict_raw_type, always_specify_types + Map? map; + Map? stringMap; + Map? intMap; + Map? enumMap; + Map? objectMap; + Map?>? listMap; + Map?>? mapMap; + Map? recursiveClassMap; +} + +sealed class PlatformEvent {} + +class IntEvent extends PlatformEvent { + IntEvent(this.value); + final int value; +} + +class StringEvent extends PlatformEvent { + StringEvent(this.value); + final String value; +} + +class BoolEvent extends PlatformEvent { + BoolEvent(this.value); + final bool value; +} + +class DoubleEvent extends PlatformEvent { + DoubleEvent(this.value); + final double value; +} + +class ObjectsEvent extends PlatformEvent { + ObjectsEvent(this.value); + final Object value; +} + +class EnumEvent extends PlatformEvent { + EnumEvent(this.value); + final EventEnum value; +} + +class ClassEvent extends PlatformEvent { + ClassEvent(this.value); + final EventAllNullableTypes value; +} + +@EventChannelApi() +abstract class EventChannelMethods { + int streamInts(); + PlatformEvent streamEvents(); +} diff --git a/packages/pigeon/pigeons/proxy_api_tests.dart b/packages/pigeon/pigeons/proxy_api_tests.dart index 0e7bfed75ff6..b9e001071bd0 100644 --- a/packages/pigeon/pigeons/proxy_api_tests.dart +++ b/packages/pigeon/pigeons/proxy_api_tests.dart @@ -54,6 +54,8 @@ abstract class ProxyApiTestClass extends ProxyApiSuperClass ProxyApiSuperClass? nullableProxyApiParam, ); + ProxyApiTestClass.namedConstructor(); + late bool aBool; late int anInt; late double aDouble; diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart index b7861ee4d574..76d63eb0fd86 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart @@ -3,5 +3,6 @@ // found in the LICENSE file. export 'src/generated/core_tests.gen.dart'; +export 'src/generated/event_channel_tests.gen.dart'; export 'src/generated/proxy_api_tests.gen.dart' show ProxyApiSuperClass, ProxyApiTestClass, ProxyApiTestEnum; diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart index 21d76637675c..01115629506d 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart @@ -4,6 +4,8 @@ // ignore_for_file: unused_local_variable +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -2170,6 +2172,22 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { return; } + testWidgets('named constructor', (_) async { + final ProxyApiTestClass instance = ProxyApiTestClass.namedConstructor( + aBool: true, + anInt: 0, + aDouble: 0.0, + aString: '', + aUint8List: Uint8List(0), + aList: const [], + aMap: const {}, + anEnum: ProxyApiTestEnum.one, + aProxyApi: ProxyApiSuperClass(), + ); + // Ensure no error calling method on instance. + await instance.noop(); + }); + testWidgets('noop', (_) async { final ProxyApiTestClass api = _createGenericProxyApiTestClass(); @@ -2840,6 +2858,60 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { final UnusedClass unused = UnusedClass(); expect(unused, unused); }); + + /// Event channels + + const List eventChannelSupported = [ + TargetGenerator.kotlin, + TargetGenerator.swift + ]; + testWidgets('event channel sends continuous ints', (_) async { + final Stream events = streamInts(); + final List listEvents = await events.toList(); + for (final int value in listEvents) { + expect(listEvents[value], value); + } + }, skip: !eventChannelSupported.contains(targetGenerator)); + + testWidgets('event channel handles extended sealed classes', (_) async { + final Completer completer = Completer(); + int count = 0; + final Stream events = streamEvents(); + events.listen((PlatformEvent event) { + switch (event) { + case IntEvent(): + expect(event.value, 1); + expect(count, 0); + count++; + case StringEvent(): + expect(event.value, 'string'); + expect(count, 1); + count++; + case BoolEvent(): + expect(event.value, false); + expect(count, 2); + count++; + case DoubleEvent(): + expect(event.value, 3.14); + expect(count, 3); + count++; + case ObjectsEvent(): + expect(event.value, true); + expect(count, 4); + count++; + case EnumEvent(): + expect(event.value, EventEnum.fortyTwo); + expect(count, 5); + count++; + case ClassEvent(): + expect(event.value.aNullableInt, 0); + expect(count, 6); + count++; + completer.complete(); + } + }); + await completer.future; + }, skip: !eventChannelSupported.contains(targetGenerator)); } class _FlutterApiTestImplementation implements FlutterIntegrationCoreApi { diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart new file mode 100644 index 000000000000..fe57f06e99f8 --- /dev/null +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart @@ -0,0 +1,453 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +enum EventEnum { + one, + two, + three, + fortyTwo, + fourHundredTwentyTwo, +} + +enum AnotherEventEnum { + justInCase, +} + +/// A class containing all supported nullable types. +class EventAllNullableTypes { + EventAllNullableTypes({ + this.aNullableBool, + this.aNullableInt, + this.aNullableInt64, + this.aNullableDouble, + this.aNullableByteArray, + this.aNullable4ByteArray, + this.aNullable8ByteArray, + this.aNullableFloatArray, + this.aNullableEnum, + this.anotherNullableEnum, + this.aNullableString, + this.aNullableObject, + this.allNullableTypes, + this.list, + this.stringList, + this.intList, + this.doubleList, + this.boolList, + this.enumList, + this.objectList, + this.listList, + this.mapList, + this.recursiveClassList, + this.map, + this.stringMap, + this.intMap, + this.enumMap, + this.objectMap, + this.listMap, + this.mapMap, + this.recursiveClassMap, + }); + + bool? aNullableBool; + + int? aNullableInt; + + int? aNullableInt64; + + double? aNullableDouble; + + Uint8List? aNullableByteArray; + + Int32List? aNullable4ByteArray; + + Int64List? aNullable8ByteArray; + + Float64List? aNullableFloatArray; + + EventEnum? aNullableEnum; + + AnotherEventEnum? anotherNullableEnum; + + String? aNullableString; + + Object? aNullableObject; + + EventAllNullableTypes? allNullableTypes; + + List? list; + + List? stringList; + + List? intList; + + List? doubleList; + + List? boolList; + + List? enumList; + + List? objectList; + + List?>? listList; + + List?>? mapList; + + List? recursiveClassList; + + Map? map; + + Map? stringMap; + + Map? intMap; + + Map? enumMap; + + Map? objectMap; + + Map?>? listMap; + + Map?>? mapMap; + + Map? recursiveClassMap; + + Object encode() { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ]; + } + + static EventAllNullableTypes decode(Object result) { + result as List; + return EventAllNullableTypes( + aNullableBool: result[0] as bool?, + aNullableInt: result[1] as int?, + aNullableInt64: result[2] as int?, + aNullableDouble: result[3] as double?, + aNullableByteArray: result[4] as Uint8List?, + aNullable4ByteArray: result[5] as Int32List?, + aNullable8ByteArray: result[6] as Int64List?, + aNullableFloatArray: result[7] as Float64List?, + aNullableEnum: result[8] as EventEnum?, + anotherNullableEnum: result[9] as AnotherEventEnum?, + aNullableString: result[10] as String?, + aNullableObject: result[11], + allNullableTypes: result[12] as EventAllNullableTypes?, + list: result[13] as List?, + stringList: (result[14] as List?)?.cast(), + intList: (result[15] as List?)?.cast(), + doubleList: (result[16] as List?)?.cast(), + boolList: (result[17] as List?)?.cast(), + enumList: (result[18] as List?)?.cast(), + objectList: (result[19] as List?)?.cast(), + listList: (result[20] as List?)?.cast?>(), + mapList: (result[21] as List?)?.cast?>(), + recursiveClassList: + (result[22] as List?)?.cast(), + map: result[23] as Map?, + stringMap: + (result[24] as Map?)?.cast(), + intMap: (result[25] as Map?)?.cast(), + enumMap: (result[26] as Map?) + ?.cast(), + objectMap: + (result[27] as Map?)?.cast(), + listMap: + (result[28] as Map?)?.cast?>(), + mapMap: (result[29] as Map?) + ?.cast?>(), + recursiveClassMap: (result[30] as Map?) + ?.cast(), + ); + } +} + +sealed class PlatformEvent {} + +class IntEvent extends PlatformEvent { + IntEvent({ + required this.value, + }); + + int value; + + Object encode() { + return [ + value, + ]; + } + + static IntEvent decode(Object result) { + result as List; + return IntEvent( + value: result[0]! as int, + ); + } +} + +class StringEvent extends PlatformEvent { + StringEvent({ + required this.value, + }); + + String value; + + Object encode() { + return [ + value, + ]; + } + + static StringEvent decode(Object result) { + result as List; + return StringEvent( + value: result[0]! as String, + ); + } +} + +class BoolEvent extends PlatformEvent { + BoolEvent({ + required this.value, + }); + + bool value; + + Object encode() { + return [ + value, + ]; + } + + static BoolEvent decode(Object result) { + result as List; + return BoolEvent( + value: result[0]! as bool, + ); + } +} + +class DoubleEvent extends PlatformEvent { + DoubleEvent({ + required this.value, + }); + + double value; + + Object encode() { + return [ + value, + ]; + } + + static DoubleEvent decode(Object result) { + result as List; + return DoubleEvent( + value: result[0]! as double, + ); + } +} + +class ObjectsEvent extends PlatformEvent { + ObjectsEvent({ + required this.value, + }); + + Object value; + + Object encode() { + return [ + value, + ]; + } + + static ObjectsEvent decode(Object result) { + result as List; + return ObjectsEvent( + value: result[0]!, + ); + } +} + +class EnumEvent extends PlatformEvent { + EnumEvent({ + required this.value, + }); + + EventEnum value; + + Object encode() { + return [ + value, + ]; + } + + static EnumEvent decode(Object result) { + result as List; + return EnumEvent( + value: result[0]! as EventEnum, + ); + } +} + +class ClassEvent extends PlatformEvent { + ClassEvent({ + required this.value, + }); + + EventAllNullableTypes value; + + Object encode() { + return [ + value, + ]; + } + + static ClassEvent decode(Object result) { + result as List; + return ClassEvent( + value: result[0]! as EventAllNullableTypes, + ); + } +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is EventEnum) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is AnotherEventEnum) { + buffer.putUint8(130); + writeValue(buffer, value.index); + } else if (value is EventAllNullableTypes) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is IntEvent) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is StringEvent) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is BoolEvent) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is DoubleEvent) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is ObjectsEvent) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is EnumEvent) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is ClassEvent) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : EventEnum.values[value]; + case 130: + final int? value = readValue(buffer) as int?; + return value == null ? null : AnotherEventEnum.values[value]; + case 131: + return EventAllNullableTypes.decode(readValue(buffer)!); + case 132: + return IntEvent.decode(readValue(buffer)!); + case 133: + return StringEvent.decode(readValue(buffer)!); + case 134: + return BoolEvent.decode(readValue(buffer)!); + case 135: + return DoubleEvent.decode(readValue(buffer)!); + case 136: + return ObjectsEvent.decode(readValue(buffer)!); + case 137: + return EnumEvent.decode(readValue(buffer)!); + case 138: + return ClassEvent.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +const StandardMethodCodec pigeonMethodCodec = + StandardMethodCodec(_PigeonCodec()); + +Stream streamInts({String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.$instanceName'; + } + const EventChannel streamIntsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamInts', + pigeonMethodCodec); + return streamIntsChannel.receiveBroadcastStream().map((dynamic event) { + return event as int; + }); +} + +Stream streamEvents({String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.$instanceName'; + } + const EventChannel streamEventsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamEvents', + pigeonMethodCodec); + return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { + return event as PlatformEvent; + }); +} diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart index 54d95c4149b7..a371c3333497 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart @@ -54,7 +54,6 @@ abstract class PigeonInternalProxyApiBaseClass { final BinaryMessenger? pigeon_binaryMessenger; /// Maintains instances stored to communicate with native language objects. - @protected final PigeonInstanceManager pigeon_instanceManager; /// Instantiates and returns a functionally identical object to oneself. @@ -579,6 +578,104 @@ class ProxyApiTestClass extends ProxyApiSuperClass }(); } + ProxyApiTestClass.namedConstructor({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.aBool, + required this.anInt, + required this.aDouble, + required this.aString, + required this.aUint8List, + required this.aList, + required this.aMap, + required this.anEnum, + required this.aProxyApi, + this.aNullableBool, + this.aNullableInt, + this.aNullableDouble, + this.aNullableString, + this.aNullableUint8List, + this.aNullableList, + this.aNullableMap, + this.aNullableEnum, + this.aNullableProxyApi, + this.anInterfaceMethod, + this.flutterNoop, + this.flutterThrowError, + this.flutterThrowErrorFromVoid, + this.flutterEchoBool, + this.flutterEchoInt, + this.flutterEchoDouble, + this.flutterEchoString, + this.flutterEchoUint8List, + this.flutterEchoList, + this.flutterEchoProxyApiList, + this.flutterEchoMap, + this.flutterEchoProxyApiMap, + this.flutterEchoEnum, + this.flutterEchoProxyApi, + this.flutterEchoNullableBool, + this.flutterEchoNullableInt, + this.flutterEchoNullableDouble, + this.flutterEchoNullableString, + this.flutterEchoNullableUint8List, + this.flutterEchoNullableList, + this.flutterEchoNullableMap, + this.flutterEchoNullableEnum, + this.flutterEchoNullableProxyApi, + this.flutterNoopAsync, + this.flutterEchoAsyncString, + }) : super.pigeon_detached() { + final int pigeonVar_instanceIdentifier = + pigeon_instanceManager.addDartCreatedInstance(this); + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecProxyApiTestClass; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + () async { + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.namedConstructor'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([ + pigeonVar_instanceIdentifier, + aBool, + anInt, + aDouble, + aString, + aUint8List, + aList, + aMap, + anEnum, + aProxyApi, + aNullableBool, + aNullableInt, + aNullableDouble, + aNullableString, + aNullableUint8List, + aNullableList, + aNullableMap, + aNullableEnum, + aNullableProxyApi + ]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + }(); + } + /// Constructs [ProxyApiTestClass] without creating the associated native object. /// /// This should only be used by subclasses created by this library or to diff --git a/packages/pigeon/platform_tests/test_plugin/android/build.gradle b/packages/pigeon/platform_tests/test_plugin/android/build.gradle index 2ed864258e26..348ded6b8f8c 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/build.gradle +++ b/packages/pigeon/platform_tests/test_plugin/android/build.gradle @@ -2,7 +2,7 @@ group 'com.example.test_plugin' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '2.0.20' + ext.kotlin_version = '2.1.0' repositories { google() mavenCentral() @@ -66,9 +66,9 @@ android { dependencies { testImplementation 'junit:junit:4.+' - testImplementation "io.mockk:mockk:1.13.12" + testImplementation "io.mockk:mockk:1.13.13" // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 - implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) + implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.0.21")) } } diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore index 5f3fcaf31604..bbe6aef544f5 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore @@ -7,5 +7,7 @@ # This contains the declaration of the test classes wrapped by the ProxyApi tests and the # implemetations of their APIs. !ProxyApiTestApiImpls.kt -# Including this makes it easier to review code generation changes. + +# Including these makes it easier to review code generation changes. !ProxyApiTests.gen.kt +!EventChannelTests.gen.kt diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt new file mode 100644 index 000000000000..08eb85b55ef7 --- /dev/null +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt @@ -0,0 +1,472 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package com.example.test_plugin + +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class EventChannelTestsError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class EventEnum(val raw: Int) { + ONE(0), + TWO(1), + THREE(2), + FORTY_TWO(3), + FOUR_HUNDRED_TWENTY_TWO(4); + + companion object { + fun ofRaw(raw: Int): EventEnum? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class AnotherEventEnum(val raw: Int) { + JUST_IN_CASE(0); + + companion object { + fun ofRaw(raw: Int): AnotherEventEnum? { + return values().firstOrNull { it.raw == raw } + } + } +} + +/** + * A class containing all supported nullable types. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class EventAllNullableTypes( + val aNullableBool: Boolean? = null, + val aNullableInt: Long? = null, + val aNullableInt64: Long? = null, + val aNullableDouble: Double? = null, + val aNullableByteArray: ByteArray? = null, + val aNullable4ByteArray: IntArray? = null, + val aNullable8ByteArray: LongArray? = null, + val aNullableFloatArray: DoubleArray? = null, + val aNullableEnum: EventEnum? = null, + val anotherNullableEnum: AnotherEventEnum? = null, + val aNullableString: String? = null, + val aNullableObject: Any? = null, + val allNullableTypes: EventAllNullableTypes? = null, + val list: List? = null, + val stringList: List? = null, + val intList: List? = null, + val doubleList: List? = null, + val boolList: List? = null, + val enumList: List? = null, + val objectList: List? = null, + val listList: List?>? = null, + val mapList: List?>? = null, + val recursiveClassList: List? = null, + val map: Map? = null, + val stringMap: Map? = null, + val intMap: Map? = null, + val enumMap: Map? = null, + val objectMap: Map? = null, + val listMap: Map?>? = null, + val mapMap: Map?>? = null, + val recursiveClassMap: Map? = null +) { + companion object { + fun fromList(pigeonVar_list: List): EventAllNullableTypes { + val aNullableBool = pigeonVar_list[0] as Boolean? + val aNullableInt = pigeonVar_list[1] as Long? + val aNullableInt64 = pigeonVar_list[2] as Long? + val aNullableDouble = pigeonVar_list[3] as Double? + val aNullableByteArray = pigeonVar_list[4] as ByteArray? + val aNullable4ByteArray = pigeonVar_list[5] as IntArray? + val aNullable8ByteArray = pigeonVar_list[6] as LongArray? + val aNullableFloatArray = pigeonVar_list[7] as DoubleArray? + val aNullableEnum = pigeonVar_list[8] as EventEnum? + val anotherNullableEnum = pigeonVar_list[9] as AnotherEventEnum? + val aNullableString = pigeonVar_list[10] as String? + val aNullableObject = pigeonVar_list[11] + val allNullableTypes = pigeonVar_list[12] as EventAllNullableTypes? + val list = pigeonVar_list[13] as List? + val stringList = pigeonVar_list[14] as List? + val intList = pigeonVar_list[15] as List? + val doubleList = pigeonVar_list[16] as List? + val boolList = pigeonVar_list[17] as List? + val enumList = pigeonVar_list[18] as List? + val objectList = pigeonVar_list[19] as List? + val listList = pigeonVar_list[20] as List?>? + val mapList = pigeonVar_list[21] as List?>? + val recursiveClassList = pigeonVar_list[22] as List? + val map = pigeonVar_list[23] as Map? + val stringMap = pigeonVar_list[24] as Map? + val intMap = pigeonVar_list[25] as Map? + val enumMap = pigeonVar_list[26] as Map? + val objectMap = pigeonVar_list[27] as Map? + val listMap = pigeonVar_list[28] as Map?>? + val mapMap = pigeonVar_list[29] as Map?>? + val recursiveClassMap = pigeonVar_list[30] as Map? + return EventAllNullableTypes( + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap) + } + } + + fun toList(): List { + return listOf( + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ) + } +} + +/** + * Generated class from Pigeon that represents data sent in messages. This class should not be + * extended by any user class outside of the generated file. + */ +sealed class PlatformEvent +/** Generated class from Pigeon that represents data sent in messages. */ +data class IntEvent(val value: Long) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): IntEvent { + val value = pigeonVar_list[0] as Long + return IntEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class StringEvent(val value: String) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): StringEvent { + val value = pigeonVar_list[0] as String + return StringEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class BoolEvent(val value: Boolean) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): BoolEvent { + val value = pigeonVar_list[0] as Boolean + return BoolEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class DoubleEvent(val value: Double) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): DoubleEvent { + val value = pigeonVar_list[0] as Double + return DoubleEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class ObjectsEvent(val value: Any) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): ObjectsEvent { + val value = pigeonVar_list[0] as Any + return ObjectsEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class EnumEvent(val value: EventEnum) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): EnumEvent { + val value = pigeonVar_list[0] as EventEnum + return EnumEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class ClassEvent(val value: EventAllNullableTypes) : PlatformEvent() { + companion object { + fun fromList(pigeonVar_list: List): ClassEvent { + val value = pigeonVar_list[0] as EventAllNullableTypes + return ClassEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +private open class EventChannelTestsPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as Long?)?.let { EventEnum.ofRaw(it.toInt()) } + } + 130.toByte() -> { + return (readValue(buffer) as Long?)?.let { AnotherEventEnum.ofRaw(it.toInt()) } + } + 131.toByte() -> { + return (readValue(buffer) as? List)?.let { EventAllNullableTypes.fromList(it) } + } + 132.toByte() -> { + return (readValue(buffer) as? List)?.let { IntEvent.fromList(it) } + } + 133.toByte() -> { + return (readValue(buffer) as? List)?.let { StringEvent.fromList(it) } + } + 134.toByte() -> { + return (readValue(buffer) as? List)?.let { BoolEvent.fromList(it) } + } + 135.toByte() -> { + return (readValue(buffer) as? List)?.let { DoubleEvent.fromList(it) } + } + 136.toByte() -> { + return (readValue(buffer) as? List)?.let { ObjectsEvent.fromList(it) } + } + 137.toByte() -> { + return (readValue(buffer) as? List)?.let { EnumEvent.fromList(it) } + } + 138.toByte() -> { + return (readValue(buffer) as? List)?.let { ClassEvent.fromList(it) } + } + else -> super.readValueOfType(type, buffer) + } + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is EventEnum -> { + stream.write(129) + writeValue(stream, value.raw) + } + is AnotherEventEnum -> { + stream.write(130) + writeValue(stream, value.raw) + } + is EventAllNullableTypes -> { + stream.write(131) + writeValue(stream, value.toList()) + } + is IntEvent -> { + stream.write(132) + writeValue(stream, value.toList()) + } + is StringEvent -> { + stream.write(133) + writeValue(stream, value.toList()) + } + is BoolEvent -> { + stream.write(134) + writeValue(stream, value.toList()) + } + is DoubleEvent -> { + stream.write(135) + writeValue(stream, value.toList()) + } + is ObjectsEvent -> { + stream.write(136) + writeValue(stream, value.toList()) + } + is EnumEvent -> { + stream.write(137) + writeValue(stream, value.toList()) + } + is ClassEvent -> { + stream.write(138) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +val EventChannelTestsPigeonMethodCodec = StandardMethodCodec(EventChannelTestsPigeonCodec()) + +private class PigeonStreamHandler(val wrapper: PigeonEventChannelWrapper) : + EventChannel.StreamHandler { + var pigeonSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + pigeonSink = PigeonEventSink(sink) + wrapper.onListen(p0, pigeonSink!!) + } + + override fun onCancel(p0: Any?) { + pigeonSink = null + wrapper.onCancel(p0) + } +} + +interface PigeonEventChannelWrapper { + open fun onListen(p0: Any?, sink: PigeonEventSink) {} + + open fun onCancel(p0: Any?) {} +} + +class PigeonEventSink(private val sink: EventChannel.EventSink) { + fun success(value: T) { + sink.success(value) + } + + fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + sink.error(errorCode, errorMessage, errorDetails) + } + + fun endOfStream() { + sink.endOfStream() + } +} + +abstract class StreamIntsStreamHandler : PigeonEventChannelWrapper { + companion object { + fun register( + messenger: BinaryMessenger, + streamHandler: StreamIntsStreamHandler, + instanceName: String = "" + ) { + var channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamInts" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val internalStreamHandler = PigeonStreamHandler(streamHandler) + EventChannel(messenger, channelName, EventChannelTestsPigeonMethodCodec) + .setStreamHandler(internalStreamHandler) + } + } +} + +abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { + companion object { + fun register( + messenger: BinaryMessenger, + streamHandler: StreamEventsStreamHandler, + instanceName: String = "" + ) { + var channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamEvents" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val internalStreamHandler = PigeonStreamHandler(streamHandler) + EventChannel(messenger, channelName, EventChannelTestsPigeonMethodCodec) + .setStreamHandler(internalStreamHandler) + } + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTestApiImpls.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTestApiImpls.kt index bfe6e4257e79..b9389fd10dc7 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTestApiImpls.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTestApiImpls.kt @@ -74,6 +74,29 @@ class ProxyApiTestClassApi(override val pigeonRegistrar: ProxyApiRegistrar) : return ProxyApiTestClass() } + override fun namedConstructor( + aBool: Boolean, + anInt: Long, + aDouble: Double, + aString: String, + aUint8List: ByteArray, + aList: List, + aMap: Map, + anEnum: ProxyApiTestEnum, + aProxyApi: ProxyApiSuperClass, + aNullableBool: Boolean?, + aNullableInt: Long?, + aNullableDouble: Double?, + aNullableString: String?, + aNullableUint8List: ByteArray?, + aNullableList: List?, + aNullableMap: Map?, + aNullableEnum: ProxyApiTestEnum?, + aNullableProxyApi: ProxyApiSuperClass?, + ): ProxyApiTestClass { + return ProxyApiTestClass() + } + override fun attachedField(pigeon_instance: ProxyApiTestClass): ProxyApiSuperClass { return ProxyApiSuperClass() } diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt index 184a5b00f599..08b6b635c27e 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/ProxyApiTests.gen.kt @@ -549,6 +549,27 @@ abstract class PigeonApiProxyApiTestClass( nullableProxyApiParam: com.example.test_plugin.ProxyApiSuperClass? ): ProxyApiTestClass + abstract fun namedConstructor( + aBool: Boolean, + anInt: Long, + aDouble: Double, + aString: String, + aUint8List: ByteArray, + aList: List, + aMap: Map, + anEnum: ProxyApiTestEnum, + aProxyApi: com.example.test_plugin.ProxyApiSuperClass, + aNullableBool: Boolean?, + aNullableInt: Long?, + aNullableDouble: Double?, + aNullableString: String?, + aNullableUint8List: ByteArray?, + aNullableList: List?, + aNullableMap: Map?, + aNullableEnum: ProxyApiTestEnum?, + aNullableProxyApi: com.example.test_plugin.ProxyApiSuperClass? + ): ProxyApiTestClass + abstract fun attachedField( pigeon_instance: ProxyApiTestClass ): com.example.test_plugin.ProxyApiSuperClass @@ -1108,6 +1129,67 @@ abstract class PigeonApiProxyApiTestClass( channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.namedConstructor", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_identifierArg = args[0] as Long + val aBoolArg = args[1] as Boolean + val anIntArg = args[2] as Long + val aDoubleArg = args[3] as Double + val aStringArg = args[4] as String + val aUint8ListArg = args[5] as ByteArray + val aListArg = args[6] as List + val aMapArg = args[7] as Map + val anEnumArg = args[8] as ProxyApiTestEnum + val aProxyApiArg = args[9] as com.example.test_plugin.ProxyApiSuperClass + val aNullableBoolArg = args[10] as Boolean? + val aNullableIntArg = args[11] as Long? + val aNullableDoubleArg = args[12] as Double? + val aNullableStringArg = args[13] as String? + val aNullableUint8ListArg = args[14] as ByteArray? + val aNullableListArg = args[15] as List? + val aNullableMapArg = args[16] as Map? + val aNullableEnumArg = args[17] as ProxyApiTestEnum? + val aNullableProxyApiArg = args[18] as com.example.test_plugin.ProxyApiSuperClass? + val wrapped: List = + try { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + api.namedConstructor( + aBoolArg, + anIntArg, + aDoubleArg, + aStringArg, + aUint8ListArg, + aListArg, + aMapArg, + anEnumArg, + aProxyApiArg, + aNullableBoolArg, + aNullableIntArg, + aNullableDoubleArg, + aNullableStringArg, + aNullableUint8ListArg, + aNullableListArg, + aNullableMapArg, + aNullableEnumArg, + aNullableProxyApiArg), + pigeon_identifierArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index 9563941624d7..f175076abcda 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -4,6 +4,8 @@ package com.example.test_plugin +import android.os.Handler +import android.os.Looper import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding @@ -26,6 +28,9 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { proxyApiRegistrar = ProxyApiRegistrar(binding.binaryMessenger) proxyApiRegistrar!!.setUp() + + StreamEventsStreamHandler.register(binding.binaryMessenger, SendClass) + StreamIntsStreamHandler.register(binding.binaryMessenger, SendInts) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { @@ -867,3 +872,58 @@ class TestPluginWithSuffix : HostSmallApi { callback(Result.success(Unit)) } } + +object SendInts : StreamIntsStreamHandler() { + val handler = Handler(Looper.getMainLooper()) + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + var count: Long = 0 + val r: Runnable = + object : Runnable { + override fun run() { + handler.post { + if (count >= 5) { + sink.endOfStream() + } else { + sink.success(count) + count++ + handler.postDelayed(this, 10) + } + } + } + } + handler.postDelayed(r, 10) + } +} + +object SendClass : StreamEventsStreamHandler() { + val handler = Handler(Looper.getMainLooper()) + val eventList = + listOf( + IntEvent(1), + StringEvent("string"), + BoolEvent(false), + DoubleEvent(3.14), + ObjectsEvent(true), + EnumEvent(EventEnum.FORTY_TWO), + ClassEvent(EventAllNullableTypes(aNullableInt = 0))) + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + var count: Int = 0 + val r: Runnable = + object : Runnable { + override fun run() { + if (count >= eventList.size) { + sink.endOfStream() + } else { + handler.post { + sink.success(eventList[count]) + count++ + } + handler.postDelayed(this, 10) + } + } + } + handler.postDelayed(r, 10) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift b/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore b/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore index 18ad850358d9..94e339ef912d 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore @@ -5,5 +5,7 @@ !CoreTests.gen.swift # Keeping this makes it easier to review changes to ProxyApi generation. !ProxyApiTests.gen.swift -# Contains the class declartions for testing ProxyApis. + +# Including these makes it easier to review code generation changes. !ProxyApiTestClass.swift +!EventChannelTests.gen.swift diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift new file mode 100644 index 000000000000..09f682ece4b6 --- /dev/null +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift @@ -0,0 +1,576 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class EventChannelTestsError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "EventChannelTestsError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +enum EventEnum: Int { + case one = 0 + case two = 1 + case three = 2 + case fortyTwo = 3 + case fourHundredTwentyTwo = 4 +} + +enum AnotherEventEnum: Int { + case justInCase = 0 +} + +/// A class containing all supported nullable types. +/// +/// Generated class from Pigeon that represents data sent in messages. +class EventAllNullableTypes { + init( + aNullableBool: Bool? = nil, + aNullableInt: Int64? = nil, + aNullableInt64: Int64? = nil, + aNullableDouble: Double? = nil, + aNullableByteArray: FlutterStandardTypedData? = nil, + aNullable4ByteArray: FlutterStandardTypedData? = nil, + aNullable8ByteArray: FlutterStandardTypedData? = nil, + aNullableFloatArray: FlutterStandardTypedData? = nil, + aNullableEnum: EventEnum? = nil, + anotherNullableEnum: AnotherEventEnum? = nil, + aNullableString: String? = nil, + aNullableObject: Any? = nil, + allNullableTypes: EventAllNullableTypes? = nil, + list: [Any?]? = nil, + stringList: [String?]? = nil, + intList: [Int64?]? = nil, + doubleList: [Double?]? = nil, + boolList: [Bool?]? = nil, + enumList: [EventEnum?]? = nil, + objectList: [Any?]? = nil, + listList: [[Any?]?]? = nil, + mapList: [[AnyHashable?: Any?]?]? = nil, + recursiveClassList: [EventAllNullableTypes?]? = nil, + map: [AnyHashable?: Any?]? = nil, + stringMap: [String?: String?]? = nil, + intMap: [Int64?: Int64?]? = nil, + enumMap: [EventEnum?: EventEnum?]? = nil, + objectMap: [AnyHashable?: Any?]? = nil, + listMap: [Int64?: [Any?]?]? = nil, + mapMap: [Int64?: [AnyHashable?: Any?]?]? = nil, + recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nil + ) { + self.aNullableBool = aNullableBool + self.aNullableInt = aNullableInt + self.aNullableInt64 = aNullableInt64 + self.aNullableDouble = aNullableDouble + self.aNullableByteArray = aNullableByteArray + self.aNullable4ByteArray = aNullable4ByteArray + self.aNullable8ByteArray = aNullable8ByteArray + self.aNullableFloatArray = aNullableFloatArray + self.aNullableEnum = aNullableEnum + self.anotherNullableEnum = anotherNullableEnum + self.aNullableString = aNullableString + self.aNullableObject = aNullableObject + self.allNullableTypes = allNullableTypes + self.list = list + self.stringList = stringList + self.intList = intList + self.doubleList = doubleList + self.boolList = boolList + self.enumList = enumList + self.objectList = objectList + self.listList = listList + self.mapList = mapList + self.recursiveClassList = recursiveClassList + self.map = map + self.stringMap = stringMap + self.intMap = intMap + self.enumMap = enumMap + self.objectMap = objectMap + self.listMap = listMap + self.mapMap = mapMap + self.recursiveClassMap = recursiveClassMap + } + var aNullableBool: Bool? + var aNullableInt: Int64? + var aNullableInt64: Int64? + var aNullableDouble: Double? + var aNullableByteArray: FlutterStandardTypedData? + var aNullable4ByteArray: FlutterStandardTypedData? + var aNullable8ByteArray: FlutterStandardTypedData? + var aNullableFloatArray: FlutterStandardTypedData? + var aNullableEnum: EventEnum? + var anotherNullableEnum: AnotherEventEnum? + var aNullableString: String? + var aNullableObject: Any? + var allNullableTypes: EventAllNullableTypes? + var list: [Any?]? + var stringList: [String?]? + var intList: [Int64?]? + var doubleList: [Double?]? + var boolList: [Bool?]? + var enumList: [EventEnum?]? + var objectList: [Any?]? + var listList: [[Any?]?]? + var mapList: [[AnyHashable?: Any?]?]? + var recursiveClassList: [EventAllNullableTypes?]? + var map: [AnyHashable?: Any?]? + var stringMap: [String?: String?]? + var intMap: [Int64?: Int64?]? + var enumMap: [EventEnum?: EventEnum?]? + var objectMap: [AnyHashable?: Any?]? + var listMap: [Int64?: [Any?]?]? + var mapMap: [Int64?: [AnyHashable?: Any?]?]? + var recursiveClassMap: [Int64?: EventAllNullableTypes?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EventAllNullableTypes? { + let aNullableBool: Bool? = nilOrValue(pigeonVar_list[0]) + let aNullableInt: Int64? = nilOrValue(pigeonVar_list[1]) + let aNullableInt64: Int64? = nilOrValue(pigeonVar_list[2]) + let aNullableDouble: Double? = nilOrValue(pigeonVar_list[3]) + let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[4]) + let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[5]) + let aNullable8ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[6]) + let aNullableFloatArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[7]) + let aNullableEnum: EventEnum? = nilOrValue(pigeonVar_list[8]) + let anotherNullableEnum: AnotherEventEnum? = nilOrValue(pigeonVar_list[9]) + let aNullableString: String? = nilOrValue(pigeonVar_list[10]) + let aNullableObject: Any? = pigeonVar_list[11] + let allNullableTypes: EventAllNullableTypes? = nilOrValue(pigeonVar_list[12]) + let list: [Any?]? = nilOrValue(pigeonVar_list[13]) + let stringList: [String?]? = nilOrValue(pigeonVar_list[14]) + let intList: [Int64?]? = nilOrValue(pigeonVar_list[15]) + let doubleList: [Double?]? = nilOrValue(pigeonVar_list[16]) + let boolList: [Bool?]? = nilOrValue(pigeonVar_list[17]) + let enumList: [EventEnum?]? = nilOrValue(pigeonVar_list[18]) + let objectList: [Any?]? = nilOrValue(pigeonVar_list[19]) + let listList: [[Any?]?]? = nilOrValue(pigeonVar_list[20]) + let mapList: [[AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[21]) + let recursiveClassList: [EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[22]) + let map: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[23]) + let stringMap: [String?: String?]? = nilOrValue(pigeonVar_list[24]) + let intMap: [Int64?: Int64?]? = nilOrValue(pigeonVar_list[25]) + let enumMap: [EventEnum?: EventEnum?]? = pigeonVar_list[26] as? [EventEnum?: EventEnum?] + let objectMap: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[27]) + let listMap: [Int64?: [Any?]?]? = nilOrValue(pigeonVar_list[28]) + let mapMap: [Int64?: [AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[29]) + let recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[30]) + + return EventAllNullableTypes( + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableInt64: aNullableInt64, + aNullableDouble: aNullableDouble, + aNullableByteArray: aNullableByteArray, + aNullable4ByteArray: aNullable4ByteArray, + aNullable8ByteArray: aNullable8ByteArray, + aNullableFloatArray: aNullableFloatArray, + aNullableEnum: aNullableEnum, + anotherNullableEnum: anotherNullableEnum, + aNullableString: aNullableString, + aNullableObject: aNullableObject, + allNullableTypes: allNullableTypes, + list: list, + stringList: stringList, + intList: intList, + doubleList: doubleList, + boolList: boolList, + enumList: enumList, + objectList: objectList, + listList: listList, + mapList: mapList, + recursiveClassList: recursiveClassList, + map: map, + stringMap: stringMap, + intMap: intMap, + enumMap: enumMap, + objectMap: objectMap, + listMap: listMap, + mapMap: mapMap, + recursiveClassMap: recursiveClassMap + ) + } + func toList() -> [Any?] { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +/// This protocol should not be extended by any user class outside of the generated file. +protocol PlatformEvent { + +} + +/// Generated class from Pigeon that represents data sent in messages. +struct IntEvent: PlatformEvent { + var value: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> IntEvent? { + let value = pigeonVar_list[0] as! Int64 + + return IntEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct StringEvent: PlatformEvent { + var value: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> StringEvent? { + let value = pigeonVar_list[0] as! String + + return StringEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct BoolEvent: PlatformEvent { + var value: Bool + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> BoolEvent? { + let value = pigeonVar_list[0] as! Bool + + return BoolEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DoubleEvent: PlatformEvent { + var value: Double + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DoubleEvent? { + let value = pigeonVar_list[0] as! Double + + return DoubleEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ObjectsEvent: PlatformEvent { + var value: Any + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ObjectsEvent? { + let value = pigeonVar_list[0]! + + return ObjectsEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct EnumEvent: PlatformEvent { + var value: EventEnum + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EnumEvent? { + let value = pigeonVar_list[0] as! EventEnum + + return EnumEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ClassEvent: PlatformEvent { + var value: EventAllNullableTypes + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ClassEvent? { + let value = pigeonVar_list[0] as! EventAllNullableTypes + + return ClassEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +private class EventChannelTestsPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return EventEnum(rawValue: enumResultAsInt) + } + return nil + case 130: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return AnotherEventEnum(rawValue: enumResultAsInt) + } + return nil + case 131: + return EventAllNullableTypes.fromList(self.readValue() as! [Any?]) + case 132: + return IntEvent.fromList(self.readValue() as! [Any?]) + case 133: + return StringEvent.fromList(self.readValue() as! [Any?]) + case 134: + return BoolEvent.fromList(self.readValue() as! [Any?]) + case 135: + return DoubleEvent.fromList(self.readValue() as! [Any?]) + case 136: + return ObjectsEvent.fromList(self.readValue() as! [Any?]) + case 137: + return EnumEvent.fromList(self.readValue() as! [Any?]) + case 138: + return ClassEvent.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class EventChannelTestsPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? EventEnum { + super.writeByte(129) + super.writeValue(value.rawValue) + } else if let value = value as? AnotherEventEnum { + super.writeByte(130) + super.writeValue(value.rawValue) + } else if let value = value as? EventAllNullableTypes { + super.writeByte(131) + super.writeValue(value.toList()) + } else if let value = value as? IntEvent { + super.writeByte(132) + super.writeValue(value.toList()) + } else if let value = value as? StringEvent { + super.writeByte(133) + super.writeValue(value.toList()) + } else if let value = value as? BoolEvent { + super.writeByte(134) + super.writeValue(value.toList()) + } else if let value = value as? DoubleEvent { + super.writeByte(135) + super.writeValue(value.toList()) + } else if let value = value as? ObjectsEvent { + super.writeByte(136) + super.writeValue(value.toList()) + } else if let value = value as? EnumEvent { + super.writeByte(137) + super.writeValue(value.toList()) + } else if let value = value as? ClassEvent { + super.writeByte(138) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class EventChannelTestsPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return EventChannelTestsPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return EventChannelTestsPigeonCodecWriter(data: data) + } +} + +class EventChannelTestsPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = EventChannelTestsPigeonCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) +} + +var eventChannelTestsPigeonMethodCodec = FlutterStandardMethodCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) + +private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } +} + +class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} +} + +class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + +} + +class StreamIntsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: StreamIntsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamInts" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(internalStreamHandler) + } +} + +class StreamEventsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: StreamEventsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamEvents" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(internalStreamHandler) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/ProxyApiTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/ProxyApiTests.gen.swift index d3a699ca47a9..506565c1dd67 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/ProxyApiTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/ProxyApiTests.gen.swift @@ -628,6 +628,15 @@ protocol PigeonApiDelegateProxyApiTestClass { nullableListParam: [Any?]?, nullableMapParam: [String?: Any?]?, nullableEnumParam: ProxyApiTestEnum?, nullableProxyApiParam: ProxyApiSuperClass? ) throws -> ProxyApiTestClass + func namedConstructor( + pigeonApi: PigeonApiProxyApiTestClass, aBool: Bool, anInt: Int64, aDouble: Double, + aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], aMap: [String?: Any?], + anEnum: ProxyApiTestEnum, aProxyApi: ProxyApiSuperClass, aNullableBool: Bool?, + aNullableInt: Int64?, aNullableDouble: Double?, aNullableString: String?, + aNullableUint8List: FlutterStandardTypedData?, aNullableList: [Any?]?, + aNullableMap: [String?: Any?]?, aNullableEnum: ProxyApiTestEnum?, + aNullableProxyApi: ProxyApiSuperClass? + ) throws -> ProxyApiTestClass func attachedField(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws -> ProxyApiSuperClass func staticAttachedField(pigeonApi: PigeonApiProxyApiTestClass) throws -> ProxyApiSuperClass @@ -1168,6 +1177,50 @@ final class PigeonApiProxyApiTestClass: PigeonApiProtocolProxyApiTestClass { } else { pigeonDefaultConstructorChannel.setMessageHandler(nil) } + let namedConstructorChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.namedConstructor", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + namedConstructorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pigeonIdentifierArg = args[0] as! Int64 + let aBoolArg = args[1] as! Bool + let anIntArg = args[2] as! Int64 + let aDoubleArg = args[3] as! Double + let aStringArg = args[4] as! String + let aUint8ListArg = args[5] as! FlutterStandardTypedData + let aListArg = args[6] as! [Any?] + let aMapArg = args[7] as! [String?: Any?] + let anEnumArg = args[8] as! ProxyApiTestEnum + let aProxyApiArg = args[9] as! ProxyApiSuperClass + let aNullableBoolArg: Bool? = nilOrValue(args[10]) + let aNullableIntArg: Int64? = nilOrValue(args[11]) + let aNullableDoubleArg: Double? = nilOrValue(args[12]) + let aNullableStringArg: String? = nilOrValue(args[13]) + let aNullableUint8ListArg: FlutterStandardTypedData? = nilOrValue(args[14]) + let aNullableListArg: [Any?]? = nilOrValue(args[15]) + let aNullableMapArg: [String?: Any?]? = nilOrValue(args[16]) + let aNullableEnumArg: ProxyApiTestEnum? = nilOrValue(args[17]) + let aNullableProxyApiArg: ProxyApiSuperClass? = nilOrValue(args[18]) + do { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + try api.pigeonDelegate.namedConstructor( + pigeonApi: api, aBool: aBoolArg, anInt: anIntArg, aDouble: aDoubleArg, + aString: aStringArg, aUint8List: aUint8ListArg, aList: aListArg, aMap: aMapArg, + anEnum: anEnumArg, aProxyApi: aProxyApiArg, aNullableBool: aNullableBoolArg, + aNullableInt: aNullableIntArg, aNullableDouble: aNullableDoubleArg, + aNullableString: aNullableStringArg, aNullableUint8List: aNullableUint8ListArg, + aNullableList: aNullableListArg, aNullableMap: aNullableMapArg, + aNullableEnum: aNullableEnumArg, aNullableProxyApi: aNullableProxyApiArg), + withIdentifier: pigeonIdentifierArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + namedConstructorChannel.setMessageHandler(nil) + } let attachedFieldChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.attachedField", binaryMessenger: binaryMessenger, codec: codec) diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index b4238de3be91..6b39a002a141 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -28,6 +28,9 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { binaryMessenger: binaryMessenger, messageChannelSuffix: "suffixOne") flutterSmallApiTwo = FlutterSmallApi( binaryMessenger: binaryMessenger, messageChannelSuffix: "suffixTwo") + + StreamIntsStreamHandler.register(with: binaryMessenger, streamHandler: SendInts()) + StreamEventsStreamHandler.register(with: binaryMessenger, streamHandler: SendEvents()) proxyApiRegistrar = ProxyApiTestsPigeonProxyApiRegistrar( binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() @@ -1214,6 +1217,61 @@ public class TestPluginWithSuffix: HostSmallApi { } +class SendInts: StreamIntsStreamHandler { + var timerActive = false + var timer: Timer? + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count: Int64 = 0 + if !timerActive { + timerActive = true + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + sink.success(count) + count += 1 + if count >= 5 { + sink.endOfStream() + self.timer?.invalidate() + } + } + } + } + } +} + +class SendEvents: StreamEventsStreamHandler { + var timerActive = false + var timer: Timer? + var eventList: [PlatformEvent] = + [ + IntEvent(value: 1), + StringEvent(value: "string"), + BoolEvent(value: false), + DoubleEvent(value: 3.14), + ObjectsEvent(value: true), + EnumEvent(value: EventEnum.fortyTwo), + ClassEvent(value: EventAllNullableTypes(aNullableInt: 0)), + ] + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count = 0 + if !timerActive { + timerActive = true + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + if count >= self.eventList.count { + sink.endOfStream() + self.timer?.invalidate() + } else { + sink.success(self.eventList[count]) + count += 1 + } + } + } + } + } +} + class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func pigeonApiProxyApiTestClass(_ registrar: ProxyApiTestsPigeonProxyApiRegistrar) -> PigeonApiProxyApiTestClass @@ -1221,7 +1279,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { class ProxyApiTestClassDelegate: PigeonApiDelegateProxyApiTestClass { func pigeonDefaultConstructor( pigeonApi: PigeonApiProxyApiTestClass, aBool: Bool, anInt: Int64, aDouble: Double, - aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], aMap: [String?: Any?], + aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], + aMap: [String?: Any?], anEnum: ProxyApiTestEnum, aProxyApi: ProxyApiSuperClass, aNullableBool: Bool?, aNullableInt: Int64?, aNullableDouble: Double?, aNullableString: String?, aNullableUint8List: FlutterStandardTypedData?, aNullableList: [Any?]?, @@ -1238,35 +1297,53 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return ProxyApiTestClass() } + func namedConstructor( + pigeonApi: PigeonApiProxyApiTestClass, aBool: Bool, anInt: Int64, aDouble: Double, + aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], aMap: [String?: Any?], + anEnum: ProxyApiTestEnum, aProxyApi: ProxyApiSuperClass, aNullableBool: Bool?, + aNullableInt: Int64?, aNullableDouble: Double?, aNullableString: String?, + aNullableUint8List: FlutterStandardTypedData?, aNullableList: [Any?]?, + aNullableMap: [String?: Any?]?, aNullableEnum: ProxyApiTestEnum?, + aNullableProxyApi: ProxyApiSuperClass? + ) throws -> ProxyApiTestClass { + return ProxyApiTestClass() + } + func attachedField(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws -> ProxyApiSuperClass { return ProxyApiSuperClass() } - func staticAttachedField(pigeonApi: PigeonApiProxyApiTestClass) throws -> ProxyApiSuperClass { + func staticAttachedField(pigeonApi: PigeonApiProxyApiTestClass) throws + -> ProxyApiSuperClass + { return ProxyApiSuperClass() } - func aBool(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aBool(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> Bool { return true } - func anInt(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func anInt(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> Int64 { return 0 } - func aDouble(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aDouble(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> Double { return 0.0 } - func aString(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aString(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> String { return "" @@ -1278,7 +1355,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return FlutterStandardTypedData(bytes: Data()) } - func aList(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aList(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> [Any?] { return [] @@ -1290,7 +1368,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return [:] } - func anEnum(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func anEnum(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> ProxyApiTestEnum { return ProxyApiTestEnum.one @@ -1302,25 +1381,33 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return ProxyApiSuperClass() } - func aNullableBool(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableBool( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> Bool? { return nil } - func aNullableInt(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableInt( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> Int64? { return nil } - func aNullableDouble(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableDouble( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> Double? { return nil } - func aNullableString(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableString( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> String? { return nil @@ -1332,19 +1419,25 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return nil } - func aNullableList(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableList( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> [Any?]? { return nil } - func aNullableMap(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableMap( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> [String?: Any?]? { return nil } - func aNullableEnum(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableEnum( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> ProxyApiTestEnum? { return nil @@ -1384,7 +1477,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double ) throws -> Double { return aDouble } @@ -1396,7 +1490,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String ) throws -> String { return aString } @@ -1540,7 +1635,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double, completion: @escaping (Result) -> Void ) { completion(.success(aDouble)) @@ -1554,7 +1650,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String, completion: @escaping (Result) -> Void ) { completion(.success(aString)) @@ -1591,7 +1688,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func echoAsyncEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum, + completion: @escaping (Result) -> Void ) { completion(.success(anEnum)) } @@ -1628,7 +1726,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncNullableDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double?, completion: @escaping (Result) -> Void ) { completion(.success(aDouble)) @@ -1642,7 +1741,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncNullableString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String?, completion: @escaping (Result) -> Void ) { completion(.success(aString)) @@ -1657,14 +1757,16 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncNullableObject( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, anObject: Any?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + anObject: Any?, completion: @escaping (Result) -> Void ) { completion(.success(anObject)) } func echoAsyncNullableList( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aList: [Any?]?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void ) { completion(.success(aList)) @@ -1679,7 +1781,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func echoAsyncNullableEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum?, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum?, + completion: @escaping (Result) -> Void ) { completion(.success(anEnum)) } @@ -1688,13 +1791,15 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } - func echoStaticString(pigeonApi: PigeonApiProxyApiTestClass, aString: String) throws -> String + func echoStaticString(pigeonApi: PigeonApiProxyApiTestClass, aString: String) throws + -> String { return aString } func staticAsyncNoop( - pigeonApi: PigeonApiProxyApiTestClass, completion: @escaping (Result) -> Void + pigeonApi: PigeonApiProxyApiTestClass, + completion: @escaping (Result) -> Void ) { completion(.success(Void())) } @@ -1770,10 +1875,12 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoDouble(pigeonInstance: pigeonInstance, aDouble: aDouble) { response in + pigeonApi.flutterEchoDouble(pigeonInstance: pigeonInstance, aDouble: aDouble) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1784,10 +1891,12 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoString(pigeonInstance: pigeonInstance, aString: aString) { response in + pigeonApi.flutterEchoString(pigeonInstance: pigeonInstance, aString: aString) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1862,7 +1971,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { aMap: [String?: ProxyApiTestClass?], completion: @escaping (Result<[String?: ProxyApiTestClass?], Error>) -> Void ) { - pigeonApi.flutterEchoProxyApiMap(pigeonInstance: pigeonInstance, aMap: aMap) { response in + pigeonApi.flutterEchoProxyApiMap(pigeonInstance: pigeonInstance, aMap: aMap) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1874,7 +1984,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func callFlutterEchoEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum, + completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoEnum(pigeonInstance: pigeonInstance, anEnum: anEnum) { response in switch response { @@ -1921,7 +2032,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, anInt: Int64?, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoNullableInt(pigeonInstance: pigeonInstance, anInt: anInt) { response in + pigeonApi.flutterEchoNullableInt(pigeonInstance: pigeonInstance, anInt: anInt) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1932,7 +2044,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoNullableDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double?, completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoNullableDouble(pigeonInstance: pigeonInstance, aDouble: aDouble) { @@ -1947,7 +2060,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoNullableString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String?, completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoNullableString(pigeonInstance: pigeonInstance, aString: aString) { @@ -1966,7 +2080,9 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { aUint8List: FlutterStandardTypedData?, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoNullableUint8List(pigeonInstance: pigeonInstance, aList: aUint8List) { + pigeonApi.flutterEchoNullableUint8List( + pigeonInstance: pigeonInstance, aList: aUint8List + ) { response in switch response { case .success(let res): @@ -1978,7 +2094,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoNullableList( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aList: [Any?]?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void ) { pigeonApi.flutterEchoNullableList(pigeonInstance: pigeonInstance, aList: aList) { @@ -1996,7 +2113,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void ) { - pigeonApi.flutterEchoNullableMap(pigeonInstance: pigeonInstance, aMap: aMap) { response in + pigeonApi.flutterEchoNullableMap(pigeonInstance: pigeonInstance, aMap: aMap) { + response in switch response { case .success(let res): completion(.success(res)) @@ -2008,7 +2126,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func callFlutterEchoNullableEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum?, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum?, + completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoNullableEnum(pigeonInstance: pigeonInstance, anEnum: anEnum) { response in @@ -2026,8 +2145,9 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { aProxyApi: ProxyApiSuperClass?, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoNullableProxyApi(pigeonInstance: pigeonInstance, aProxyApi: aProxyApi) - { response in + pigeonApi.flutterEchoNullableProxyApi( + pigeonInstance: pigeonInstance, aProxyApi: aProxyApi + ) { response in switch response { case .success(let res): completion(.success(res)) @@ -2052,7 +2172,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoAsyncString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String, completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoAsyncString(pigeonInstance: pigeonInstance, aString: aString) { @@ -2081,7 +2202,9 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return ProxyApiSuperClass() } - func aSuperMethod(pigeonApi: PigeonApiProxyApiSuperClass, pigeonInstance: ProxyApiSuperClass) + func aSuperMethod( + pigeonApi: PigeonApiProxyApiSuperClass, pigeonInstance: ProxyApiSuperClass + ) throws {} } diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore b/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore index 7a58c79195bb..cd69c2f5c29a 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore @@ -5,5 +5,6 @@ !CoreTests.gen.swift # Keeping this makes it easier to review changes to ProxyApi generation. !ProxyApiTests.gen.swift -# Contains the class declartions for testing ProxyApis. -!ProxyApiTestClass.swift \ No newline at end of file +# Including these makes it easier to review code generation changes. +!ProxyApiTestClass.swift +!EventChannelTests.gen.swift \ No newline at end of file diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift new file mode 100644 index 000000000000..09f682ece4b6 --- /dev/null +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift @@ -0,0 +1,576 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class EventChannelTestsError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "EventChannelTestsError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +enum EventEnum: Int { + case one = 0 + case two = 1 + case three = 2 + case fortyTwo = 3 + case fourHundredTwentyTwo = 4 +} + +enum AnotherEventEnum: Int { + case justInCase = 0 +} + +/// A class containing all supported nullable types. +/// +/// Generated class from Pigeon that represents data sent in messages. +class EventAllNullableTypes { + init( + aNullableBool: Bool? = nil, + aNullableInt: Int64? = nil, + aNullableInt64: Int64? = nil, + aNullableDouble: Double? = nil, + aNullableByteArray: FlutterStandardTypedData? = nil, + aNullable4ByteArray: FlutterStandardTypedData? = nil, + aNullable8ByteArray: FlutterStandardTypedData? = nil, + aNullableFloatArray: FlutterStandardTypedData? = nil, + aNullableEnum: EventEnum? = nil, + anotherNullableEnum: AnotherEventEnum? = nil, + aNullableString: String? = nil, + aNullableObject: Any? = nil, + allNullableTypes: EventAllNullableTypes? = nil, + list: [Any?]? = nil, + stringList: [String?]? = nil, + intList: [Int64?]? = nil, + doubleList: [Double?]? = nil, + boolList: [Bool?]? = nil, + enumList: [EventEnum?]? = nil, + objectList: [Any?]? = nil, + listList: [[Any?]?]? = nil, + mapList: [[AnyHashable?: Any?]?]? = nil, + recursiveClassList: [EventAllNullableTypes?]? = nil, + map: [AnyHashable?: Any?]? = nil, + stringMap: [String?: String?]? = nil, + intMap: [Int64?: Int64?]? = nil, + enumMap: [EventEnum?: EventEnum?]? = nil, + objectMap: [AnyHashable?: Any?]? = nil, + listMap: [Int64?: [Any?]?]? = nil, + mapMap: [Int64?: [AnyHashable?: Any?]?]? = nil, + recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nil + ) { + self.aNullableBool = aNullableBool + self.aNullableInt = aNullableInt + self.aNullableInt64 = aNullableInt64 + self.aNullableDouble = aNullableDouble + self.aNullableByteArray = aNullableByteArray + self.aNullable4ByteArray = aNullable4ByteArray + self.aNullable8ByteArray = aNullable8ByteArray + self.aNullableFloatArray = aNullableFloatArray + self.aNullableEnum = aNullableEnum + self.anotherNullableEnum = anotherNullableEnum + self.aNullableString = aNullableString + self.aNullableObject = aNullableObject + self.allNullableTypes = allNullableTypes + self.list = list + self.stringList = stringList + self.intList = intList + self.doubleList = doubleList + self.boolList = boolList + self.enumList = enumList + self.objectList = objectList + self.listList = listList + self.mapList = mapList + self.recursiveClassList = recursiveClassList + self.map = map + self.stringMap = stringMap + self.intMap = intMap + self.enumMap = enumMap + self.objectMap = objectMap + self.listMap = listMap + self.mapMap = mapMap + self.recursiveClassMap = recursiveClassMap + } + var aNullableBool: Bool? + var aNullableInt: Int64? + var aNullableInt64: Int64? + var aNullableDouble: Double? + var aNullableByteArray: FlutterStandardTypedData? + var aNullable4ByteArray: FlutterStandardTypedData? + var aNullable8ByteArray: FlutterStandardTypedData? + var aNullableFloatArray: FlutterStandardTypedData? + var aNullableEnum: EventEnum? + var anotherNullableEnum: AnotherEventEnum? + var aNullableString: String? + var aNullableObject: Any? + var allNullableTypes: EventAllNullableTypes? + var list: [Any?]? + var stringList: [String?]? + var intList: [Int64?]? + var doubleList: [Double?]? + var boolList: [Bool?]? + var enumList: [EventEnum?]? + var objectList: [Any?]? + var listList: [[Any?]?]? + var mapList: [[AnyHashable?: Any?]?]? + var recursiveClassList: [EventAllNullableTypes?]? + var map: [AnyHashable?: Any?]? + var stringMap: [String?: String?]? + var intMap: [Int64?: Int64?]? + var enumMap: [EventEnum?: EventEnum?]? + var objectMap: [AnyHashable?: Any?]? + var listMap: [Int64?: [Any?]?]? + var mapMap: [Int64?: [AnyHashable?: Any?]?]? + var recursiveClassMap: [Int64?: EventAllNullableTypes?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EventAllNullableTypes? { + let aNullableBool: Bool? = nilOrValue(pigeonVar_list[0]) + let aNullableInt: Int64? = nilOrValue(pigeonVar_list[1]) + let aNullableInt64: Int64? = nilOrValue(pigeonVar_list[2]) + let aNullableDouble: Double? = nilOrValue(pigeonVar_list[3]) + let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[4]) + let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[5]) + let aNullable8ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[6]) + let aNullableFloatArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[7]) + let aNullableEnum: EventEnum? = nilOrValue(pigeonVar_list[8]) + let anotherNullableEnum: AnotherEventEnum? = nilOrValue(pigeonVar_list[9]) + let aNullableString: String? = nilOrValue(pigeonVar_list[10]) + let aNullableObject: Any? = pigeonVar_list[11] + let allNullableTypes: EventAllNullableTypes? = nilOrValue(pigeonVar_list[12]) + let list: [Any?]? = nilOrValue(pigeonVar_list[13]) + let stringList: [String?]? = nilOrValue(pigeonVar_list[14]) + let intList: [Int64?]? = nilOrValue(pigeonVar_list[15]) + let doubleList: [Double?]? = nilOrValue(pigeonVar_list[16]) + let boolList: [Bool?]? = nilOrValue(pigeonVar_list[17]) + let enumList: [EventEnum?]? = nilOrValue(pigeonVar_list[18]) + let objectList: [Any?]? = nilOrValue(pigeonVar_list[19]) + let listList: [[Any?]?]? = nilOrValue(pigeonVar_list[20]) + let mapList: [[AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[21]) + let recursiveClassList: [EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[22]) + let map: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[23]) + let stringMap: [String?: String?]? = nilOrValue(pigeonVar_list[24]) + let intMap: [Int64?: Int64?]? = nilOrValue(pigeonVar_list[25]) + let enumMap: [EventEnum?: EventEnum?]? = pigeonVar_list[26] as? [EventEnum?: EventEnum?] + let objectMap: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[27]) + let listMap: [Int64?: [Any?]?]? = nilOrValue(pigeonVar_list[28]) + let mapMap: [Int64?: [AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[29]) + let recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[30]) + + return EventAllNullableTypes( + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableInt64: aNullableInt64, + aNullableDouble: aNullableDouble, + aNullableByteArray: aNullableByteArray, + aNullable4ByteArray: aNullable4ByteArray, + aNullable8ByteArray: aNullable8ByteArray, + aNullableFloatArray: aNullableFloatArray, + aNullableEnum: aNullableEnum, + anotherNullableEnum: anotherNullableEnum, + aNullableString: aNullableString, + aNullableObject: aNullableObject, + allNullableTypes: allNullableTypes, + list: list, + stringList: stringList, + intList: intList, + doubleList: doubleList, + boolList: boolList, + enumList: enumList, + objectList: objectList, + listList: listList, + mapList: mapList, + recursiveClassList: recursiveClassList, + map: map, + stringMap: stringMap, + intMap: intMap, + enumMap: enumMap, + objectMap: objectMap, + listMap: listMap, + mapMap: mapMap, + recursiveClassMap: recursiveClassMap + ) + } + func toList() -> [Any?] { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +/// This protocol should not be extended by any user class outside of the generated file. +protocol PlatformEvent { + +} + +/// Generated class from Pigeon that represents data sent in messages. +struct IntEvent: PlatformEvent { + var value: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> IntEvent? { + let value = pigeonVar_list[0] as! Int64 + + return IntEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct StringEvent: PlatformEvent { + var value: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> StringEvent? { + let value = pigeonVar_list[0] as! String + + return StringEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct BoolEvent: PlatformEvent { + var value: Bool + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> BoolEvent? { + let value = pigeonVar_list[0] as! Bool + + return BoolEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DoubleEvent: PlatformEvent { + var value: Double + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DoubleEvent? { + let value = pigeonVar_list[0] as! Double + + return DoubleEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ObjectsEvent: PlatformEvent { + var value: Any + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ObjectsEvent? { + let value = pigeonVar_list[0]! + + return ObjectsEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct EnumEvent: PlatformEvent { + var value: EventEnum + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EnumEvent? { + let value = pigeonVar_list[0] as! EventEnum + + return EnumEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ClassEvent: PlatformEvent { + var value: EventAllNullableTypes + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ClassEvent? { + let value = pigeonVar_list[0] as! EventAllNullableTypes + + return ClassEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +private class EventChannelTestsPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return EventEnum(rawValue: enumResultAsInt) + } + return nil + case 130: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return AnotherEventEnum(rawValue: enumResultAsInt) + } + return nil + case 131: + return EventAllNullableTypes.fromList(self.readValue() as! [Any?]) + case 132: + return IntEvent.fromList(self.readValue() as! [Any?]) + case 133: + return StringEvent.fromList(self.readValue() as! [Any?]) + case 134: + return BoolEvent.fromList(self.readValue() as! [Any?]) + case 135: + return DoubleEvent.fromList(self.readValue() as! [Any?]) + case 136: + return ObjectsEvent.fromList(self.readValue() as! [Any?]) + case 137: + return EnumEvent.fromList(self.readValue() as! [Any?]) + case 138: + return ClassEvent.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class EventChannelTestsPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? EventEnum { + super.writeByte(129) + super.writeValue(value.rawValue) + } else if let value = value as? AnotherEventEnum { + super.writeByte(130) + super.writeValue(value.rawValue) + } else if let value = value as? EventAllNullableTypes { + super.writeByte(131) + super.writeValue(value.toList()) + } else if let value = value as? IntEvent { + super.writeByte(132) + super.writeValue(value.toList()) + } else if let value = value as? StringEvent { + super.writeByte(133) + super.writeValue(value.toList()) + } else if let value = value as? BoolEvent { + super.writeByte(134) + super.writeValue(value.toList()) + } else if let value = value as? DoubleEvent { + super.writeByte(135) + super.writeValue(value.toList()) + } else if let value = value as? ObjectsEvent { + super.writeByte(136) + super.writeValue(value.toList()) + } else if let value = value as? EnumEvent { + super.writeByte(137) + super.writeValue(value.toList()) + } else if let value = value as? ClassEvent { + super.writeByte(138) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class EventChannelTestsPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return EventChannelTestsPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return EventChannelTestsPigeonCodecWriter(data: data) + } +} + +class EventChannelTestsPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = EventChannelTestsPigeonCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) +} + +var eventChannelTestsPigeonMethodCodec = FlutterStandardMethodCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) + +private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } +} + +class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} +} + +class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + +} + +class StreamIntsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: StreamIntsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamInts" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(internalStreamHandler) + } +} + +class StreamEventsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: StreamEventsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamEvents" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(internalStreamHandler) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/ProxyApiTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/ProxyApiTests.gen.swift index d3a699ca47a9..506565c1dd67 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/ProxyApiTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/ProxyApiTests.gen.swift @@ -628,6 +628,15 @@ protocol PigeonApiDelegateProxyApiTestClass { nullableListParam: [Any?]?, nullableMapParam: [String?: Any?]?, nullableEnumParam: ProxyApiTestEnum?, nullableProxyApiParam: ProxyApiSuperClass? ) throws -> ProxyApiTestClass + func namedConstructor( + pigeonApi: PigeonApiProxyApiTestClass, aBool: Bool, anInt: Int64, aDouble: Double, + aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], aMap: [String?: Any?], + anEnum: ProxyApiTestEnum, aProxyApi: ProxyApiSuperClass, aNullableBool: Bool?, + aNullableInt: Int64?, aNullableDouble: Double?, aNullableString: String?, + aNullableUint8List: FlutterStandardTypedData?, aNullableList: [Any?]?, + aNullableMap: [String?: Any?]?, aNullableEnum: ProxyApiTestEnum?, + aNullableProxyApi: ProxyApiSuperClass? + ) throws -> ProxyApiTestClass func attachedField(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws -> ProxyApiSuperClass func staticAttachedField(pigeonApi: PigeonApiProxyApiTestClass) throws -> ProxyApiSuperClass @@ -1168,6 +1177,50 @@ final class PigeonApiProxyApiTestClass: PigeonApiProtocolProxyApiTestClass { } else { pigeonDefaultConstructorChannel.setMessageHandler(nil) } + let namedConstructorChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.namedConstructor", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + namedConstructorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pigeonIdentifierArg = args[0] as! Int64 + let aBoolArg = args[1] as! Bool + let anIntArg = args[2] as! Int64 + let aDoubleArg = args[3] as! Double + let aStringArg = args[4] as! String + let aUint8ListArg = args[5] as! FlutterStandardTypedData + let aListArg = args[6] as! [Any?] + let aMapArg = args[7] as! [String?: Any?] + let anEnumArg = args[8] as! ProxyApiTestEnum + let aProxyApiArg = args[9] as! ProxyApiSuperClass + let aNullableBoolArg: Bool? = nilOrValue(args[10]) + let aNullableIntArg: Int64? = nilOrValue(args[11]) + let aNullableDoubleArg: Double? = nilOrValue(args[12]) + let aNullableStringArg: String? = nilOrValue(args[13]) + let aNullableUint8ListArg: FlutterStandardTypedData? = nilOrValue(args[14]) + let aNullableListArg: [Any?]? = nilOrValue(args[15]) + let aNullableMapArg: [String?: Any?]? = nilOrValue(args[16]) + let aNullableEnumArg: ProxyApiTestEnum? = nilOrValue(args[17]) + let aNullableProxyApiArg: ProxyApiSuperClass? = nilOrValue(args[18]) + do { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + try api.pigeonDelegate.namedConstructor( + pigeonApi: api, aBool: aBoolArg, anInt: anIntArg, aDouble: aDoubleArg, + aString: aStringArg, aUint8List: aUint8ListArg, aList: aListArg, aMap: aMapArg, + anEnum: anEnumArg, aProxyApi: aProxyApiArg, aNullableBool: aNullableBoolArg, + aNullableInt: aNullableIntArg, aNullableDouble: aNullableDoubleArg, + aNullableString: aNullableStringArg, aNullableUint8List: aNullableUint8ListArg, + aNullableList: aNullableListArg, aNullableMap: aNullableMapArg, + aNullableEnum: aNullableEnumArg, aNullableProxyApi: aNullableProxyApiArg), + withIdentifier: pigeonIdentifierArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + namedConstructorChannel.setMessageHandler(nil) + } let attachedFieldChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.attachedField", binaryMessenger: binaryMessenger, codec: codec) diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index fb5c21dabf18..bcee6f7eb382 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -30,6 +30,9 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { proxyApiRegistrar = ProxyApiTestsPigeonProxyApiRegistrar( binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() + + StreamIntsStreamHandler.register(with: binaryMessenger, streamHandler: SendInts()) + StreamEventsStreamHandler.register(with: binaryMessenger, streamHandler: SendEvents()) } public func detachFromEngine(for registrar: FlutterPluginRegistrar) { @@ -1212,6 +1215,60 @@ public class TestPluginWithSuffix: HostSmallApi { } } +class SendInts: StreamIntsStreamHandler { + var timerActive = false + var timer: Timer? + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count: Int64 = 0 + if !timerActive { + timerActive = true + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + sink.success(count) + count += 1 + if count >= 5 { + sink.endOfStream() + self.timer?.invalidate() + } + } + } + } + } +} + +class SendEvents: StreamEventsStreamHandler { + var timerActive = false + var timer: Timer? + var eventList: [PlatformEvent] = + [ + IntEvent(value: 1), + StringEvent(value: "string"), + BoolEvent(value: false), + DoubleEvent(value: 3.14), + ObjectsEvent(value: true), + EnumEvent(value: EventEnum.fortyTwo), + ClassEvent(value: EventAllNullableTypes(aNullableInt: 0)), + ] + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count = 0 + if !timerActive { + timerActive = true + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + if count >= self.eventList.count { + sink.endOfStream() + self.timer?.invalidate() + } else { + sink.success(self.eventList[count]) + count += 1 + } + } + } + } + } +} class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func pigeonApiProxyApiTestClass(_ registrar: ProxyApiTestsPigeonProxyApiRegistrar) @@ -1237,6 +1294,18 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return ProxyApiTestClass() } + func namedConstructor( + pigeonApi: PigeonApiProxyApiTestClass, aBool: Bool, anInt: Int64, aDouble: Double, + aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], aMap: [String?: Any?], + anEnum: ProxyApiTestEnum, aProxyApi: ProxyApiSuperClass, aNullableBool: Bool?, + aNullableInt: Int64?, aNullableDouble: Double?, aNullableString: String?, + aNullableUint8List: FlutterStandardTypedData?, aNullableList: [Any?]?, + aNullableMap: [String?: Any?]?, aNullableEnum: ProxyApiTestEnum?, + aNullableProxyApi: ProxyApiSuperClass? + ) throws -> ProxyApiTestClass { + return ProxyApiTestClass() + } + func attachedField(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws -> ProxyApiSuperClass { diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 31b736278cee..a7b5056bac10 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 22.6.1 # This must match the version in lib/generator_tools.dart +version: 22.7.0 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.3.0 diff --git a/packages/pigeon/test/cpp_generator_test.dart b/packages/pigeon/test/cpp_generator_test.dart index 65a8f7ed8fdf..c88311f4c5be 100644 --- a/packages/pigeon/test/cpp_generator_test.dart +++ b/packages/pigeon/test/cpp_generator_test.dart @@ -249,23 +249,29 @@ void main() { }); test('Error field is private with public accessors', () { - final Root root = Root(apis: [ - AstHostApi(name: 'Api', methods: [ - Method( - name: 'doSomething', - location: ApiLocation.host, - parameters: [ - Parameter( - type: const TypeDeclaration( - baseName: 'int', - isNullable: false, - ), - name: 'someInput') - ], - returnType: const TypeDeclaration(baseName: 'int', isNullable: false), - ) - ]) - ], classes: [], enums: []); + final Root root = Root( + apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + parameters: [ + Parameter( + type: const TypeDeclaration( + baseName: 'int', + isNullable: false, + ), + name: 'someInput') + ], + returnType: + const TypeDeclaration(baseName: 'int', isNullable: false), + ) + ]) + ], + classes: [], + enums: [], + containsHostApi: true, + ); { final StringBuffer sink = StringBuffer(); const CppGenerator generator = CppGenerator(); @@ -2070,6 +2076,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const CppGenerator generator = CppGenerator(); diff --git a/packages/pigeon/test/dart/proxy_api_test.dart b/packages/pigeon/test/dart/proxy_api_test.dart index fd1d887189e3..6e3377af72bb 100644 --- a/packages/pigeon/test/dart/proxy_api_test.dart +++ b/packages/pigeon/test/dart/proxy_api_test.dart @@ -177,6 +177,62 @@ void main() { ); }); + group('ProxyApi base class', () { + test('class name', () { + final Root root = Root(apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [], + ) + ], classes: [], enums: []); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + + expect( + code, + contains(r'abstract class PigeonInternalProxyApiBaseClass'), + ); + }); + + test('InstanceManager field', () { + final Root root = Root(apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [], + ) + ], classes: [], enums: []); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + + expect( + collapsedCode, + contains( + '/// Maintains instances stored to communicate with native language objects. ' + 'final PigeonInstanceManager pigeon_instanceManager;', + ), + ); + }); + }); + group('inheritance', () { test('extends', () { final AstProxyApi api2 = AstProxyApi( diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart index 4dca4b416844..7a4d44c26177 100644 --- a/packages/pigeon/test/dart_generator_test.dart +++ b/packages/pigeon/test/dart_generator_test.dart @@ -667,7 +667,7 @@ void main() { expect(code, matches('pigeonVar_channel.send[(]null[)]')); }); - test('mock dart handler', () { + test('mock Dart handler', () { final Root root = Root(apis: [ AstHostApi(name: 'Api', dartHostTestHandler: 'ApiMock', methods: [ Method( @@ -1711,17 +1711,22 @@ name: foobar }); test('connection error contains channel name', () { - final Root root = Root(apis: [ - AstHostApi(name: 'Api', methods: [ - Method( - name: 'method', - location: ApiLocation.host, - parameters: [], - returnType: - const TypeDeclaration(baseName: 'Output', isNullable: false), - ) - ]) - ], classes: [], enums: []); + final Root root = Root( + apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'method', + location: ApiLocation.host, + parameters: [], + returnType: + const TypeDeclaration(baseName: 'Output', isNullable: false), + ) + ]) + ], + classes: [], + enums: [], + containsHostApi: true, + ); final StringBuffer sink = StringBuffer(); const DartGenerator generator = DartGenerator(); generator.generate( diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart index 63a6e8330eab..49358cf7ddaa 100644 --- a/packages/pigeon/test/java_generator_test.dart +++ b/packages/pigeon/test/java_generator_test.dart @@ -121,45 +121,50 @@ void main() { }); test('gen one host api', () { - final Root root = Root(apis: [ - AstHostApi(name: 'Api', methods: [ - Method( - name: 'doSomething', - location: ApiLocation.host, - parameters: [ - Parameter( - type: TypeDeclaration( - baseName: 'Input', - associatedClass: emptyClass, - isNullable: false, - ), - name: '') - ], - returnType: TypeDeclaration( - baseName: 'Output', - associatedClass: emptyClass, - isNullable: false, - ), - ) - ]) - ], classes: [ - Class(name: 'Input', fields: [ - NamedType( - type: const TypeDeclaration( - baseName: 'String', - isNullable: true, - ), - name: 'input') - ]), - Class(name: 'Output', fields: [ - NamedType( - type: const TypeDeclaration( - baseName: 'String', - isNullable: true, + final Root root = Root( + apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + parameters: [ + Parameter( + type: TypeDeclaration( + baseName: 'Input', + associatedClass: emptyClass, + isNullable: false, + ), + name: '') + ], + returnType: TypeDeclaration( + baseName: 'Output', + associatedClass: emptyClass, + isNullable: false, ), - name: 'output') - ]) - ], enums: []); + ) + ]) + ], + classes: [ + Class(name: 'Input', fields: [ + NamedType( + type: const TypeDeclaration( + baseName: 'String', + isNullable: true, + ), + name: 'input') + ]), + Class(name: 'Output', fields: [ + NamedType( + type: const TypeDeclaration( + baseName: 'String', + isNullable: true, + ), + name: 'output') + ]) + ], + enums: [], + containsHostApi: true, + ); final StringBuffer sink = StringBuffer(); const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaGenerator generator = JavaGenerator(); @@ -1565,6 +1570,7 @@ void main() { apis: [api], classes: [], enums: [], + containsHostApi: true, ); final StringBuffer sink = StringBuffer(); const JavaOptions javaOptions = JavaOptions(className: 'Messages'); @@ -1604,6 +1610,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const JavaGenerator generator = JavaGenerator(); diff --git a/packages/pigeon/test/kotlin_generator_test.dart b/packages/pigeon/test/kotlin_generator_test.dart index 549601b8851d..6043229fd729 100644 --- a/packages/pigeon/test/kotlin_generator_test.dart +++ b/packages/pigeon/test/kotlin_generator_test.dart @@ -1533,6 +1533,7 @@ void main() { apis: [api], classes: [], enums: [], + containsHostApi: true, ); final StringBuffer sink = StringBuffer(); const KotlinOptions kotlinOptions = @@ -1577,6 +1578,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const KotlinOptions kotlinOptions = KotlinOptions(); diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart index 734145e2c24b..728bb50ac9c7 100644 --- a/packages/pigeon/test/objc_generator_test.dart +++ b/packages/pigeon/test/objc_generator_test.dart @@ -2861,6 +2861,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const ObjcGenerator generator = ObjcGenerator(); diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart index 9e3fa213fc78..bf6fc7cbca16 100644 --- a/packages/pigeon/test/pigeon_lib_test.dart +++ b/packages/pigeon/test/pigeon_lib_test.dart @@ -232,6 +232,39 @@ abstract class Api { expect(results.errors[0].message, contains('dynamic')); }); + test('Only allow one api annotation', () { + const String source = ''' +@HostApi() +@FlutterApi() +abstract class Api { + int foo(); +} +'''; + final ParseResults results = parseSource(source); + expect(results.errors.length, 1); + expect( + results.errors[0].message, + contains( + 'API "Api" can only have one API annotation but contains: [@HostApi(), @FlutterApi()]')); + }); + + test('Only allow one api annotation plus @ConfigurePigeon', () { + const String source = ''' +@ConfigurePigeon(PigeonOptions( + dartOut: 'stdout', + javaOut: 'stdout', + dartOptions: DartOptions(), +)) +@HostApi() +abstract class Api { + void ping(); +} + +'''; + final ParseResults results = parseSource(source); + expect(results.errors.length, 0); + }); + test('enum in classes', () { const String code = ''' enum Enum1 { @@ -1567,4 +1600,69 @@ abstract class MyClass { ); }); }); + + group('event channel validation', () { + test('methods cannot contain parameters', () { + const String code = ''' +@EventChannelApi() +abstract class EventChannelApi { + int streamInts(int event); +} +'''; + final ParseResults parseResult = parseSource(code); + expect(parseResult.errors.length, equals(1)); + expect( + parseResult.errors.single.message, + contains( + 'event channel methods must not be contain parameters, in method "streamInts" in API: "EventChannelApi"'), + ); + }); + }); + + group('sealed inheritance validation', () { + test('super class must be sealed', () { + const String code = ''' +class DataClass {} +class ChildClass extends DataClass { + ChildClass(this.input); + int input; +} + +@EventChannelApi() +abstract class events { + void aMethod(ChildClass param); +} +'''; + final ParseResults parseResult = parseSource(code); + expect(parseResult.errors, isNotEmpty); + expect( + parseResult.errors[0].message, + contains('Child class: "ChildClass" must extend a sealed class.'), + ); + }); + + test('super class must be sealed', () { + const String code = ''' +sealed class DataClass { + DataClass(this.input); + int input; +} +class ChildClass extends DataClass { + ChildClass(this.input); + int input; +} + +@EventChannelApi() +abstract class events { + void aMethod(ChildClass param); +} +'''; + final ParseResults parseResult = parseSource(code); + expect(parseResult.errors, isNotEmpty); + expect( + parseResult.errors[0].message, + contains('Sealed class: "DataClass" must not contain fields.'), + ); + }); + }); } diff --git a/packages/pigeon/test/swift/proxy_api_test.dart b/packages/pigeon/test/swift/proxy_api_test.dart index 18df858f81e9..3c9cb12c7562 100644 --- a/packages/pigeon/test/swift/proxy_api_test.dart +++ b/packages/pigeon/test/swift/proxy_api_test.dart @@ -355,6 +355,43 @@ void main() { ); }); + test('named constructor', () { + final Root root = Root( + apis: [ + AstProxyApi(name: 'Api', constructors: [ + Constructor( + name: 'myConstructorName', + parameters: [], + ) + ], fields: [], methods: []), + ], + classes: [], + enums: [], + ); + final StringBuffer sink = StringBuffer(); + const SwiftGenerator generator = SwiftGenerator(); + generator.generate( + const SwiftOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect( + collapsedCode, + contains( + 'func myConstructorName(pigeonApi: PigeonApiApi) throws -> Api', + ), + ); + expect( + collapsedCode, + contains( + r'let myConstructorNameChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.test_package.Api.myConstructorName", binaryMessenger: binaryMessenger, codec: codec)', + ), + ); + }); + test('multiple params constructor', () { final Enum anEnum = Enum( name: 'AnEnum', diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart index b9f7de5d356a..6ad36f934ecc 100644 --- a/packages/pigeon/test/swift_generator_test.dart +++ b/packages/pigeon/test/swift_generator_test.dart @@ -1508,6 +1508,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const SwiftOptions kotlinOptions = SwiftOptions(); diff --git a/packages/pigeon/tool/run_tests.dart b/packages/pigeon/tool/run_tests.dart index e380efdd14b9..28229972834f 100644 --- a/packages/pigeon/tool/run_tests.dart +++ b/packages/pigeon/tool/run_tests.dart @@ -87,7 +87,14 @@ Future _validateGeneratedFiles( print('Validating generated files:'); print(' $generationMessage...'); - final int generateExitCode = await generateExamplePigeons(); + int generateExitCode = await generateExamplePigeons(); + + if (generateExitCode != 0) { + print('Generation failed; see above for errors.'); + exit(generateExitCode); + } + + generateExitCode = await generateTestPigeons(baseDir: baseDir); if (generateExitCode != 0) { print('Generation failed; see above for errors.'); diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 4cee0d4de568..fdb503843665 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -23,7 +23,20 @@ enum GeneratorLanguage { // A map of pigeons/ files to the languages that they can't yet be generated // for due to limitations of that generator. const Map> _unsupportedFiles = - >{}; + >{ + 'event_channel_tests': { + GeneratorLanguage.cpp, + GeneratorLanguage.gobject, + GeneratorLanguage.java, + GeneratorLanguage.objc, + }, + 'proxy_api_tests': { + GeneratorLanguage.cpp, + GeneratorLanguage.gobject, + GeneratorLanguage.java, + GeneratorLanguage.objc, + }, +}; String _snakeToPascalCase(String snake) { final List parts = snake.split('_'); @@ -48,21 +61,29 @@ String _javaFilenameForName(String inputName) { } Future generateExamplePigeons() async { - return runPigeon( + int success = 0; + success = await runPigeon( input: './example/app/pigeons/messages.dart', basePath: './example/app', suppressVersion: true, ); + success += await runPigeon( + input: './example/app/pigeons/event_channel_messages.dart', + basePath: './example/app', + suppressVersion: true, + ); + return success; } Future generateTestPigeons( {required String baseDir, bool includeOverflow = false}) async { // TODO(stuartmorgan): Make this dynamic rather than hard-coded. Or eliminate // it entirely; see https://github.com/flutter/flutter/issues/115169. - const List inputs = [ + const Set inputs = { 'background_platform_channels', 'core_tests', 'enum', + 'event_channel_tests', 'flutter_unittests', // Only for Dart unit tests in shared_test_plugin_code 'message', 'multiple_arity', @@ -71,7 +92,7 @@ Future generateTestPigeons( 'nullable_returns', 'primitive', 'proxy_api_tests', - ]; + }; final String outputBase = p.join(baseDir, 'platform_tests', 'test_plugin'); final String alternateOutputBase = @@ -118,7 +139,7 @@ Future generateTestPigeons( ? null : '$outputBase/ios/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'core_tests', + swiftIncludeErrorClass: input != 'background_platform_channels', // Linux gobjectHeaderOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null @@ -150,7 +171,7 @@ Future generateTestPigeons( ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'core_tests', + swiftIncludeErrorClass: input != 'background_platform_channels', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', injectOverflowTypes: includeOverflow && input == 'core_tests', diff --git a/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/project.pbxproj b/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/project.pbxproj index ef327c5ebb3a..f094be1b0316 100644 --- a/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -56,6 +57,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, F7B3FF2A19BF60DCE2927CCE /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -141,13 +143,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - D271A2AAEBA84769C4A17920 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -159,7 +163,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -177,6 +181,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -254,23 +261,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - D271A2AAEBA84769C4A17920 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -554,6 +544,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a09bea5..d795332e1b7b 100644 --- a/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner/AppDelegate.swift b/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner/AppDelegate.swift index 8891e986b166..2ed87872b128 100644 --- a/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner/AppDelegate.swift +++ b/packages/pointer_interceptor/pointer_interceptor/example/ios/Runner/AppDelegate.swift @@ -5,7 +5,7 @@ import Flutter import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/packages/pointer_interceptor/pointer_interceptor_ios/example/.gitignore b/packages/pointer_interceptor/pointer_interceptor_ios/example/.gitignore index 29a3a5017f04..79c113f9b501 100644 --- a/packages/pointer_interceptor/pointer_interceptor_ios/example/.gitignore +++ b/packages/pointer_interceptor/pointer_interceptor_ios/example/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/project.pbxproj index d7a9dc44fba2..a2b81eb7750f 100644 --- a/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 46643191504C316CD4ABDB75 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7324BF52939888500E1D0F3 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -93,6 +94,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, E168ED82D399C1A9329D0876 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -237,13 +239,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - EBA0739E77CF3B079516C7F7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -274,7 +278,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -300,6 +304,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -415,23 +422,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - EBA0739E77CF3B079516C7F7 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -870,6 +860,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 72a43df55cc6..a11820ee194d 100644 --- a/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner/AppDelegate.swift b/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner/AppDelegate.swift index 8891e986b166..2ed87872b128 100644 --- a/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner/AppDelegate.swift +++ b/packages/pointer_interceptor/pointer_interceptor_ios/example/ios/Runner/AppDelegate.swift @@ -5,7 +5,7 @@ import Flutter import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/packages/quick_actions/quick_actions/example/android/app/build.gradle b/packages/quick_actions/quick_actions/example/android/app/build.gradle index 0aab20bd886e..c0fe4a5dc7cd 100644 --- a/packages/quick_actions/quick_actions/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'io.flutter.plugins.quickactionsexample' compileSdk flutter.compileSdkVersion @@ -32,7 +30,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.quickactionsexample" minSdkVersion flutter.minSdkVersion - targetSdkVersion 28 + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/quick_actions/quick_actions/example/android/app/src/main/AndroidManifest.xml b/packages/quick_actions/quick_actions/example/android/app/src/main/AndroidManifest.xml index 20969735d866..06a0b6798912 100644 --- a/packages/quick_actions/quick_actions/example/android/app/src/main/AndroidManifest.xml +++ b/packages/quick_actions/quick_actions/example/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize"> diff --git a/packages/quick_actions/quick_actions/example/android/build.gradle b/packages/quick_actions/quick_actions/example/android/build.gradle index 0bed8906c094..b9db5700753a 100644 --- a/packages/quick_actions/quick_actions/example/android/build.gradle +++ b/packages/quick_actions/quick_actions/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.2' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/quick_actions/quick_actions/example/android/settings.gradle b/packages/quick_actions/quick_actions/example/android/settings.gradle index e54a7e1fd6e5..0667903d5724 100644 --- a/packages/quick_actions/quick_actions/example/android/settings.gradle +++ b/packages/quick_actions/quick_actions/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.2" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj index a8c21426bac6..beec8b609599 100644 --- a/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 50EB54C1FE43DB743F5DEC7C /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D1A69703A518C37D73BF8B91 /* libPods-RunnerTests.a */; }; 686BE83025E58CCF00862533 /* RunnerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686BE82F25E58CCF00862533 /* RunnerUITests.m */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -99,6 +100,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -253,13 +255,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 1F1D0B027AB03A798EA38824 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -270,7 +274,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 33E20B3126EFCDFC00A4A191 = { @@ -296,6 +300,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -336,24 +343,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1F1D0B027AB03A798EA38824 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/quick_actions_ios/quick_actions_ios_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/quick_actions_ios_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -770,6 +759,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 2810c229f3a0..8fee1e557bc1 100644 --- a/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/quick_actions/quick_actions/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index db8734cdfb5a..ac95e9ad4ac6 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - def androidXTestVersion = '1.2.0' android { diff --git a/packages/quick_actions/quick_actions_android/example/android/build.gradle b/packages/quick_actions/quick_actions_android/example/android/build.gradle index 08062b9baa32..62ed70de53f4 100644 --- a/packages/quick_actions/quick_actions_android/example/android/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.5.1' - } -} - allprojects { repositories { // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. diff --git a/packages/quick_actions/quick_actions_android/example/android/settings.gradle b/packages/quick_actions/quick_actions_android/example/android/settings.gradle index e54a7e1fd6e5..078698fe752d 100644 --- a/packages/quick_actions/quick_actions_android/example/android/settings.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/settings.gradle @@ -1,28 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } // See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.5.1" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" } -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" + +include ":app" diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj index 907b664f5884..9c01c467f43b 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 2632072169FF635893D8EB4D /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 6A841C2B6AED5CF8DB2A1894 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C35AD3650AB6BF850E016715 /* libPods-Runner.a */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -101,6 +102,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 6A841C2B6AED5CF8DB2A1894 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -264,13 +266,15 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 2C9700E785D8786FD21B19DB /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -281,7 +285,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 33E20B3126EFCDFC00A4A191 = { @@ -309,6 +313,9 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; @@ -349,24 +356,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 2C9700E785D8786FD21B19DB /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/quick_actions_ios/quick_actions_ios_privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/quick_actions_ios_privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -794,6 +783,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 2810c229f3a0..8fee1e557bc1 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md index 05f06428ea92..941bed1ba610 100644 --- a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.1.0 +* Adds localizedSubtitle field for iOS quick actions. * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 1.0.6 diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart index 0f936db870c7..2d5b6874344b 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart @@ -45,6 +45,8 @@ class MethodChannelQuickActions extends QuickActionsPlatform { return { 'type': item.type, 'localizedTitle': item.localizedTitle, + if (item.localizedSubtitle != null) + 'localizedSubtitle': item.localizedSubtitle, 'icon': item.icon, }; } diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart b/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart index 1d84e16ac996..81679ddb6c93 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/types/shortcut_item.dart @@ -11,6 +11,7 @@ class ShortcutItem { const ShortcutItem({ required this.type, required this.localizedTitle, + this.localizedSubtitle, this.icon, }); @@ -20,6 +21,11 @@ class ShortcutItem { /// Localized title of the item. final String localizedTitle; + /// Localized subtitle of the item. + /// + /// May be ignored on platforms that don't support localized subtitles. + final String? localizedSubtitle; + /// Name of native resource (xcassets etc; NOT a Flutter asset) to be /// displayed as the icon for this item. final String? icon; diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index c9c05c64702f..11ff5823bba1 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.6 +version: 1.1.0 environment: sdk: ^3.3.0 diff --git a/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart b/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart index 41e2ed4e7244..0005fda52285 100644 --- a/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart +++ b/packages/quick_actions/quick_actions_platform_interface/test/method_channel_quick_actions_test.dart @@ -61,7 +61,38 @@ void main() { quickActions.initialize((String type) {}); quickActions.setShortcutItems([ const ShortcutItem( - type: 'test', localizedTitle: 'title', icon: 'icon.svg') + type: 'test', + localizedTitle: 'title', + localizedSubtitle: 'subtitle', + icon: 'icon.svg', + ) + ]); + + expect( + log, + [ + isMethodCall('getLaunchAction', arguments: null), + isMethodCall('setShortcutItems', arguments: >[ + { + 'type': 'test', + 'localizedTitle': 'title', + 'localizedSubtitle': 'subtitle', + 'icon': 'icon.svg', + } + ]), + ], + ); + }); + + test('passes shortcutItem through channel with null localizedSubtitle', + () { + quickActions.initialize((String type) {}); + quickActions.setShortcutItems([ + const ShortcutItem( + type: 'test', + localizedTitle: 'title', + icon: 'icon.svg', + ) ]); expect( @@ -82,10 +113,15 @@ void main() { test('setShortcutItems with demo data', () async { const String type = 'type'; const String localizedTitle = 'localizedTitle'; + const String localizedSubtitle = 'localizedSubtitle'; const String icon = 'icon'; await quickActions.setShortcutItems( const [ - ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon) + ShortcutItem( + type: type, + localizedTitle: localizedTitle, + localizedSubtitle: localizedSubtitle, + icon: icon) ], ); expect( @@ -97,6 +133,7 @@ void main() { { 'type': type, 'localizedTitle': localizedTitle, + 'localizedSubtitle': localizedSubtitle, 'icon': icon, } ], @@ -138,13 +175,19 @@ void main() { test('Shortcut item can be constructed', () { const String type = 'type'; const String localizedTitle = 'title'; + const String localizedSubtitle = 'subtitle'; const String icon = 'foo'; - const ShortcutItem item = - ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon); + const ShortcutItem item = ShortcutItem( + type: type, + localizedTitle: localizedTitle, + localizedSubtitle: localizedSubtitle, + icon: icon, + ); expect(item.type, type); expect(item.localizedTitle, localizedTitle); + expect(item.localizedSubtitle, localizedSubtitle); expect(item.icon, icon); }); }); diff --git a/packages/rfw/example/hello/android/app/build.gradle b/packages/rfw/example/hello/android/app/build.gradle index 5f291d55b6b2..1132f6671c9a 100644 --- a/packages/rfw/example/hello/android/app/build.gradle +++ b/packages/rfw/example/hello/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace 'dev.flutter.rfw.examples.hello' compileSdk flutter.compileSdkVersion @@ -46,7 +43,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "dev.flutter.rfw.examples.hello" minSdkVersion flutter.minSdkVersion - targetSdkVersion 30 + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } @@ -63,7 +60,3 @@ android { flutter { source '../..' } - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/packages/rfw/example/hello/android/app/src/main/AndroidManifest.xml b/packages/rfw/example/hello/android/app/src/main/AndroidManifest.xml index a528520e011d..abb77e597531 100644 --- a/packages/rfw/example/hello/android/app/src/main/AndroidManifest.xml +++ b/packages/rfw/example/hello/android/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" + android:exported="true" android:windowSoftInputMode="adjustResize">