diff --git a/.agent/rules/flutter.md b/.agent/rules/flutter.md new file mode 100644 index 000000000000..2f36c8befc58 --- /dev/null +++ b/.agent/rules/flutter.md @@ -0,0 +1,173 @@ +# AI Rules for Flutter + +You are an expert Flutter and Dart developer. Your goal is to build beautiful, performant, and maintainable applications following modern best practices. + +## Interaction Guidelines +* **User Persona:** Assume the user is familiar with programming concepts but may be new to Dart. +* **Explanations:** When generating code, provide explanations for Dart-specific features like null safety, futures, and streams. +* **Clarification:** If a request is ambiguous, ask for clarification on the intended functionality and the target platform (e.g., command-line, web, server). +* **Dependencies:** When suggesting new dependencies from `pub.dev`, explain their benefits. Use `pub_dev_search` if available. +* **Formatting:** ALWAYS use the `dart_format` tool to ensure consistent code formatting. +* **Fixes:** Use the `dart_fix` tool to automatically fix many common errors. +* **Linting:** Use the Dart linter with `flutter_lints` to catch common issues. + +## Flutter Style Guide +* **SOLID Principles:** Apply SOLID principles throughout the codebase. +* **Concise and Declarative:** Write concise, modern, technical Dart code. Prefer functional and declarative patterns. +* **Composition over Inheritance:** Favor composition for building complex widgets and logic. +* **Immutability:** Prefer immutable data structures. Widgets (especially `StatelessWidget`) should be immutable. +* **State Management:** Separate ephemeral state and app state. Use a state management solution for app state. +* **Widgets are for UI:** Everything in Flutter's UI is a widget. Compose complex UIs from smaller, reusable widgets. + +## Package Management +* **Pub Tool:** Use `pub` or `flutter pub add`. +* **Dev Dependencies:** Use `flutter pub add dev:`. +* **Overrides:** Use `flutter pub add override::`. +* **Removal:** `dart pub remove `. + +## Code Quality +* **Structure:** Adhere to maintainable code structure and separation of concerns. +* **Naming:** Avoid abbreviations. Use `PascalCase` (classes), `camelCase` (members), `snake_case` (files). +* **Conciseness:** Functions should be short (<20 lines) and single-purpose. +* **Error Handling:** Anticipate and handle potential errors. Don't let code fail silently. +* **Logging:** Use `dart:developer` `log` instead of `print`. + +## Dart Best Practices +* **Effective Dart:** Follow official guidelines. +* **Async/Await:** Use `Future`, `async`, `await` for operations. Use `Stream` for events. +* **Null Safety:** Write sound null-safe code. Avoid `!` operator unless guaranteed. +* **Pattern Matching:** Use switch expressions and pattern matching. +* **Records:** Use records for multiple return values. +* **Exception Handling:** Use custom exceptions for specific situations. +* **Arrow Functions:** Use `=>` for one-line functions. + +## Flutter Best Practices +* **Immutability:** Widgets are immutable. Rebuild, don't mutate. +* **Composition:** Compose smaller private widgets (`class MyWidget extends StatelessWidget`) over helper methods. +* **Lists:** Use `ListView.builder` or `SliverList` for performance. +* **Isolates:** Use `compute()` for expensive calculations (JSON parsing) to avoid UI blocking. +* **Const:** Use `const` constructors everywhere possible to reduce rebuilds. +* **Build Methods:** Avoid expensive ops (network) in `build()`. + +## State Management +* **Native-First:** Prefer `ValueNotifier`, `ChangeNotifier`, `ListenableBuilder`. +* **Restrictions:** Do NOT use Riverpod, Bloc, or GetX unless explicitly requested. +* **ChangeNotifier:** For state that is more complex or shared across multiple widgets, use `ChangeNotifier`. +* **MVVM:** When a more robust solution is needed, structure the app using the Model-View-ViewModel (MVVM) pattern. +* **Dependency Injection:** Use simple manual constructor dependency injection to make a class's dependencies explicit in its API, and to manage dependencies between different layers of the application. + +```dart +// Simple Local State +final ValueNotifier _counter = ValueNotifier(0); +ValueListenableBuilder( + valueListenable: _counter, + builder: (context, value, child) => Text('Count: $value'), +); +``` + +## Routing (GoRouter) +Use `go_router` for all navigation needs (deep linking, web). Ensure users are redirected to login when unauthorized. + +```dart +final GoRouter _router = GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (context, state) => const HomeScreen(), + routes: [ + GoRoute( + path: 'details/:id', + builder: (context, state) { + final String id = state.pathParameters['id']!; + return DetailScreen(id: id); + }, + ), + ], + ), + ], +); +MaterialApp.router(routerConfig: _router); +``` + +## Data Handling & Serialization +* **JSON:** Use `json_serializable` and `json_annotation`. +* **Naming:** Use `fieldRename: FieldRename.snake` for consistency. + +```dart +@JsonSerializable(fieldRename: FieldRename.snake) +class User { + final String firstName; + final String lastName; + User({required this.firstName, required this.lastName}); + factory User.fromJson(Map json) => _$UserFromJson(json); +} +``` + +## Visual Design & Theming (Material 3) +* **Visual Design:** Build beautiful and intuitive user interfaces that follow modern design guidelines. +* **Typography:** Stress and emphasize font sizes to ease understanding, e.g., hero text, section headlines. +* **Background:** Apply subtle noise texture to the main background to add a premium, tactile feel. +* **Shadows:** Multi-layered drop shadows create a strong sense of depth; cards have a soft, deep shadow to look "lifted." +* **Icons:** Incorporate icons to enhance the user’s understanding and the logical navigation of the app. +* **Interactive Elements:** Buttons, checkboxes, sliders, lists, charts, graphs, and other interactive elements have a shadow with elegant use of color to create a "glow" effect. +* **Centralized Theme:** Define a centralized `ThemeData` object to ensure a consistent application-wide style. +* **Light and Dark Themes:** Implement support for both light and dark themes using `theme` and `darkTheme`. +* **Color Scheme Generation:** Generate harmonious color palettes from a single color using `ColorScheme.fromSeed`. + +```dart +final ThemeData lightTheme = ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.deepPurple, + brightness: Brightness.light, + ), + textTheme: GoogleFonts.outfitTextTheme(), +); +``` + +## Layout Best Practices +* **Expanded:** Use to make a child widget fill the remaining available space along the main axis. +* **Flexible:** Use when you want a widget to shrink to fit, but not necessarily grow. Don't combine `Flexible` and `Expanded` in the same `Row` or `Column`. +* **Wrap:** Use when you have a series of widgets that would overflow a `Row` or `Column`, and you want them to move to the next line. +* **SingleChildScrollView:** Use when your content is intrinsically larger than the viewport, but is a fixed size. +* **ListView / GridView:** For long lists or grids of content, always use a builder constructor (`.builder`). +* **FittedBox:** Use to scale or fit a single child widget within its parent. +* **LayoutBuilder:** Use for complex, responsive layouts to make decisions based on the available space. +* **Positioned:** Use to precisely place a child within a `Stack` by anchoring it to the edges. +* **OverlayPortal:** Use to show UI elements (like custom dropdowns or tooltips) "on top" of everything else. + +```dart +// Network Image with Error Handler +Image.network( + 'https://example.com/img.png', + errorBuilder: (ctx, err, stack) => const Icon(Icons.error), + loadingBuilder: (ctx, child, prog) => prog == null ? child : const CircularProgressIndicator(), +); +``` + +## Documentation Philosophy +* **Comment wisely:** Use comments to explain why the code is written a certain way, not what the code does. The code itself should be self-explanatory. +* **Document for the user:** Write documentation with the reader in mind. If you had a question and found the answer, add it to the documentation where you first looked. +* **No useless documentation:** If the documentation only restates the obvious from the code's name, it's not helpful. +* **Consistency is key:** Use consistent terminology throughout your documentation. +* **Use `///` for doc comments:** This allows documentation generation tools to pick them up. +* **Start with a single-sentence summary:** The first sentence should be a concise, user-centric summary ending with a period. +* **Avoid redundancy:** Don't repeat information that's obvious from the code's context, like the class name or signature. +* **Public APIs are a priority:** Always document public APIs. + +## Accessibility +* **Contrast:** Ensure text has a contrast ratio of at least **4.5:1** against its background. +* **Dynamic Text Scaling:** Test your UI to ensure it remains usable when users increase the system font size. +* **Semantic Labels:** Use the `Semantics` widget to provide clear, descriptive labels for UI elements. +* **Screen Reader Testing:** Regularly test your app with TalkBack (Android) and VoiceOver (iOS). + +## Analysis Options +Strictly follow `flutter_lints`. + +```yaml +include: package:flutter_lints/flutter.yaml +linter: + rules: + avoid_print: true + prefer_single_quotes: true + always_use_package_imports: true +``` diff --git a/.gemini/styleguide.md b/.gemini/styleguide.md new file mode 100644 index 000000000000..fda88ab0528b --- /dev/null +++ b/.gemini/styleguide.md @@ -0,0 +1,80 @@ +# AI Rules for Flutter + +## Persona & Tools +* **Role:** Expert Flutter Developer. Focus: Beautiful, performant, maintainable code. +* **Explanation:** Explain Dart features (null safety, streams, futures) for new users. +* **Tools:** ALWAYS run `dart_format`. Use `dart_fix` for cleanups. Use `analyze_files` with `flutter_lints` to catch errors early. +* **Dependencies:** Add with `flutter pub add`. Use `pub_dev_search` for discovery. Explain why a package is needed. + +## Architecture & Structure +* **Entry:** Standard `lib/main.dart`. +* **Layers:** Presentation (Widgets), Domain (Logic), Data (Repo/API). +* **Features:** Group by feature (e.g., `lib/features/login/`) for scalable apps. +* **SOLID:** Strictly enforced. +* **State Management:** + * **Pattern:** Separate UI state (ephemeral) from App state. + * **Native First:** Use `ValueNotifier`, `ChangeNotifier`. + * **Prohibited:** NO Riverpod, Bloc, GetX unless explicitly requested. + * **DI:** Manual constructor injection or `provider` package if requested. + +## Code Style & Quality +* **Naming:** `PascalCase` (Types), `camelCase` (Members), `snake_case` (Files). +* **Conciseness:** Functions <20 lines. Avoid verbosity. +* **Null Safety:** NO `!` operator. Use `?` and flow analysis (e.g. `if (x != null)`). +* **Async:** Use `async/await` for Futures. Catch all errors with `try-catch`. +* **Logging:** Use `dart:developer` `log()` locally. NEVER use `print`. + +## Flutter Best Practices +* **Build Methods:** Keep pure and fast. No side effects. No network calls. +* **Isolates:** Use `compute()` for heavy tasks like JSON parsing. +* **Lists:** `ListView.builder` or `SliverList` for performance. +* **Immutability:** `const` constructors everywhere validation. `StatelessWidget` preference. +* **Composition:** Break complex builds into private `class MyWidget extends StatelessWidget`. + +## Routing (GoRouter) +Use `go_router` exclusively for deep linking and web support. + +```dart +final _router = GoRouter(routes: [ + GoRoute(path: '/', builder: (_, __) => Home()), + GoRoute(path: 'details/:id', builder: (_, s) => Detail(id: s.pathParameters['id']!)), +]); +MaterialApp.router(routerConfig: _router); +``` + +## Data (JSON) +Use `json_serializable` with `fieldRename: FieldRename.snake`. + +```dart +@JsonSerializable(fieldRename: FieldRename.snake) +class User { + final String name; + User({required this.name}); + factory User.fromJson(Map json) => _$UserFromJson(json); +} +``` + +## Visual Design (Material 3) +* **Aesthetics:** Premium, custom look. "Wow" the user. Avoid default blue. +* **Theme:** Use `ThemeData` with `ColorScheme.fromSeed`. +* **Modes:** Support Light & Dark modes (`ThemeMode.system`). +* **Typography:** `google_fonts`. Define a consistent Type Scale. +* **Layout:** `LayoutBuilder` for responsiveness. `OverlayPortal` for popups. +* **Components:** Use `ThemeExtension` for custom tokens (colors/sizes). + +## Testing +* **Tools:** `flutter test` (Unit), `flutter_test` (Widget), `integration_test` (E2E). +* **Mocks:** Prefer Fakes. Use `mockito` sparingly. +* **Pattern:** Arrange-Act-Assert. +* **Assertions:** Use `package:checks`. + +## Accessibility (A11Y) +* **Contrast:** 4.5:1 minimum for text. +* **Semantics:** Label all interactive elements specifically. +* **Scale:** Test dynamic font sizes (up to 200%). +* **Screen Readers:** Verify with TalkBack/VoiceOver. + +## Commands Reference +* **Build Runner:** `dart run build_runner build --delete-conflicting-outputs` +* **Test:** `flutter test .` +* **Analyze:** `flutter analyze .` diff --git a/.github/workflows/all_plugins.yaml b/.github/workflows/all_plugins.yaml index ac5acce22e61..629ce3dcb758 100644 --- a/.github/workflows/all_plugins.yaml +++ b/.github/workflows/all_plugins.yaml @@ -20,21 +20,24 @@ on: jobs: analyze: - timeout-minutes: 45 + timeout-minutes: 50 runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: melos-version: '5.3.0' - name: 'Run Analyze' run: melos analyze-ci + - name: 'Validate Workspace' + if: always() + run: melos run validate:workspace # Separated from "analyse" action as pubspec_override file is not being taken into account when running `flutter pub publish --dry-run` # This will fail on CI until this is fixed: https://github.com/invertase/melos/issues/467 @@ -43,14 +46,14 @@ jobs: timeout-minutes: 30 runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: melos-version: '5.3.0' - name: 'Pub Check' @@ -62,14 +65,14 @@ jobs: timeout-minutes: 30 runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: melos-version: '5.3.0' - name: 'Flutter Pub Get' @@ -80,16 +83,18 @@ jobs: format: # switch back to ubuntu-latest when swiftformat is working again runs-on: macos-latest - timeout-minutes: 20 + timeout-minutes: 40 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + with: + fetch-depth: 0 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: melos-version: '5.3.0' - uses: Homebrew/actions/setup-homebrew@master @@ -104,28 +109,31 @@ jobs: run: | clang-format --version swiftformat --version - - name: 'Dart, Java and Objective-C ' + - name: 'Dart, Java, Objective-C and Swift' run: | - flutter pub global run flutter_plugin_tools format + flutter pub global run flutter_plugin_tools format --base-branch=origin/main ./.github/workflows/scripts/validate-formatting.sh - - name: 'Swift' - if: ${{ success() || failure() }} + + # Clean generated build artificats(from format step) to prevent post run jobs from timing out + - name: Clean generated build artifacts + if: ${{ always() }} run: | - swiftformat . - ./.github/workflows/scripts/validate-formatting.sh + rm -rf build + find packages -type d -name build -prune -exec rm -rf {} + build_examples_dart: timeout-minutes: 30 runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' + flutter-version: '3.41.9' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: melos-version: '5.3.0' - name: 'Build Examples' @@ -136,27 +144,28 @@ jobs: runs-on: macos-15 timeout-minutes: 45 env: - FLUTTER_DEPENDENCIES: "cloud_firestore firebase_remote_config cloud_functions firebase_database firebase_auth firebase_storage firebase_analytics firebase_messaging firebase_app_check firebase_in_app_messaging firebase_performance firebase_crashlytics firebase_ml_model_downloader firebase_app_installations" + FLUTTER_DEPENDENCIES: "cloud_firestore firebase_remote_config cloud_functions firebase_database firebase_auth firebase_storage firebase_analytics firebase_messaging firebase_app_check firebase_in_app_messaging firebase_performance firebase_crashlytics firebase_ml_model_downloader firebase_app_installations firebase_ai" PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' + flutter-version: '3.41.9' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - name: Xcode run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 + with: + melos-version: '5.3.0' - name: Setup firebase_core example app to test Swift integration - # run this before running melos boostrap to ensure the example app is set up + # Run after melos bootstrap so workspace pubspec_overrides resolve unpublished package versions. run: | cd packages/firebase_core/firebase_core/example flutter pub add $FLUTTER_DEPENDENCIES cd ../../../.. - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa - with: - melos-version: '5.3.0' - name: 'Swift Integration Setup' run: flutter config --enable-swift-package-manager - name: 'Build Apps with Swift Package Manager' @@ -166,14 +175,14 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: melos-version: '5.3.0' - name: 'Flutter Test' @@ -185,17 +194,21 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: go-version: '^1.13.1' # Go is used by addlicense command (addlicense is used in melos run # check-license-header) - run: go install github.com/google/addlicense@latest - - name: Install Dart - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 + with: + channel: 'stable' + cache: true + cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" + pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - name: Install Melos - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: # Running `melos bootstrap` is not needed because we use Melos just # for the `check-license-header` script. diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index 54bb34693732..dc00cfb53eca 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -1,7 +1,7 @@ name: e2e-android concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-android cancel-in-progress: true on: @@ -23,27 +23,34 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: + inputs: + nightly_test_mode: + type: boolean + default: false jobs: android: runs-on: ubuntu-latest - timeout-minutes: 45 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 45 }} strategy: fail-fast: false matrix: working_directory: ['tests', 'packages/cloud_firestore/cloud_firestore/example'] + exclude: + - working_directory: ${{ inputs.nightly_test_mode && 'packages/cloud_firestore/cloud_firestore/example' || 'none' }} env: AVD_ARCH: x86_64 AVD_API_LEVEL: 34 AVD_TARGET: google_apis steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' @@ -53,7 +60,7 @@ jobs: echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -62,13 +69,13 @@ jobs: path: ~/.cache/firebase/emulators key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} restore-keys: firebase-emulators-v3 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -82,7 +89,7 @@ jobs: sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - name: Gradle cache - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v6 - name: Free Disk Space (Ubuntu) uses: AdityaGarg8/remove-unwanted-software@90e01b21170618765a73370fcc3abbd1684a7793 with: @@ -91,18 +98,33 @@ jobs: remove-codeql: true remove-docker-images: true remove-large-packages: true + - name: Prepare AVD home on /mnt + # GitHub-hosted runners mount a ~74GB volume at /mnt. Create it before AVD cache + # restore and android-emulator-runner (avdmanager needs the space at create time). + run: | + sudo mkdir -p /mnt/avd + sudo chown "$USER:$USER" /mnt/avd + df -h / /mnt - name: AVD cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true id: avd-cache with: # Must match the save path exactly path: | - ~/.android/avd/* + /mnt/avd/* ~/.android/adb* key: avd-${{ runner.os }}-${{ env.AVD_API_LEVEL }}-${{ env.AVD_TARGET }}-${{ env.AVD_ARCH }} + - name: Link AVD home to /mnt + # android-emulator-runner exportVariables ANDROID_AVD_HOME to $HOME/.android/avd + run: | + mkdir -p "$HOME/.android" + rm -rf "$HOME/.android/avd" + ln -s /mnt/avd "$HOME/.android/avd" - name: Start AVD then run E2E tests - uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b + uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a + env: + ANDROID_AVD_HOME: /mnt/avd with: api-level: ${{ env.AVD_API_LEVEL }} target: ${{ env.AVD_TARGET }} @@ -119,7 +141,7 @@ jobs: - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae with: # The firebase emulators are pure javascript and java, OS-independent enableCrossOsArchive: true @@ -129,10 +151,36 @@ jobs: - name: Save Android Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae with: key: ${{ steps.avd-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: | - ~/.android/avd/* + /mnt/avd/* ~/.android/adb* + + agp9-compatibility: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + with: + distribution: 'temurin' + java-version: '21' + - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + with: + channel: 'stable' + cache: true + cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" + pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 + with: + run-bootstrap: false + melos-version: '5.3.0' + - name: 'Bootstrap tests package' + run: melos bootstrap --scope tests + - name: Gradle cache + uses: gradle/actions/setup-gradle@v4 + - name: 'Build tests app with AGP 9' + run: bash ./.github/workflows/scripts/agp9-compatibility.sh diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index 998ae091362d..ce9e4e570691 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -1,7 +1,7 @@ name: e2e-fdc concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-fdc cancel-in-progress: true on: @@ -20,6 +20,11 @@ on: - 'website/**' - '**/example/**' - '**.md' + workflow_call: + inputs: + nightly_test_mode: + type: boolean + default: false permissions: contents: read @@ -27,7 +32,7 @@ permissions: jobs: android: runs-on: ubuntu-latest - timeout-minutes: 45 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 45 }} strategy: fail-fast: false env: @@ -35,39 +40,36 @@ jobs: AVD_API_LEVEL: 34 AVD_TARGET: google_apis steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + uses: ikalnytskyi/action-setup-postgres@v8 + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -76,6 +78,7 @@ jobs: - name: Start Firebase Emulator run: | cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh @@ -85,7 +88,7 @@ jobs: sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - name: Gradle cache - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v6 - name: Free Disk Space (Ubuntu) uses: AdityaGarg8/remove-unwanted-software@90e01b21170618765a73370fcc3abbd1684a7793 with: @@ -94,18 +97,30 @@ jobs: remove-codeql: true remove-docker-images: true remove-large-packages: true + - name: Prepare AVD home on /mnt + run: | + sudo mkdir -p /mnt/avd + sudo chown "$USER:$USER" /mnt/avd + df -h / /mnt - name: AVD cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae id: avd-cache continue-on-error: true with: # Must match the save path exactly path: | - ~/.android/avd/* + /mnt/avd/* ~/.android/adb* key: avd-${{ runner.os }}-${{ env.AVD_API_LEVEL }}-${{ env.AVD_TARGET }}-${{ env.AVD_ARCH }} + - name: Link AVD home to /mnt + run: | + mkdir -p "$HOME/.android" + rm -rf "$HOME/.android/avd" + ln -s /mnt/avd "$HOME/.android/avd" - name: Start AVD then run E2E tests - uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b + uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a + env: + ANDROID_AVD_HOME: /mnt/avd with: api-level: ${{ env.AVD_API_LEVEL }} target: ${{ env.AVD_TARGET }} @@ -117,52 +132,50 @@ jobs: - name: Save Android Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: key: ${{ steps.avd-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: | - ~/.android/avd/* + /mnt/avd/* ~/.android/adb* - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators ios: runs-on: macos-15 - timeout-minutes: 45 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 45 }} strategy: fail-fast: false steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - name: Xcode run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - - uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 + uses: ikalnytskyi/action-setup-postgres@v8 + - uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 name: Xcode Compile Cache with: key: xcode-cache-${{ runner.os }} save: "${{ github.ref == 'refs/heads/main' }}" max-size: 700M - - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true name: Pods Cache id: pods-cache @@ -174,25 +187,22 @@ jobs: - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -213,13 +223,20 @@ jobs: run: | sudo chown -R 501:20 "/Users/runner/.npm" cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh - uses: futureware-tech/simulator-action@e89aa8f93d3aec35083ff49d2854d07f7186f7f5 id: simulator with: + # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#installed-simulators model: "iPhone 16" + - name: Ensure Simulator Ready + env: + SIMULATOR: ${{ steps.simulator.outputs.udid }} + ENSURE_BOOT_IF_NEEDED: "0" + run: .github/workflows/scripts/ensure-simulator-ready.sh - name: 'E2E Tests' working-directory: 'packages/firebase_data_connect/firebase_data_connect/example' env: @@ -227,31 +244,26 @@ jobs: run: | # Uncomment following line to have simulator logs printed out for debugging purposes. # xcrun simctl spawn booted log stream --predicate 'eventMessage contains "flutter"' & - # The iOS simulator sometimes fails to connect the VM Service, hanging for - # 12 minutes before timing out. Use a 6-minute limit and retry once with - # a simulator reboot. Normal connection takes 30s-5min. - perl -e 'alarm 360; exec @ARGV' -- flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --dart-define=CI=true --timeout 10x || { + # Retry once after VM Service / simulator flake (reboot + migration-aware wait). + perl -e 'alarm 900; exec @ARGV' -- flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --dart-define=CI=true --timeout 10x || { echo "First attempt failed or timed out. Rebooting simulator and retrying..." xcrun simctl shutdown "$SIMULATOR" || true - xcrun simctl boot "$SIMULATOR" - xcrun simctl bootstatus "$SIMULATOR" -b + "${GITHUB_WORKSPACE}/.github/workflows/scripts/ensure-simulator-ready.sh" flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --dart-define=CI=true --timeout 10x } - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators - name: Save Pods Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: key: ${{ steps.pods-cache.outputs.cache-primary-key }} @@ -260,28 +272,28 @@ jobs: web: runs-on: macos-latest - timeout-minutes: 15 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 15 }} strategy: fail-fast: false steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + uses: ikalnytskyi/action-setup-postgres@v8 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -290,22 +302,20 @@ jobs: - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} - name: Start Firebase Emulator run: | sudo chown -R 501:20 "/Users/runner/.npm" cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh @@ -328,39 +338,37 @@ jobs: - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators web-wasm: runs-on: macos-latest - timeout-minutes: 15 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 15 }} strategy: fail-fast: false steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + uses: ikalnytskyi/action-setup-postgres@v8 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -369,21 +377,19 @@ jobs: - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} - name: Start Firebase Emulator run: | cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh @@ -407,11 +413,9 @@ jobs: - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators diff --git a/.github/workflows/e2e_tests_pipeline.yaml b/.github/workflows/e2e_tests_pipeline.yaml new file mode 100644 index 000000000000..22ffb4736201 --- /dev/null +++ b/.github/workflows/e2e_tests_pipeline.yaml @@ -0,0 +1,235 @@ +name: e2e-pipeline + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-pipeline + cancel-in-progress: true + +on: + pull_request: + paths-ignore: + - 'docs/**' + - 'website/**' + - '**/example/**' + - '**.md' + push: + branches: + - main + paths-ignore: + - 'docs/**' + - 'website/**' + - '**/example/**' + - '**.md' + workflow_call: + +permissions: + contents: read + +jobs: + pipeline-e2e-android: + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + AVD_ARCH: x86_64 + AVD_API_LEVEL: 34 + AVD_TARGET: google_apis + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a + name: Install Node.js 20 + with: + node-version: '20' + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 + with: + distribution: 'temurin' + java-version: '21' + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 + with: + channel: 'stable' + flutter-version: '3.41.9' + cache: true + cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" + pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" + - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + with: + run-bootstrap: false + melos-version: '5.3.0' + - name: Inject Firebase config for pipeline E2E + env: + FIREBASE_OPTIONS_DART: ${{ secrets.PIPELINE_E2E_FIREBASE_OPTIONS_DART }} + GOOGLE_SERVICES_JSON: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICES_JSON }} + GOOGLE_SERVICE_INFO_PLIST: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICE_INFO_PLIST }} + run: | + echo "$FIREBASE_OPTIONS_DART" > packages/cloud_firestore/cloud_firestore/pipeline_example/lib/firebase_options.dart + echo "$GOOGLE_SERVICES_JSON" > packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/google-services.json + echo "$GOOGLE_SERVICE_INFO_PLIST" > packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/GoogleService-Info.plist + - name: Bootstrap package + run: melos bootstrap --scope "cloud_firestore*" + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: Gradle cache + uses: gradle/actions/setup-gradle@v6 + - name: Free Disk Space (Ubuntu) + uses: AdityaGarg8/remove-unwanted-software@90e01b21170618765a73370fcc3abbd1684a7793 + with: + remove-dotnet: true + remove-haskell: true + remove-codeql: true + remove-docker-images: true + remove-large-packages: true + - name: Prepare AVD home on /mnt + run: | + sudo mkdir -p /mnt/avd + sudo chown "$USER:$USER" /mnt/avd + df -h / /mnt + - name: AVD cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae + continue-on-error: true + id: avd-cache + with: + path: | + /mnt/avd/* + ~/.android/adb* + key: avd-${{ runner.os }}-${{ env.AVD_API_LEVEL }}-${{ env.AVD_TARGET }}-${{ env.AVD_ARCH }} + - name: Link AVD home to /mnt + run: | + mkdir -p "$HOME/.android" + rm -rf "$HOME/.android/avd" + ln -s /mnt/avd "$HOME/.android/avd" + - name: Start AVD then run pipeline E2E tests + uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a + env: + ANDROID_AVD_HOME: /mnt/avd + with: + api-level: ${{ env.AVD_API_LEVEL }} + target: ${{ env.AVD_TARGET }} + arch: ${{ env.AVD_ARCH }} + emulator-build: 14214601 + working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example + script: | + flutter test integration_test/pipeline/pipeline_live_test.dart --timeout 10x --dart-define=CI=true -d emulator-5554 + - name: Ensure Appium is shut down + run: | + pgrep -f appium && pkill -f appium || echo "No Appium process found" + - name: Save Android Emulator Cache + if: github.ref == 'refs/heads/main' + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae + continue-on-error: true + with: + key: ${{ steps.avd-cache.outputs.cache-primary-key }} + path: | + /mnt/avd/* + ~/.android/adb* + + pipeline-e2e-web: + runs-on: macos-latest + timeout-minutes: 25 + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a + name: Install Node.js 20 + with: + node-version: '20' + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 + with: + channel: 'stable' + cache: true + cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" + pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" + - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + with: + run-bootstrap: false + melos-version: '5.3.0' + - name: Inject Firebase config for pipeline E2E + env: + FIREBASE_OPTIONS_DART: ${{ secrets.PIPELINE_E2E_FIREBASE_OPTIONS_DART }} + GOOGLE_SERVICES_JSON: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICES_JSON }} + GOOGLE_SERVICE_INFO_PLIST: ${{ secrets.PIPELINE_E2E_GOOGLE_SERVICE_INFO_PLIST }} + run: | + echo "$FIREBASE_OPTIONS_DART" > packages/cloud_firestore/cloud_firestore/pipeline_example/lib/firebase_options.dart + echo "$GOOGLE_SERVICES_JSON" > packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/google-services.json + echo "$GOOGLE_SERVICE_INFO_PLIST" > packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/GoogleService-Info.plist + - name: Bootstrap package + run: melos bootstrap --scope "cloud_firestore*" + - name: Run pipeline E2E tests (Chrome) + working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example + # Web devices are not supported for the `flutter test` command yet. As a + # workaround we use the `flutter drive` command. Tracking issue: + # https://github.com/flutter/flutter/issues/66264 + run: | + chromedriver --port=4444 --trace-buffer-size=100000 & + flutter drive --target=./integration_test/pipeline/pipeline_live_test.dart --driver=./test_driver/integration_test.dart -d chrome --dart-define=CI=true | tee output.log + output=$( packages/cloud_firestore/cloud_firestore/pipeline_example/lib/firebase_options.dart + echo "$GOOGLE_SERVICES_JSON" > packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/google-services.json + echo "$GOOGLE_SERVICE_INFO_PLIST" > packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/GoogleService-Info.plist + - name: Bootstrap package + run: melos bootstrap --scope "cloud_firestore*" + - name: Prepare iOS project for Swift Package Manager + working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example/ios + run: | + if [ -f Podfile ]; then pod deintegrate; fi + rm -f Podfile Podfile.lock + rm -rf Pods + - uses: futureware-tech/simulator-action@e89aa8f93d3aec35083ff49d2854d07f7186f7f5 + id: simulator + with: + # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#installed-simulators + model: "iPhone 16" + - name: Build iOS (simulator) + working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example + run: | + flutter build ios --no-codesign --simulator --debug --target=./integration_test/pipeline/pipeline_live_test.dart --dart-define=CI=true + - name: Ensure Simulator Ready + env: + SIMULATOR: ${{ steps.simulator.outputs.udid }} + ENSURE_BOOT_IF_NEEDED: "0" + run: .github/workflows/scripts/ensure-simulator-ready.sh + - name: Run pipeline E2E tests (iOS) + working-directory: packages/cloud_firestore/cloud_firestore/pipeline_example + env: + SIMULATOR: ${{ steps.simulator.outputs.udid }} + run: | + flutter test integration_test/pipeline/pipeline_live_test.dart -d "$SIMULATOR" --timeout 10x --dart-define=CI=true diff --git a/.github/workflows/ios.yaml b/.github/workflows/ios.yaml index 494ca9adc250..1a8d7841ff5c 100644 --- a/.github/workflows/ios.yaml +++ b/.github/workflows/ios.yaml @@ -1,7 +1,7 @@ name: e2e-iOS concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-ios cancel-in-progress: true on: @@ -23,35 +23,42 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: + inputs: + nightly_test_mode: + type: boolean + default: false jobs: ios: runs-on: macos-15 - timeout-minutes: 60 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 60 }} strategy: fail-fast: false matrix: working_directory: ['tests', 'packages/cloud_firestore/cloud_firestore/example'] + exclude: + - working_directory: ${{ inputs.nightly_test_mode && 'packages/cloud_firestore/cloud_firestore/example' || 'none' }} steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - name: Xcode run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - - uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 + - uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 name: Xcode Compile Cache with: key: xcode-cache-${{ runner.os }} save: "${{ github.ref == 'refs/heads/main' }}" max-size: 700M - - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true name: Pods Cache id: pods-cache @@ -66,7 +73,7 @@ jobs: echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -75,13 +82,14 @@ jobs: path: ~/.cache/firebase/emulators key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} restore-keys: firebase-emulators-v3 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true + flutter-version: '3.41.9' cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -117,8 +125,13 @@ jobs: - uses: futureware-tech/simulator-action@e89aa8f93d3aec35083ff49d2854d07f7186f7f5 id: simulator with: - # List of available simulators: https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md#installed-simulators + # List of available simulators: https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#installed-simulators model: "iPhone 16" + - name: Ensure Simulator Ready + env: + SIMULATOR: ${{ steps.simulator.outputs.udid }} + ENSURE_BOOT_IF_NEEDED: "0" + run: .github/workflows/scripts/ensure-simulator-ready.sh - name: 'E2E Tests' working-directory: ${{ matrix.working_directory }} env: @@ -126,20 +139,13 @@ jobs: run: | # Uncomment following line to have simulator logs printed out for debugging purposes. # xcrun simctl spawn booted log stream --predicate 'eventMessage contains "flutter"' & - # The iOS simulator sometimes fails to connect the VM Service, hanging for - # 12 minutes before timing out. Use a 6-minute limit and retry once with - # a simulator reboot. Normal connection takes 30s-5min. - perl -e 'alarm 360; exec @ARGV' -- flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --timeout 10x --dart-define=CI=true || { - echo "First attempt failed or timed out. Rebooting simulator and retrying..." - xcrun simctl shutdown "$SIMULATOR" || true - xcrun simctl boot "$SIMULATOR" - xcrun simctl bootstatus "$SIMULATOR" -b - flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --timeout 10x --dart-define=CI=true - } + # Once the integration test runner has launched, leave it running rather + # than starting a second app instance from a retry. + flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --timeout 10x --dart-define=CI=true - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -150,7 +156,7 @@ jobs: - name: Save Pods Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: key: ${{ steps.pods-cache.outputs.cache-primary-key }} diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index b92acb0687ea..b2817c514338 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -1,7 +1,7 @@ name: e2e-macOS concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-macos cancel-in-progress: true on: @@ -23,27 +23,34 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: + inputs: + nightly_test_mode: + type: boolean + default: false jobs: macos: runs-on: macos-15 - timeout-minutes: 45 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 45 }} strategy: fail-fast: false matrix: working_directory: ['tests', 'packages/cloud_firestore/cloud_firestore/example'] + exclude: + - working_directory: ${{ inputs.nightly_test_mode && 'packages/cloud_firestore/cloud_firestore/example' || 'none' }} steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - - uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 + - uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 name: Xcode Compile Cache with: key: xcode-cache-${{ runner.os }} @@ -51,7 +58,7 @@ jobs: max-size: 700M - name: Pods Cache continue-on-error: true - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae id: pods-cache with: # Must match the save path exactly @@ -64,7 +71,7 @@ jobs: echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -73,13 +80,14 @@ jobs: path: ~/.cache/firebase/emulators key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} restore-keys: firebase-emulators-v3 - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' + flutter-version: '3.41.9' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -126,7 +134,7 @@ jobs: continue-on-error: true # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae with: # The firebase emulators are pure javascript and java, OS-independent enableCrossOsArchive: true @@ -137,7 +145,7 @@ jobs: continue-on-error: true # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae with: key: ${{ steps.pods-cache.outputs.cache-primary-key }} # Must match the restore paths exactly diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml new file mode 100644 index 000000000000..97b67dfb505a --- /dev/null +++ b/.github/workflows/nightly.yaml @@ -0,0 +1,81 @@ +name: nightly-ci +run-name: ${{ github.event.inputs.test_mode == 'true' && format('[Test Mode] {0}', github.workflow) || github.workflow }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +on: + schedule: + - cron: '0 4 * * *' + # The dispatch is only for test purpose + # workflow_dispatch: + # inputs: + # test_mode: + # description: 'Run in test mode' + # type: boolean + # default: false + +permissions: + contents: read + issues: write + +jobs: + e2e-android: + uses: ./.github/workflows/android.yaml + with: + nightly_test_mode: ${{ github.event.inputs.test_mode == 'true' }} + secrets: inherit + e2e-ios: + uses: ./.github/workflows/ios.yaml + with: + nightly_test_mode: ${{ github.event.inputs.test_mode == 'true' }} + secrets: inherit + e2e-macos: + uses: ./.github/workflows/macos.yaml + with: + nightly_test_mode: ${{ github.event.inputs.test_mode == 'true' }} + secrets: inherit + e2e-web: + uses: ./.github/workflows/web.yaml + with: + nightly_test_mode: ${{ github.event.inputs.test_mode == 'true' }} + secrets: inherit + e2e-windows: + uses: ./.github/workflows/windows.yaml + with: + nightly_test_mode: ${{ github.event.inputs.test_mode == 'true' }} + secrets: inherit + e2e-fdc: + uses: ./.github/workflows/e2e_tests_fdc.yaml + with: + nightly_test_mode: ${{ github.event.inputs.test_mode == 'true' }} + secrets: inherit + e2e-pipeline: + uses: ./.github/workflows/e2e_tests_pipeline.yaml + if: ${{ github.event.inputs.test_mode != 'true' }} + secrets: inherit + + update-dashboard: + runs-on: ubuntu-latest + if: ${{ always() && !cancelled() }} + needs: [e2e-android, e2e-ios, e2e-macos, e2e-web, e2e-windows, e2e-fdc, e2e-pipeline] + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 + with: + channel: 'stable' + - name: Update Dashboard Issue + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TEST_MODE: ${{ github.event.inputs.test_mode == 'true' }} + ANDROID_STATUS: ${{ needs.e2e-android.result }} + IOS_STATUS: ${{ needs.e2e-ios.result }} + MACOS_STATUS: ${{ needs.e2e-macos.result }} + WEB_STATUS: ${{ needs.e2e-web.result }} + WINDOWS_STATUS: ${{ needs.e2e-windows.result }} + FDC_STATUS: ${{ needs.e2e-fdc.result }} + PIPELINE_STATUS: ${{ needs.e2e-pipeline.result }} + REPO: ${{ github.repository }} + run: | + dart .github/workflows/scripts/nightly_issue_dashboard.dart diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 95e86678e12e..7e633e33e267 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -32,12 +32,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v3.1.0 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v3.1.0 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/pr_title.yaml b/.github/workflows/pr_title.yaml index 453064704526..76971ebc00c9 100644 --- a/.github/workflows/pr_title.yaml +++ b/.github/workflows/pr_title.yaml @@ -5,7 +5,6 @@ on: types: - opened - edited - - synchronize jobs: validate: @@ -13,6 +12,6 @@ jobs: pull-requests: read runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/scripts/agp9-compatibility.sh b/.github/workflows/scripts/agp9-compatibility.sh new file mode 100644 index 000000000000..9cf19d2c40ea --- /dev/null +++ b/.github/workflows/scripts/agp9-compatibility.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -euo pipefail + +AGP_VERSION="${AGP_VERSION:-9.0.1}" +GRADLE_VERSION="${GRADLE_VERSION:-9.1.0}" + +TEST_ANDROID_DIR="tests/android" + +perl -0pi -e "s/id \"com\.android\.application\" version \"[^\"]+\" apply false/id \"com.android.application\" version \"$AGP_VERSION\" apply false/" \ + "$TEST_ANDROID_DIR/settings.gradle" + +perl -0pi -e "s#distributionUrl=https\\\\://services.gradle.org/distributions/gradle-[^-]+-all.zip#distributionUrl=https\\\\://services.gradle.org/distributions/gradle-$GRADLE_VERSION-all.zip#" \ + "$TEST_ANDROID_DIR/gradle/wrapper/gradle-wrapper.properties" + +# Flutter's Gradle plugin does not fully support AGP 9's new DSL yet. Opt out in +# this CI-only checkout so the job validates FlutterFire plugin compatibility. +# https://docs.flutter.cn/release/breaking-changes/migrate-to-agp-9/ +grep -q '^android.newDsl=false$' "$TEST_ANDROID_DIR/gradle.properties" || \ + printf '\nandroid.newDsl=false\n' >> "$TEST_ANDROID_DIR/gradle.properties" + +# AGP 9 has built-in Kotlin support. Keep the compatibility check focused on +# FlutterFire plugins by applying the same migration to the test app at runtime. +perl -0pi -e 's/\n\s*id "kotlin-android"\n/\n/' "$TEST_ANDROID_DIR/app/build.gradle" +perl -0pi -e 's/\n\s*kotlinOptions \{\n\s*jvmTarget = JavaVersion\.VERSION_17\n\s*\}\n/\n/' "$TEST_ANDROID_DIR/app/build.gradle" + +# AGP 9 rejects older Espresso artifacts that share the same namespace. +grep -q 'androidx.test.espresso:espresso-core:3.7.0' "$TEST_ANDROID_DIR/app/build.gradle" || cat <<'EOF' >> "$TEST_ANDROID_DIR/app/build.gradle" + +dependencies { + debugImplementation 'androidx.test.espresso:espresso-core:3.7.0' + debugImplementation 'androidx.test.espresso:espresso-idling-resource:3.7.0' +} +EOF + +grep -q "id \"com.android.application\" version \"$AGP_VERSION\" apply false" "$TEST_ANDROID_DIR/settings.gradle" +grep -q "gradle-$GRADLE_VERSION-all.zip" "$TEST_ANDROID_DIR/gradle/wrapper/gradle-wrapper.properties" +grep -q '^android.newDsl=false$' "$TEST_ANDROID_DIR/gradle.properties" +! grep -q 'id "kotlin-android"' "$TEST_ANDROID_DIR/app/build.gradle" +! grep -q 'kotlinOptions' "$TEST_ANDROID_DIR/app/build.gradle" +grep -q 'androidx.test.espresso:espresso-core:3.7.0' "$TEST_ANDROID_DIR/app/build.gradle" +grep -q 'androidx.test.espresso:espresso-idling-resource:3.7.0' "$TEST_ANDROID_DIR/app/build.gradle" + +cd tests +flutter build apk --debug --dart-define=CI=true --no-android-gradle-daemon diff --git a/.github/workflows/scripts/drive-example.sh b/.github/workflows/scripts/drive-example.sh index 2004a01b45ca..2c342e3f0fdd 100755 --- a/.github/workflows/scripts/drive-example.sh +++ b/.github/workflows/scripts/drive-example.sh @@ -13,12 +13,11 @@ fi if [ "$ACTION" == "ios" ] then - SIMULATOR="iPhone 14" - # Boot simulator and wait for System app to be ready. - xcrun simctl bootstatus "$SIMULATOR" -b + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + SIMULATOR="${SIMULATOR:-iPhone 16}" + export SIMULATOR + "${SCRIPT_DIR}/ensure-simulator-ready.sh" xcrun simctl logverbose "$SIMULATOR" enable - # Sleep to allow simulator to settle. - sleep 15 # Uncomment following line to have simulator logs printed out for debugging purposes. # xcrun simctl spawn booted log stream --predicate 'eventMessage contains "flutter"' & melos exec -c 1 --fail-fast --scope="$FLUTTERFIRE_PLUGIN_SCOPE_EXAMPLE" --dir-exists=integration_test -- \ diff --git a/.github/workflows/scripts/ensure-simulator-ready.sh b/.github/workflows/scripts/ensure-simulator-ready.sh new file mode 100755 index 000000000000..5a18a42f791a --- /dev/null +++ b/.github/workflows/scripts/ensure-simulator-ready.sh @@ -0,0 +1,182 @@ +#!/bin/bash + +# Wait until an iOS Simulator is fully ready for integration tests (including first-boot +# data migration). Intended to run after futureware-tech/simulator-action (or any step +# that has issued simctl boot) and before flutter test / flutter drive. +# +# Usage: +# SIMULATOR= ./ensure-simulator-ready.sh +# ./ensure-simulator-ready.sh +# +# Environment: +# SIMULATOR UDID or device name (required if not passed as arg) +# BOOT_POLL_INTERVAL_SECONDS Poll interval (default: 20) +# BOOT_PROBE_TIMEOUT_SECONDS Per-probe timeout (default: 12) +# BOOT_MAX_WAIT_SECONDS Max wait for full boot (default: 660) +# ENSURE_OPEN_SIMULATOR_APP Open Simulator.app when booting (default: 1) +# ENSURE_BOOT_IF_NEEDED simctl boot when not Booted yet (default: 1) +set -euo pipefail + +BOOT_POLL_INTERVAL_SECONDS="${BOOT_POLL_INTERVAL_SECONDS:-20}" +BOOT_PROBE_TIMEOUT_SECONDS="${BOOT_PROBE_TIMEOUT_SECONDS:-12}" +BOOT_MAX_WAIT_SECONDS="${BOOT_MAX_WAIT_SECONDS:-660}" +ENSURE_OPEN_SIMULATOR_APP="${ENSURE_OPEN_SIMULATOR_APP:-1}" +ENSURE_BOOT_IF_NEEDED="${ENSURE_BOOT_IF_NEEDED:-1}" + +DEVICE="${SIMULATOR:-${1:-}}" +if [[ -z "$DEVICE" ]]; then + echo "[boot-status] ERROR: set SIMULATOR or pass device UDID/name as first argument" >&2 + exit 1 +fi + +run_with_timeout() { + local max="$1" + shift + "$@" & + local cmd_pid=$! + local waited=0 + while kill -0 "$cmd_pid" 2>/dev/null && (( waited < max )); do + sleep 1 + waited=$((waited + 1)) + done + if kill -0 "$cmd_pid" 2>/dev/null; then + kill "$cmd_pid" 2>/dev/null + wait "$cmd_pid" 2>/dev/null || true + return 124 + fi + wait "$cmd_pid" +} + +log_boot_status() { + echo "[boot-status] $*" +} + +is_udid() { + [[ "$1" =~ ^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$ ]] +} + +describe_booted_device() { + local device="$1" + if is_udid "$device"; then + xcrun simctl list devices booted 2>/dev/null \ + | grep -F "$device" \ + | grep -v 'unavailable' \ + | head -1 \ + || true + else + xcrun simctl list devices booted 2>/dev/null \ + | grep -i "${device} (" \ + | grep -v 'Phone:' \ + | grep -v 'unavailable' \ + | grep -v CoreSimulator \ + | head -1 \ + || true + fi +} + +is_device_booted() { + [[ -n "$(describe_booted_device "$1")" ]] +} + +log_migration_status() { + local device="$1" + local migration_output probe_rc + + log_boot_status "probing data migration (bootstatus -d, up to ${BOOT_PROBE_TIMEOUT_SECONDS}s)..." + set +e + migration_output="$(run_with_timeout "$BOOT_PROBE_TIMEOUT_SECONDS" xcrun simctl bootstatus "$device" -d 2>&1)" + probe_rc=$? + set -e + + if [[ "$probe_rc" -eq 124 ]]; then + log_boot_status " data migration / system bring-up still in progress" + return 1 + fi + + if [[ -n "$migration_output" ]]; then + while IFS= read -r line; do + [[ -z "$line" ]] && continue + log_boot_status " ${line}" + done <<<"$migration_output" + else + log_boot_status " no migration details reported" + fi + return 0 +} + +wait_for_simulator_ready() { + local device="$1" + local start=$SECONDS + + while (( SECONDS - start < BOOT_MAX_WAIT_SECONDS )); do + local elapsed=$(( SECONDS - start )) + local booted_line ready_rc + + log_boot_status "elapsed=${elapsed}s phase=wait_for_full_boot device=\"${device}\"" + + booted_line="$(describe_booted_device "$device")" + if [[ -z "$booted_line" ]]; then + log_boot_status " simctl list: not in Booted state yet" + else + log_boot_status " simctl list: ${booted_line}" + log_migration_status "$device" || true + fi + + set +e + run_with_timeout "$BOOT_PROBE_TIMEOUT_SECONDS" xcrun simctl bootstatus "$device" >/dev/null 2>&1 + ready_rc=$? + set -e + + if [[ "$ready_rc" -eq 0 ]]; then + log_boot_status "bootstatus: simulator ready after ${elapsed}s" + log_migration_status "$device" || true + return 0 + fi + + if [[ "$ready_rc" -eq 124 ]]; then + log_boot_status "bootstatus: still booting (probe timed out after ${BOOT_PROBE_TIMEOUT_SECONDS}s)" + else + log_boot_status "bootstatus: probe exited with status ${ready_rc}" + fi + + sleep "$BOOT_POLL_INTERVAL_SECONDS" + done + + log_boot_status "ERROR: timed out after ${BOOT_MAX_WAIT_SECONDS}s waiting for simulator to become ready" + return 1 +} + +if is_udid "$DEVICE"; then + log_boot_status "phase=resolve_device udid=\"${DEVICE}\"" +else + log_boot_status "phase=resolve_device name=\"${DEVICE}\"" +fi + +if ! is_device_booted "$DEVICE"; then + if [[ "$ENSURE_BOOT_IF_NEEDED" == "1" ]]; then + log_boot_status "phase=boot_command device not Booted; starting simctl boot..." + set +e + boot_output="$(xcrun simctl boot "$DEVICE" 2>&1)" + boot_rc=$? + set -e + if [[ "$boot_rc" -ne 0 ]]; then + log_boot_status "simctl boot exited ${boot_rc}: ${boot_output}" + else + log_boot_status "simctl boot command returned (device may still be migrating data)" + fi + if [[ "$ENSURE_OPEN_SIMULATOR_APP" == "1" ]]; then + log_boot_status "phase=foreground_simulator opening Simulator.app..." + open -a Simulator.app || true + fi + else + log_boot_status "phase=boot_command skipped (ENSURE_BOOT_IF_NEEDED=0); waiting for existing boot..." + fi +else + log_boot_status "phase=boot_command device already Booted; waiting for full readiness..." +fi + +if ! wait_for_simulator_ready "$DEVICE"; then + exit 1 +fi + +log_boot_status "phase=complete device=\"${DEVICE}\" ready for flutter test" diff --git a/.github/workflows/scripts/functions/package-lock.json b/.github/workflows/scripts/functions/package-lock.json index a8c5655fd5c3..8d68cfdd20e6 100644 --- a/.github/workflows/scripts/functions/package-lock.json +++ b/.github/workflows/scripts/functions/package-lock.json @@ -238,6 +238,18 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "optional": true + }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -258,9 +270,9 @@ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", @@ -282,9 +294,9 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.1.tgz", + "integrity": "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", @@ -297,9 +309,9 @@ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==" }, "node_modules/@tootallnate/once": { "version": "2.0.0", @@ -987,9 +999,9 @@ "optional": true }, "node_modules/fast-xml-builder": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", - "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "funding": [ { "type": "github", @@ -998,13 +1010,14 @@ ], "optional": true, "dependencies": { - "path-expression-matcher": "^1.1.3" + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" } }, "node_modules/fast-xml-parser": { - "version": "5.5.7", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", - "integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.1.tgz", + "integrity": "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==", "funding": [ { "type": "github", @@ -1013,9 +1026,10 @@ ], "optional": true, "dependencies": { - "fast-xml-builder": "^1.1.4", - "path-expression-matcher": "^1.1.3", - "strnum": "^2.2.0" + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.5", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" @@ -1592,9 +1606,9 @@ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -1728,9 +1742,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "engines": { "node": ">= 6.13.0" } @@ -1807,9 +1821,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", - "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "funding": [ { "type": "github", @@ -1822,9 +1836,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" }, "node_modules/proto3-json-serializer": { "version": "2.0.2", @@ -1839,21 +1853,21 @@ } }, "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.8.tgz", + "integrity": "sha512-dvpCIeLPbXZS/Ete7yLaO7RenOdken2NHKykBXbsaGxZT0UTltcarBciw+A78SRQs9iMAAVpsYA+l8b1hTePIA==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", + "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", + "@protobufjs/inquire": "^1.1.1", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.0.0" }, @@ -2180,9 +2194,9 @@ } }, "node_modules/strnum": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", - "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", + "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", "funding": [ { "type": "github", @@ -2399,6 +2413,21 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "optional": true }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "optional": true, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -2629,6 +2658,12 @@ "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", "optional": true }, + "@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "optional": true + }, "@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -2646,9 +2681,9 @@ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==" }, "@protobufjs/eventemitter": { "version": "1.1.0", @@ -2670,9 +2705,9 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.1.tgz", + "integrity": "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==" }, "@protobufjs/path": { "version": "1.1.2", @@ -2685,9 +2720,9 @@ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==" }, "@tootallnate/once": { "version": "2.0.0", @@ -3237,23 +3272,25 @@ "optional": true }, "fast-xml-builder": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", - "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "optional": true, "requires": { - "path-expression-matcher": "^1.1.3" + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" } }, "fast-xml-parser": { - "version": "5.5.7", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", - "integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.1.tgz", + "integrity": "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==", "optional": true, "requires": { - "fast-xml-builder": "^1.1.4", - "path-expression-matcher": "^1.1.3", - "strnum": "^2.2.0" + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.5", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" } }, "faye-websocket": { @@ -3683,9 +3720,9 @@ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, "lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "lodash.camelcase": { "version": "4.3.0", @@ -3778,9 +3815,9 @@ } }, "node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==" }, "object-assign": { "version": "4.1.1", @@ -3830,15 +3867,15 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "path-expression-matcher": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", - "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "optional": true }, "path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" }, "proto3-json-serializer": { "version": "2.0.2", @@ -3850,20 +3887,20 @@ } }, "protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.8.tgz", + "integrity": "sha512-dvpCIeLPbXZS/Ete7yLaO7RenOdken2NHKykBXbsaGxZT0UTltcarBciw+A78SRQs9iMAAVpsYA+l8b1hTePIA==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", + "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", + "@protobufjs/inquire": "^1.1.1", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.0.0" } @@ -4112,9 +4149,9 @@ } }, "strnum": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", - "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", + "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", "optional": true }, "stubs": { @@ -4270,6 +4307,12 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "optional": true }, + "xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "optional": true + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/.github/workflows/scripts/nightly_issue_dashboard.dart b/.github/workflows/scripts/nightly_issue_dashboard.dart new file mode 100644 index 000000000000..1b486ed5612a --- /dev/null +++ b/.github/workflows/scripts/nightly_issue_dashboard.dart @@ -0,0 +1,265 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; +import 'dart:io'; + +void main() async { + final env = Platform.environment; + final token = env['GITHUB_TOKEN']; + final repo = env['REPO']; + final androidStatus = env['ANDROID_STATUS'] ?? 'skipped'; + final webStatus = env['WEB_STATUS'] ?? 'skipped'; + final iosStatus = env['IOS_STATUS'] ?? 'skipped'; + final macosStatus = env['MACOS_STATUS'] ?? 'skipped'; + final windowsStatus = env['WINDOWS_STATUS'] ?? 'skipped'; + final fdcStatus = env['FDC_STATUS'] ?? 'skipped'; + final pipelineStatus = env['PIPELINE_STATUS'] ?? 'skipped'; + final runId = env['GITHUB_RUN_ID']; + final serverUrl = env['GITHUB_SERVER_URL'] ?? 'https://github.com'; + + final testMode = env['TEST_MODE'] == 'true'; + + if (token == null || repo == null) { + print('Error: GITHUB_TOKEN or REPO environment variables not set.'); + exit(1); + } + + final date = DateTime.now().toUtc().toString().substring(0, 10); + final runUrl = '$serverUrl/$repo/actions/runs/$runId'; + final notes = '[View Run]($runUrl)'; + + final androidIcon = _getIcon(androidStatus); + final webIcon = _getIcon(webStatus); + final iosIcon = _getIcon(iosStatus); + final macosIcon = _getIcon(macosStatus); + final windowsIcon = _getIcon(windowsStatus); + final fdcIcon = _getIcon(fdcStatus); + final pipelineIcon = _getIcon(pipelineStatus); + + final newRow = + '| $date | $androidIcon | $iosIcon | $webIcon | $macosIcon | $windowsIcon | $fdcIcon | $pipelineIcon | $notes |'; + + print('New Row: $newRow'); + + if (testMode) { + print('Test mode enabled. Skipping dashboard update.'); + print('The following row would be added to the issue:'); + print(newRow); + return; + } + + final client = HttpClient(); + try { + final issueNumber = await _findIssue(client, token, repo); + + if (issueNumber == null) { + print('Issue not found. Creating a new one.'); + await _createIssue(client, token, repo, newRow); + } else { + print('Found issue #$issueNumber. Updating.'); + await _updateIssue(client, token, repo, issueNumber, newRow); + } + } finally { + client.close(); + } +} + +String _getIcon(String status) { + switch (status) { + case 'success': + return '✅ Pass'; + case 'failure': + return '❌ Failure'; + case 'cancelled': + return '⚪ Cancelled'; + case 'skipped': + return '➖ Skipped'; + default: + return '❓ Unknown'; + } +} + +Future _findIssue(HttpClient client, String token, String repo) async { + final url = Uri.parse( + 'https://api.github.com/repos/$repo/issues?labels=nightly-testing&state=open', + ); + final request = await client.getUrl(url); + _addHeaders(request, token); + + final response = await request.close(); + if (response.statusCode != 200) { + print('Failed to search issues: ${response.statusCode}'); + return null; + } + + final body = await response.transform(utf8.decoder).join(); + final json = jsonDecode(body) as List; + + for (final issue in json) { + if (issue['title'] == '[FlutterFire] Nightly Integration Testing Report') { + return issue['number'] as int; + } + } + return null; +} + +Future _createIssue( + HttpClient client, + String token, + String repo, + String newRow, +) async { + final url = Uri.parse('https://api.github.com/repos/$repo/issues'); + final request = await client.postUrl(url); + _addHeaders(request, token); + + final body = { + 'title': '[FlutterFire] Nightly Integration Testing Report', + 'labels': ['nightly-testing'], + 'body': + ''' +## Testing History (last 30 days) + +| Date | Android | iOS | Web | MacOS | Windows | FDC | Pipeline | Notes | +| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | +$newRow +''', + }; + + request.add(utf8.encode(jsonEncode(body))); + final response = await request.close(); + if (response.statusCode != 201) { + print('Failed to create issue: ${response.statusCode}'); + final respBody = await response.transform(utf8.decoder).join(); + print('Response: $respBody'); + } else { + print('Issue created successfully.'); + } +} + +Future _updateIssue( + HttpClient client, + String token, + String repo, + int issueNumber, + String newRow, +) async { + final getUrl = Uri.parse( + 'https://api.github.com/repos/$repo/issues/$issueNumber', + ); + final getRequest = await client.getUrl(getUrl); + _addHeaders(getRequest, token); + + final getResponse = await getRequest.close(); + if (getResponse.statusCode != 200) { + print('Failed to fetch issue #$issueNumber: ${getResponse.statusCode}'); + return; + } + + final getBody = await getResponse.transform(utf8.decoder).join(); + final issueJson = jsonDecode(getBody); + String currentBody = issueJson['body'] ?? ''; + + final updatedBody = _appendRow(currentBody, newRow); + + final patchUrl = Uri.parse( + 'https://api.github.com/repos/$repo/issues/$issueNumber', + ); + final patchRequest = await client.patchUrl(patchUrl); + _addHeaders(patchRequest, token); + + final patchBody = {'body': updatedBody}; + patchRequest.add(utf8.encode(jsonEncode(patchBody))); + + final patchResponse = await patchRequest.close(); + if (patchResponse.statusCode != 200) { + print('Failed to update issue #$issueNumber: ${patchResponse.statusCode}'); + final respBody = await patchResponse.transform(utf8.decoder).join(); + print('Response: $respBody'); + } else { + print('Issue #$issueNumber updated successfully.'); + } +} + +String _appendRow(String currentBody, String newRow) { + final lines = currentBody.split('\n'); + final tableRows = []; + var inTable = false; + + for (final line in lines) { + if (line.startsWith('| Date |')) { + inTable = true; + continue; + } + if (inTable && line.startsWith('|')) { + if (line.startsWith('| ---') || line.startsWith('| :---')) { + continue; + } + tableRows.add(line); + } + } + + tableRows.add(newRow); + + if (tableRows.length > 30) { + tableRows.removeRange(0, tableRows.length - 30); + } + + final newBodyLines = []; + var processedTable = false; + + for (final line in lines) { + if (line.startsWith('| Date |')) { + if (!processedTable) { + newBodyLines.add( + '| Date | Android | iOS | Web | MacOS | Windows | FDC | Pipeline | Notes |', + ); + newBodyLines.add( + '| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |', + ); + newBodyLines.addAll(tableRows); + processedTable = true; + } + inTable = true; + continue; + } + if (inTable && line.startsWith('|')) { + continue; + } + inTable = false; + newBodyLines.add(line); + } + + if (!processedTable) { + newBodyLines.add('## Testing History (last 30 days)'); + newBodyLines.add(''); + newBodyLines.add( + '| Date | Android | iOS | Web | MacOS | Windows | FDC | Pipeline | Notes |', + ); + newBodyLines.add( + '| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |', + ); + newBodyLines.add(newRow); + } + + return newBodyLines.join('\n'); +} + +void _addHeaders(HttpClientRequest request, String token) { + request.headers.add('Authorization', 'token $token'); + request.headers.add('Accept', 'application/vnd.github.v3+json'); + request.headers.add('User-Agent', 'dart-script'); + request.headers.contentType = ContentType.json; +} diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index 7ebde01e698b..8a9b9cd8c5af 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -1,7 +1,7 @@ name: e2e-web concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-web cancel-in-progress: true on: @@ -23,33 +23,40 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: + inputs: + nightly_test_mode: + type: boolean + default: false jobs: web: runs-on: macos-latest - timeout-minutes: 15 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 15 }} strategy: fail-fast: false matrix: working_directory: ['tests', 'packages/cloud_firestore/cloud_firestore/example'] + exclude: + - working_directory: ${{ inputs.nightly_test_mode && 'packages/cloud_firestore/cloud_firestore/example' || 'none' }} steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -61,7 +68,7 @@ jobs: echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -92,7 +99,7 @@ jobs: # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' continue-on-error: true - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae with: # The firebase emulators are pure javascript and java, OS-independent enableCrossOsArchive: true @@ -104,22 +111,22 @@ jobs: runs-on: macos-latest timeout-minutes: 15 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -131,7 +138,7 @@ jobs: echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -166,7 +173,7 @@ jobs: - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -177,29 +184,31 @@ jobs: web-wasm: runs-on: macos-latest - timeout-minutes: 15 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 15 }} strategy: fail-fast: false matrix: working_directory: ['tests', 'packages/cloud_firestore/cloud_firestore/example'] + exclude: + - working_directory: ${{ inputs.nightly_test_mode && 'packages/cloud_firestore/cloud_firestore/example' || 'none' }} steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a name: Install Node.js 20 with: node-version: '20' - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '5.3.0' @@ -211,7 +220,7 @@ jobs: echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache - uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent @@ -242,7 +251,7 @@ jobs: - name: Save Firestore Emulator Cache # Branches can read main cache but main cannot read branch cache. Avoid LRU eviction with main-only cache. if: github.ref == 'refs/heads/main' - uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae continue-on-error: true with: # The firebase emulators are pure javascript and java, OS-independent diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 1783aa967913..8fdc79a80620 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -1,7 +1,7 @@ name: e2e-windows concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-windows cancel-in-progress: true on: @@ -23,14 +23,19 @@ on: - '!**/example/integration_test/**' - '**/flutterfire_ui/**' - '**.md' + workflow_call: + inputs: + nightly_test_mode: + type: boolean + default: false jobs: windows: runs-on: windows-latest - timeout-minutes: 45 + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 45 }} steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' @@ -38,13 +43,13 @@ jobs: name: Install Node.js 20 with: node-version: "20" - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '3.0.0' @@ -58,13 +63,13 @@ jobs: - name: Start Firebase Emulator and run tests run: cd ./.github/workflows/scripts && firebase emulators:exec --project flutterfire-e2e-tests "cd ../../../tests && flutter test .\integration_test\e2e_test.dart -d windows --verbose" -# We cannot run the tests but we can still try to build the app because of https://github.com/flutter/flutter/issues/79213 windows-firestore: runs-on: windows-latest - timeout-minutes: 45 + if: ${{ !inputs.nightly_test_mode }} + timeout-minutes: ${{ inputs.nightly_test_mode && 5 || 45 }} steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 with: distribution: 'temurin' java-version: '21' @@ -72,13 +77,13 @@ jobs: name: Install Node.js 20 with: node-version: "20" - - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 with: channel: 'stable' cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - uses: bluefireteam/melos-action@c7dcb921b23cc520cace360b95d02b37bf09cdaa + - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false melos-version: '3.0.0' @@ -88,4 +93,13 @@ jobs: run: | npm install -g firebase-tools - name: Start Firebase Emulator and run tests - run: cd ./.github/workflows/scripts && firebase emulators:exec --project flutterfire-e2e-tests "cd ../../../packages/cloud_firestore/cloud_firestore/example && flutter build windows" + run: | + cd ./.github/workflows/scripts + firebase emulators:exec --project flutterfire-e2e-tests "cd ../../../packages/cloud_firestore/cloud_firestore/example && flutter drive --target=.\integration_test\e2e_test.dart --driver=.\test_driver\integration_test.dart -d windows --verbose" 2>&1 | Tee-Object -FilePath output.log + $exitCode = $LASTEXITCODE + $output = Get-Content output.log -Raw + if ($output -match '\[E\]' -or $output -match 'Some tests failed') { + Write-Error "All tests did not pass. Please check the logs for more information." + exit 1 + } + exit $exitCode diff --git a/.swiftformat b/.swiftformat index 7f0db7664edf..d8573cd10895 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,6 +1,7 @@ --indent 2 --maxwidth 100 --wrapparameters afterfirst ---disable sortedImports,unusedArguments,wrapMultilineStatementBraces +# Apple swift-format from flutter_plugin_tools requires `case .success(let value)` instead of `case let .success(value)`. +--disable sortedImports,unusedArguments,wrapMultilineStatementBraces,hoistPatternLet --exclude Pods,**/MainFlutterWindow.swift,**/AppDelegate.swift,**/.symlinks/** --swiftversion 5.7 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bebc02c21d1..7588ab4f535c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,974 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2026-06-22 - [BoM 4.16.1](https://github.com/firebase/flutterfire/blob/main/VERSIONS.md#flutter-bom-4161-2026-06-22) + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`firebase_auth` - `v6.5.4`](#firebase_auth---v654) + - [`firebase_database` - `v12.4.4`](#firebase_database---v1244) + - [`firebase_messaging` - `v16.4.1`](#firebase_messaging---v1641) + - [`firebase_ai` - `v3.13.1`](#firebase_ai---v3131) + - [`firebase_data_connect` - `v0.3.0+5`](#firebase_data_connect---v0305) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `firebase_ai` - `v3.13.1` + - `firebase_data_connect` - `v0.3.0+5` + +--- + +#### `firebase_auth` - `v6.5.4` + + - **FIX**: resolve FlutterSceneLifeCycleDelegate conformance guard ([#18385](https://github.com/firebase/flutterfire/issues/18385)). ([48d67196](https://github.com/firebase/flutterfire/commit/48d67196a10affe09724529df5f67cf40b62bccf)) + +#### `firebase_database` - `v12.4.4` + + - **FIX**(database,iOS): prevent duplicate database instance initialization in `FLTFirebaseDatabasePlugin` ([#18373](https://github.com/firebase/flutterfire/issues/18373)). ([bad45287](https://github.com/firebase/flutterfire/commit/bad452875def7ec070ef3c11261eb8063f11f7de)) + +#### `firebase_messaging` - `v16.4.1` + + - **FIX**: resolve FlutterSceneLifeCycleDelegate conformance guard ([#18385](https://github.com/firebase/flutterfire/issues/18385)). ([48d67196](https://github.com/firebase/flutterfire/commit/48d67196a10affe09724529df5f67cf40b62bccf)) + + +## 2026-06-17 - [BoM 4.16.0](https://github.com/firebase/flutterfire/blob/main/VERSIONS.md#flutter-bom-4160-2026-06-17) + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`cloud_firestore` - `v6.6.0`](#cloud_firestore---v660) + - [`cloud_firestore_web` - `v5.6.0`](#cloud_firestore_web---v560) + - [`firebase_ai` - `v3.13.0`](#firebase_ai---v3130) + - [`firebase_analytics` - `v12.4.3`](#firebase_analytics---v1243) + - [`firebase_app_check` - `v0.4.5`](#firebase_app_check---v045) + - [`firebase_app_check_platform_interface` - `v0.4.1`](#firebase_app_check_platform_interface---v041) + - [`firebase_app_check_web` - `v0.2.5`](#firebase_app_check_web---v025) + - [`firebase_core` - `v4.11.0`](#firebase_core---v4110) + - [`firebase_core_platform_interface` - `v7.1.0`](#firebase_core_platform_interface---v710) + - [`firebase_core_web` - `v3.9.0`](#firebase_core_web---v390) + - [`firebase_messaging` - `v16.4.0`](#firebase_messaging---v1640) + - [`firebase_messaging_platform_interface` - `v4.9.0`](#firebase_messaging_platform_interface---v490) + - [`firebase_data_connect` - `v0.3.0+4`](#firebase_data_connect---v0304) + - [`_flutterfire_internals` - `v1.3.73`](#_flutterfire_internals---v1373) + - [`cloud_firestore_platform_interface` - `v8.0.3`](#cloud_firestore_platform_interface---v803) + - [`cloud_functions` - `v6.3.3`](#cloud_functions---v633) + - [`cloud_functions_platform_interface` - `v6.0.3`](#cloud_functions_platform_interface---v603) + - [`cloud_functions_web` - `v5.1.9`](#cloud_functions_web---v519) + - [`firebase_analytics_platform_interface` - `v6.0.3`](#firebase_analytics_platform_interface---v603) + - [`firebase_analytics_web` - `v0.6.1+9`](#firebase_analytics_web---v0619) + - [`firebase_app_installations` - `v0.4.2+4`](#firebase_app_installations---v0424) + - [`firebase_app_installations_platform_interface` - `v0.1.4+72`](#firebase_app_installations_platform_interface---v01472) + - [`firebase_app_installations_web` - `v0.1.7+9`](#firebase_app_installations_web---v0179) + - [`firebase_auth` - `v6.5.3`](#firebase_auth---v653) + - [`firebase_auth_platform_interface` - `v9.0.3`](#firebase_auth_platform_interface---v903) + - [`firebase_auth_web` - `v6.2.3`](#firebase_auth_web---v623) + - [`firebase_crashlytics` - `v5.2.4`](#firebase_crashlytics---v524) + - [`firebase_crashlytics_platform_interface` - `v3.8.24`](#firebase_crashlytics_platform_interface---v3824) + - [`firebase_database` - `v12.4.3`](#firebase_database---v1243) + - [`firebase_database_platform_interface` - `v0.4.0+3`](#firebase_database_platform_interface---v0403) + - [`firebase_database_web` - `v0.2.7+10`](#firebase_database_web---v02710) + - [`firebase_in_app_messaging` - `v0.9.2+4`](#firebase_in_app_messaging---v0924) + - [`firebase_in_app_messaging_platform_interface` - `v0.2.5+24`](#firebase_in_app_messaging_platform_interface---v02524) + - [`firebase_messaging_web` - `v4.2.1`](#firebase_messaging_web---v421) + - [`firebase_ml_model_downloader` - `v0.4.2+4`](#firebase_ml_model_downloader---v0424) + - [`firebase_ml_model_downloader_platform_interface` - `v0.1.5+24`](#firebase_ml_model_downloader_platform_interface---v01524) + - [`firebase_performance` - `v0.11.4+3`](#firebase_performance---v01143) + - [`firebase_performance_platform_interface` - `v0.2.0+3`](#firebase_performance_platform_interface---v0203) + - [`firebase_performance_web` - `v0.1.8+9`](#firebase_performance_web---v0189) + - [`firebase_remote_config` - `v6.5.3`](#firebase_remote_config---v653) + - [`firebase_remote_config_platform_interface` - `v3.0.3`](#firebase_remote_config_platform_interface---v303) + - [`firebase_remote_config_web` - `v1.10.10`](#firebase_remote_config_web---v11010) + - [`firebase_storage` - `v13.4.3`](#firebase_storage---v1343) + - [`firebase_storage_platform_interface` - `v6.0.3`](#firebase_storage_platform_interface---v603) + - [`firebase_storage_web` - `v3.11.9`](#firebase_storage_web---v3119) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `firebase_data_connect` - `v0.3.0+4` + - `_flutterfire_internals` - `v1.3.73` + - `cloud_firestore_platform_interface` - `v8.0.3` + - `cloud_functions` - `v6.3.3` + - `cloud_functions_platform_interface` - `v6.0.3` + - `cloud_functions_web` - `v5.1.9` + - `firebase_analytics_platform_interface` - `v6.0.3` + - `firebase_analytics_web` - `v0.6.1+9` + - `firebase_app_installations` - `v0.4.2+4` + - `firebase_app_installations_platform_interface` - `v0.1.4+72` + - `firebase_app_installations_web` - `v0.1.7+9` + - `firebase_auth` - `v6.5.3` + - `firebase_auth_platform_interface` - `v9.0.3` + - `firebase_auth_web` - `v6.2.3` + - `firebase_crashlytics` - `v5.2.4` + - `firebase_crashlytics_platform_interface` - `v3.8.24` + - `firebase_database` - `v12.4.3` + - `firebase_database_platform_interface` - `v0.4.0+3` + - `firebase_database_web` - `v0.2.7+10` + - `firebase_in_app_messaging` - `v0.9.2+4` + - `firebase_in_app_messaging_platform_interface` - `v0.2.5+24` + - `firebase_messaging_web` - `v4.2.1` + - `firebase_ml_model_downloader` - `v0.4.2+4` + - `firebase_ml_model_downloader_platform_interface` - `v0.1.5+24` + - `firebase_performance` - `v0.11.4+3` + - `firebase_performance_platform_interface` - `v0.2.0+3` + - `firebase_performance_web` - `v0.1.8+9` + - `firebase_remote_config` - `v6.5.3` + - `firebase_remote_config_platform_interface` - `v3.0.3` + - `firebase_remote_config_web` - `v1.10.10` + - `firebase_storage` - `v13.4.3` + - `firebase_storage_platform_interface` - `v6.0.3` + - `firebase_storage_web` - `v3.11.9` + +--- + +#### `cloud_firestore` - `v6.6.0` + + - **FEAT**(firestore): add support for search in firestore pipeline ([#18312](https://github.com/firebase/flutterfire/issues/18312)). ([b3c835f7](https://github.com/firebase/flutterfire/commit/b3c835f7bae8684d4c98167d78a071d9ed88f980)) + +#### `cloud_firestore_web` - `v5.6.0` + + - **FEAT**(firestore): add support for search in firestore pipeline ([#18312](https://github.com/firebase/flutterfire/issues/18312)). ([b3c835f7](https://github.com/firebase/flutterfire/commit/b3c835f7bae8684d4c98167d78a071d9ed88f980)) + +#### `firebase_ai` - `v3.13.0` + + - **FIX**(ai): firebase_ai server template image inputs passed as InlineDataPart were serialized as normal generative inlineData ([#18350](https://github.com/firebase/flutterfire/issues/18350)). ([f3c53792](https://github.com/firebase/flutterfire/commit/f3c53792f1acf19ab1c2c7c3d157fca3a183b5d1)) + - **FEAT**(firebaseai): Add speech config and TTS sample page ([#18358](https://github.com/firebase/flutterfire/issues/18358)). ([0af51b50](https://github.com/firebase/flutterfire/commit/0af51b501603a611c7c6800efd9d98c478abab4d)) + - **FEAT**(ai): add language code support for SpeechConfig ([#18353](https://github.com/firebase/flutterfire/issues/18353)). ([3471afc7](https://github.com/firebase/flutterfire/commit/3471afc7ffa7bae58981683982d58d669ac71d50)) + - **FEAT**(ai): add mediaResolution parameter ([#18354](https://github.com/firebase/flutterfire/issues/18354)). ([79547569](https://github.com/firebase/flutterfire/commit/795475692384385a17511b295640ad6f8ab625f6)) + - **FEAT**(ai): add support for cancellable clients for in-flight requests ([#18349](https://github.com/firebase/flutterfire/issues/18349)). ([566cfed4](https://github.com/firebase/flutterfire/commit/566cfed42599318cf0f24eefc5d696223e46128c)) + +#### `firebase_analytics` - `v12.4.3` + + - **FIX**(analytics,iOS): update iOS dependency instructions for IDFA-free usage ([#18337](https://github.com/firebase/flutterfire/issues/18337)). ([c21fc77b](https://github.com/firebase/flutterfire/commit/c21fc77b68a87b9691fc1615454c5dac39dd4ed4)) + +#### `firebase_app_check` - `v0.4.5` + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + +#### `firebase_app_check_platform_interface` - `v0.4.1` + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + +#### `firebase_app_check_web` - `v0.2.5` + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + +#### `firebase_core` - `v4.11.0` + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + - **FEAT**(core): bump Firebase ios SDK to 12.15.0 ([#18375](https://github.com/firebase/flutterfire/issues/18375)). ([4d083764](https://github.com/firebase/flutterfire/commit/4d083764c3abd94d6e4590a170fbdaaa4b161202)) + - **FEAT**(core): bump Firebase android SDK to 34.15.0 ([#18374](https://github.com/firebase/flutterfire/issues/18374)). ([1cd3a0bd](https://github.com/firebase/flutterfire/commit/1cd3a0bd76fd594139356519fabee0e0d2b12f31)) + - **FEAT**(core): Add Recaptcha Site Key to FirebaseOptions ([#18334](https://github.com/firebase/flutterfire/issues/18334)). ([57be7027](https://github.com/firebase/flutterfire/commit/57be702778d34b9b7e86b40817d93acaec4c3ca4)) + +#### `firebase_core_platform_interface` - `v7.1.0` + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + - **FEAT**(core): Add Recaptcha Site Key to FirebaseOptions ([#18334](https://github.com/firebase/flutterfire/issues/18334)). ([57be7027](https://github.com/firebase/flutterfire/commit/57be702778d34b9b7e86b40817d93acaec4c3ca4)) + +#### `firebase_core_web` - `v3.9.0` + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + - **FEAT**(core): bump Firebase web SDK to 12.15.0 ([#18376](https://github.com/firebase/flutterfire/issues/18376)). ([22eb4d5d](https://github.com/firebase/flutterfire/commit/22eb4d5d0f3f14207e080e9c9fc8373052258ef4)) + - **FEAT**(core): Add Recaptcha Site Key to FirebaseOptions ([#18334](https://github.com/firebase/flutterfire/issues/18334)). ([57be7027](https://github.com/firebase/flutterfire/commit/57be702778d34b9b7e86b40817d93acaec4c3ca4)) + +#### `firebase_messaging` - `v16.4.0` + + - **FIX**(messaging,ios): fix a race condition that could happen when getting initial message ([#18352](https://github.com/firebase/flutterfire/issues/18352)). ([77396b81](https://github.com/firebase/flutterfire/commit/77396b81ae56943a38c23b429249b0b9cbd4bc21)) + - **FEAT**(messaging,ios): add support for actionIdentifier on iOS devices ([#18357](https://github.com/firebase/flutterfire/issues/18357)). ([d60af4d9](https://github.com/firebase/flutterfire/commit/d60af4d9e1345c113490e875c85bd9ac62dad935)) + +#### `firebase_messaging_platform_interface` - `v4.9.0` + + - **FEAT**(messaging,ios): add support for actionIdentifier on iOS devices ([#18357](https://github.com/firebase/flutterfire/issues/18357)). ([d60af4d9](https://github.com/firebase/flutterfire/commit/d60af4d9e1345c113490e875c85bd9ac62dad935)) + + +## 2026-06-01 - [BoM 4.15.0](https://github.com/firebase/flutterfire/blob/main/VERSIONS.md#flutter-bom-4150-2026-06-01) + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`cloud_firestore` - `v6.5.0`](#cloud_firestore---v650) + - [`cloud_firestore_web` - `v5.5.0`](#cloud_firestore_web---v550) + - [`firebase_auth` - `v6.5.2`](#firebase_auth---v652) + - [`firebase_auth_platform_interface` - `v9.0.2`](#firebase_auth_platform_interface---v902) + - [`firebase_core` - `v4.10.0`](#firebase_core---v4100) + - [`firebase_core_web` - `v3.8.0`](#firebase_core_web---v380) + - [`firebase_messaging` - `v16.3.0`](#firebase_messaging---v1630) + - [`firebase_messaging_platform_interface` - `v4.8.0`](#firebase_messaging_platform_interface---v480) + - [`firebase_messaging_web` - `v4.2.0`](#firebase_messaging_web---v420) + - [`firebase_ai` - `v3.12.2`](#firebase_ai---v3122) + - [`firebase_data_connect` - `v0.3.0+3`](#firebase_data_connect---v0303) + - [`firebase_auth_web` - `v6.2.2`](#firebase_auth_web---v622) + - [`_flutterfire_internals` - `v1.3.72`](#_flutterfire_internals---v1372) + - [`cloud_firestore_platform_interface` - `v8.0.2`](#cloud_firestore_platform_interface---v802) + - [`cloud_functions` - `v6.3.2`](#cloud_functions---v632) + - [`cloud_functions_platform_interface` - `v6.0.2`](#cloud_functions_platform_interface---v602) + - [`cloud_functions_web` - `v5.1.8`](#cloud_functions_web---v518) + - [`firebase_analytics` - `v12.4.2`](#firebase_analytics---v1242) + - [`firebase_analytics_platform_interface` - `v6.0.2`](#firebase_analytics_platform_interface---v602) + - [`firebase_analytics_web` - `v0.6.1+8`](#firebase_analytics_web---v0618) + - [`firebase_app_check` - `v0.4.4+2`](#firebase_app_check---v0442) + - [`firebase_app_check_platform_interface` - `v0.4.0+2`](#firebase_app_check_platform_interface---v0402) + - [`firebase_app_check_web` - `v0.2.4+3`](#firebase_app_check_web---v0243) + - [`firebase_app_installations` - `v0.4.2+3`](#firebase_app_installations---v0423) + - [`firebase_app_installations_platform_interface` - `v0.1.4+71`](#firebase_app_installations_platform_interface---v01471) + - [`firebase_app_installations_web` - `v0.1.7+8`](#firebase_app_installations_web---v0178) + - [`firebase_crashlytics` - `v5.2.3`](#firebase_crashlytics---v523) + - [`firebase_crashlytics_platform_interface` - `v3.8.23`](#firebase_crashlytics_platform_interface---v3823) + - [`firebase_database` - `v12.4.2`](#firebase_database---v1242) + - [`firebase_database_platform_interface` - `v0.4.0+2`](#firebase_database_platform_interface---v0402) + - [`firebase_database_web` - `v0.2.7+9`](#firebase_database_web---v0279) + - [`firebase_in_app_messaging` - `v0.9.2+3`](#firebase_in_app_messaging---v0923) + - [`firebase_in_app_messaging_platform_interface` - `v0.2.5+23`](#firebase_in_app_messaging_platform_interface---v02523) + - [`firebase_ml_model_downloader` - `v0.4.2+3`](#firebase_ml_model_downloader---v0423) + - [`firebase_ml_model_downloader_platform_interface` - `v0.1.5+23`](#firebase_ml_model_downloader_platform_interface---v01523) + - [`firebase_performance` - `v0.11.4+2`](#firebase_performance---v01142) + - [`firebase_performance_platform_interface` - `v0.2.0+2`](#firebase_performance_platform_interface---v0202) + - [`firebase_performance_web` - `v0.1.8+8`](#firebase_performance_web---v0188) + - [`firebase_remote_config` - `v6.5.2`](#firebase_remote_config---v652) + - [`firebase_remote_config_platform_interface` - `v3.0.2`](#firebase_remote_config_platform_interface---v302) + - [`firebase_remote_config_web` - `v1.10.9`](#firebase_remote_config_web---v1109) + - [`firebase_storage` - `v13.4.2`](#firebase_storage---v1342) + - [`firebase_storage_platform_interface` - `v6.0.2`](#firebase_storage_platform_interface---v602) + - [`firebase_storage_web` - `v3.11.8`](#firebase_storage_web---v3118) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `firebase_ai` - `v3.12.2` + - `firebase_data_connect` - `v0.3.0+3` + - `firebase_auth_web` - `v6.2.2` + - `_flutterfire_internals` - `v1.3.72` + - `cloud_firestore_platform_interface` - `v8.0.2` + - `cloud_functions` - `v6.3.2` + - `cloud_functions_platform_interface` - `v6.0.2` + - `cloud_functions_web` - `v5.1.8` + - `firebase_analytics` - `v12.4.2` + - `firebase_analytics_platform_interface` - `v6.0.2` + - `firebase_analytics_web` - `v0.6.1+8` + - `firebase_app_check` - `v0.4.4+2` + - `firebase_app_check_platform_interface` - `v0.4.0+2` + - `firebase_app_check_web` - `v0.2.4+3` + - `firebase_app_installations` - `v0.4.2+3` + - `firebase_app_installations_platform_interface` - `v0.1.4+71` + - `firebase_app_installations_web` - `v0.1.7+8` + - `firebase_crashlytics` - `v5.2.3` + - `firebase_crashlytics_platform_interface` - `v3.8.23` + - `firebase_database` - `v12.4.2` + - `firebase_database_platform_interface` - `v0.4.0+2` + - `firebase_database_web` - `v0.2.7+9` + - `firebase_in_app_messaging` - `v0.9.2+3` + - `firebase_in_app_messaging_platform_interface` - `v0.2.5+23` + - `firebase_ml_model_downloader` - `v0.4.2+3` + - `firebase_ml_model_downloader_platform_interface` - `v0.1.5+23` + - `firebase_performance` - `v0.11.4+2` + - `firebase_performance_platform_interface` - `v0.2.0+2` + - `firebase_performance_web` - `v0.1.8+8` + - `firebase_remote_config` - `v6.5.2` + - `firebase_remote_config_platform_interface` - `v3.0.2` + - `firebase_remote_config_web` - `v1.10.9` + - `firebase_storage` - `v13.4.2` + - `firebase_storage_platform_interface` - `v6.0.2` + - `firebase_storage_web` - `v3.11.8` + +--- + +#### `cloud_firestore` - `v6.5.0` + + - **FIX**(firestore,ios): add forceIndex parameter to collection source stage initializations ([#18332](https://github.com/firebase/flutterfire/issues/18332)). ([1bf50d2f](https://github.com/firebase/flutterfire/commit/1bf50d2f5bbdcac29797268632e2ed8b7e344c7d)) + - **FEAT**(firestore): add support for new array expressions ([#18283](https://github.com/firebase/flutterfire/issues/18283)). ([2293dee0](https://github.com/firebase/flutterfire/commit/2293dee00767cc6eebd57a340eb2b72bdab41d52)) + +#### `cloud_firestore_web` - `v5.5.0` + + - **FEAT**(firestore): add support for new array expressions ([#18283](https://github.com/firebase/flutterfire/issues/18283)). ([2293dee0](https://github.com/firebase/flutterfire/commit/2293dee00767cc6eebd57a340eb2b72bdab41d52)) + +#### `firebase_auth` - `v6.5.2` + + - **FIX**(auth,android): update token retrieval in PigeonParser to handle Number type correctly ([#18328](https://github.com/firebase/flutterfire/issues/18328)). ([3b77147b](https://github.com/firebase/flutterfire/commit/3b77147bc00bb19af5f4821436a1a4cdd8ff6791)) + - **DOCS**(auth): clarify behavior of password reset email with email enumeration protection enabled ([#18296](https://github.com/firebase/flutterfire/issues/18296)). ([0bcce87a](https://github.com/firebase/flutterfire/commit/0bcce87a17830797dae6ff3c1a8ba4ce210c2c0d)) + +#### `firebase_auth_platform_interface` - `v9.0.2` + + - **DOCS**(auth): clarify behavior of password reset email with email enumeration protection enabled ([#18296](https://github.com/firebase/flutterfire/issues/18296)). ([0bcce87a](https://github.com/firebase/flutterfire/commit/0bcce87a17830797dae6ff3c1a8ba4ce210c2c0d)) + +#### `firebase_core` - `v4.10.0` + + - **FEAT**(core): bump Firebase ios SDK to 12.14.0 ([#18330](https://github.com/firebase/flutterfire/issues/18330)). ([b1cfe745](https://github.com/firebase/flutterfire/commit/b1cfe745d221f09665943762c83cdd64684c6e6c)) + - **FEAT**(core): bump Firebase android SDK to 34.14.0 ([#18329](https://github.com/firebase/flutterfire/issues/18329)). ([1562eace](https://github.com/firebase/flutterfire/commit/1562eace5196227ad0058df9b5426950b0094f83)) + +#### `firebase_core_web` - `v3.8.0` + + - **FEAT**(core): bump Firebase web SDK to 12.14.0 ([#18331](https://github.com/firebase/flutterfire/issues/18331)). ([3f31a88a](https://github.com/firebase/flutterfire/commit/3f31a88ab6ad96914f427e292b919b6465cf4996)) + +#### `firebase_messaging` - `v16.3.0` + + - **FEAT**(messaging,web): add support for custom service worker script path in `getToken` method ([#18290](https://github.com/firebase/flutterfire/issues/18290)). ([b37722db](https://github.com/firebase/flutterfire/commit/b37722db13548aca57b3a24ba0f27b5de021be02)) + +#### `firebase_messaging_platform_interface` - `v4.8.0` + + - **FEAT**(messaging,web): add support for custom service worker script path in `getToken` method ([#18290](https://github.com/firebase/flutterfire/issues/18290)). ([b37722db](https://github.com/firebase/flutterfire/commit/b37722db13548aca57b3a24ba0f27b5de021be02)) + +#### `firebase_messaging_web` - `v4.2.0` + + - **FEAT**(messaging,web): add support for custom service worker script path in `getToken` method ([#18290](https://github.com/firebase/flutterfire/issues/18290)). ([b37722db](https://github.com/firebase/flutterfire/commit/b37722db13548aca57b3a24ba0f27b5de021be02)) + + +## 2026-05-14 - [BoM 4.14.0](https://github.com/firebase/flutterfire/blob/main/VERSIONS.md#flutter-bom-4140-2026-05-14) + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`firebase_core` - `v4.9.0`](#firebase_core---v490) + - [`firebase_core_platform_interface` - `v7.0.1`](#firebase_core_platform_interface---v701) + - [`firebase_core_web` - `v3.7.0`](#firebase_core_web---v370) + - [`_flutterfire_internals` - `v1.3.71`](#_flutterfire_internals---v1371) + - [`cloud_firestore` - `v6.4.1`](#cloud_firestore---v641) + - [`cloud_firestore_platform_interface` - `v8.0.1`](#cloud_firestore_platform_interface---v801) + - [`cloud_firestore_web` - `v5.4.1`](#cloud_firestore_web---v541) + - [`cloud_functions` - `v6.3.1`](#cloud_functions---v631) + - [`cloud_functions_platform_interface` - `v6.0.1`](#cloud_functions_platform_interface---v601) + - [`cloud_functions_web` - `v5.1.7`](#cloud_functions_web---v517) + - [`firebase_ai` - `v3.12.1`](#firebase_ai---v3121) + - [`firebase_analytics` - `v12.4.1`](#firebase_analytics---v1241) + - [`firebase_analytics_platform_interface` - `v6.0.1`](#firebase_analytics_platform_interface---v601) + - [`firebase_analytics_web` - `v0.6.1+7`](#firebase_analytics_web---v0617) + - [`firebase_app_check` - `v0.4.4+1`](#firebase_app_check---v0441) + - [`firebase_app_check_platform_interface` - `v0.4.0+1`](#firebase_app_check_platform_interface---v0401) + - [`firebase_app_check_web` - `v0.2.4+2`](#firebase_app_check_web---v0242) + - [`firebase_app_installations` - `v0.4.2+2`](#firebase_app_installations---v0422) + - [`firebase_app_installations_platform_interface` - `v0.1.4+70`](#firebase_app_installations_platform_interface---v01470) + - [`firebase_app_installations_web` - `v0.1.7+7`](#firebase_app_installations_web---v0177) + - [`firebase_auth` - `v6.5.1`](#firebase_auth---v651) + - [`firebase_auth_platform_interface` - `v9.0.1`](#firebase_auth_platform_interface---v901) + - [`firebase_auth_web` - `v6.2.1`](#firebase_auth_web---v621) + - [`firebase_crashlytics` - `v5.2.2`](#firebase_crashlytics---v522) + - [`firebase_crashlytics_platform_interface` - `v3.8.22`](#firebase_crashlytics_platform_interface---v3822) + - [`firebase_data_connect` - `v0.3.0+2`](#firebase_data_connect---v0302) + - [`firebase_database` - `v12.4.1`](#firebase_database---v1241) + - [`firebase_database_platform_interface` - `v0.4.0+1`](#firebase_database_platform_interface---v0401) + - [`firebase_database_web` - `v0.2.7+8`](#firebase_database_web---v0278) + - [`firebase_in_app_messaging` - `v0.9.2+2`](#firebase_in_app_messaging---v0922) + - [`firebase_in_app_messaging_platform_interface` - `v0.2.5+22`](#firebase_in_app_messaging_platform_interface---v02522) + - [`firebase_messaging` - `v16.2.2`](#firebase_messaging---v1622) + - [`firebase_messaging_platform_interface` - `v4.7.11`](#firebase_messaging_platform_interface---v4711) + - [`firebase_messaging_web` - `v4.1.7`](#firebase_messaging_web---v417) + - [`firebase_ml_model_downloader` - `v0.4.2+2`](#firebase_ml_model_downloader---v0422) + - [`firebase_ml_model_downloader_platform_interface` - `v0.1.5+22`](#firebase_ml_model_downloader_platform_interface---v01522) + - [`firebase_performance` - `v0.11.4+1`](#firebase_performance---v01141) + - [`firebase_performance_platform_interface` - `v0.2.0+1`](#firebase_performance_platform_interface---v0201) + - [`firebase_performance_web` - `v0.1.8+7`](#firebase_performance_web---v0187) + - [`firebase_remote_config` - `v6.5.1`](#firebase_remote_config---v651) + - [`firebase_remote_config_platform_interface` - `v3.0.1`](#firebase_remote_config_platform_interface---v301) + - [`firebase_remote_config_web` - `v1.10.8`](#firebase_remote_config_web---v1108) + - [`firebase_storage` - `v13.4.1`](#firebase_storage---v1341) + - [`firebase_storage_platform_interface` - `v6.0.1`](#firebase_storage_platform_interface---v601) + - [`firebase_storage_web` - `v3.11.7`](#firebase_storage_web---v3117) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `_flutterfire_internals` - `v1.3.71` + - `cloud_firestore` - `v6.4.1` + - `cloud_firestore_platform_interface` - `v8.0.1` + - `cloud_firestore_web` - `v5.4.1` + - `cloud_functions` - `v6.3.1` + - `cloud_functions_platform_interface` - `v6.0.1` + - `cloud_functions_web` - `v5.1.7` + - `firebase_ai` - `v3.12.1` + - `firebase_analytics` - `v12.4.1` + - `firebase_analytics_platform_interface` - `v6.0.1` + - `firebase_analytics_web` - `v0.6.1+7` + - `firebase_app_check` - `v0.4.4+1` + - `firebase_app_check_platform_interface` - `v0.4.0+1` + - `firebase_app_check_web` - `v0.2.4+2` + - `firebase_app_installations` - `v0.4.2+2` + - `firebase_app_installations_platform_interface` - `v0.1.4+70` + - `firebase_app_installations_web` - `v0.1.7+7` + - `firebase_auth` - `v6.5.1` + - `firebase_auth_platform_interface` - `v9.0.1` + - `firebase_auth_web` - `v6.2.1` + - `firebase_crashlytics` - `v5.2.2` + - `firebase_crashlytics_platform_interface` - `v3.8.22` + - `firebase_data_connect` - `v0.3.0+2` + - `firebase_database` - `v12.4.1` + - `firebase_database_platform_interface` - `v0.4.0+1` + - `firebase_database_web` - `v0.2.7+8` + - `firebase_in_app_messaging` - `v0.9.2+2` + - `firebase_in_app_messaging_platform_interface` - `v0.2.5+22` + - `firebase_messaging` - `v16.2.2` + - `firebase_messaging_platform_interface` - `v4.7.11` + - `firebase_messaging_web` - `v4.1.7` + - `firebase_ml_model_downloader` - `v0.4.2+2` + - `firebase_ml_model_downloader_platform_interface` - `v0.1.5+22` + - `firebase_performance` - `v0.11.4+1` + - `firebase_performance_platform_interface` - `v0.2.0+1` + - `firebase_performance_web` - `v0.1.8+7` + - `firebase_remote_config` - `v6.5.1` + - `firebase_remote_config_platform_interface` - `v3.0.1` + - `firebase_remote_config_web` - `v1.10.8` + - `firebase_storage` - `v13.4.1` + - `firebase_storage_platform_interface` - `v6.0.1` + - `firebase_storage_web` - `v3.11.7` + +--- + +#### `firebase_core` - `v4.9.0` + + - **FIX**(core,iOS): use namespaced iOS Pigeon header import ([#18281](https://github.com/firebase/flutterfire/issues/18281)). ([7c1257e7](https://github.com/firebase/flutterfire/commit/7c1257e7295f9ba67f3f5820493f105a14d34d52)) + - **FEAT**: bump Firebase iOS SDK to 12.13.0 ([#18273](https://github.com/firebase/flutterfire/issues/18273)). ([78e10f02](https://github.com/firebase/flutterfire/commit/78e10f0222f4e23c96b636c63c29935ba5aa82e6)) + - **FEAT**: bump Firebase android SDK to 34.13.0 ([#18272](https://github.com/firebase/flutterfire/issues/18272)). ([d10e0ffa](https://github.com/firebase/flutterfire/commit/d10e0ffa2980a21a5899dbe67952fc772a3c6c01)) + +#### `firebase_core_platform_interface` - `v7.0.1` + + - **FIX**(core,iOS): use namespaced iOS Pigeon header import ([#18281](https://github.com/firebase/flutterfire/issues/18281)). ([7c1257e7](https://github.com/firebase/flutterfire/commit/7c1257e7295f9ba67f3f5820493f105a14d34d52)) + +#### `firebase_core_web` - `v3.7.0` + + - **FEAT**: bump Firebase JS SDK to 12.13.0 ([#18274](https://github.com/firebase/flutterfire/issues/18274)). ([bb8ad546](https://github.com/firebase/flutterfire/commit/bb8ad546f114146b6e1cd26c3296825e2964745d)) + + +## 2026-05-11 - [BoM 4.13.0](https://github.com/firebase/flutterfire/blob/main/VERSIONS.md#flutter-bom-4130-2026-05-11) + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`cloud_firestore_platform_interface` - `v8.0.0`](#cloud_firestore_platform_interface---v800) + - [`cloud_functions_platform_interface` - `v6.0.0`](#cloud_functions_platform_interface---v600) + - [`firebase_analytics_platform_interface` - `v6.0.0`](#firebase_analytics_platform_interface---v600) + - [`firebase_app_check_platform_interface` - `v0.4.0`](#firebase_app_check_platform_interface---v040) + - [`firebase_auth_platform_interface` - `v9.0.0`](#firebase_auth_platform_interface---v900) + - [`firebase_core_platform_interface` - `v7.0.0`](#firebase_core_platform_interface---v700) + - [`firebase_database_platform_interface` - `v0.4.0`](#firebase_database_platform_interface---v040) + - [`firebase_performance_platform_interface` - `v0.2.0`](#firebase_performance_platform_interface---v020) + - [`firebase_remote_config_platform_interface` - `v3.0.0`](#firebase_remote_config_platform_interface---v300) + - [`firebase_storage_platform_interface` - `v6.0.0`](#firebase_storage_platform_interface---v600) + - [`_flutterfire_internals` - `v1.3.70`](#_flutterfire_internals---v1370) + - [`cloud_firestore` - `v6.4.0`](#cloud_firestore---v640) + - [`cloud_firestore_web` - `v5.4.0`](#cloud_firestore_web---v540) + - [`cloud_functions` - `v6.3.0`](#cloud_functions---v630) + - [`cloud_functions_web` - `v5.1.6`](#cloud_functions_web---v516) + - [`firebase_ai` - `v3.12.0`](#firebase_ai---v3120) + - [`firebase_analytics` - `v12.4.0`](#firebase_analytics---v1240) + - [`firebase_analytics_web` - `v0.6.1+6`](#firebase_analytics_web---v0616) + - [`firebase_app_check` - `v0.4.4`](#firebase_app_check---v044) + - [`firebase_app_check_web` - `v0.2.4+1`](#firebase_app_check_web---v0241) + - [`firebase_app_installations` - `v0.4.2+1`](#firebase_app_installations---v0421) + - [`firebase_app_installations_platform_interface` - `v0.1.4+69`](#firebase_app_installations_platform_interface---v01469) + - [`firebase_app_installations_web` - `v0.1.7+6`](#firebase_app_installations_web---v0176) + - [`firebase_auth` - `v6.5.0`](#firebase_auth---v650) + - [`firebase_auth_web` - `v6.2.0`](#firebase_auth_web---v620) + - [`firebase_core` - `v4.8.0`](#firebase_core---v480) + - [`firebase_core_web` - `v3.6.1`](#firebase_core_web---v361) + - [`firebase_crashlytics` - `v5.2.1`](#firebase_crashlytics---v521) + - [`firebase_crashlytics_platform_interface` - `v3.8.21`](#firebase_crashlytics_platform_interface---v3821) + - [`firebase_data_connect` - `v0.3.0+1`](#firebase_data_connect---v0301) + - [`firebase_database` - `v12.4.0`](#firebase_database---v1240) + - [`firebase_database_web` - `v0.2.7+7`](#firebase_database_web---v0277) + - [`firebase_in_app_messaging` - `v0.9.2+1`](#firebase_in_app_messaging---v0921) + - [`firebase_in_app_messaging_platform_interface` - `v0.2.5+21`](#firebase_in_app_messaging_platform_interface---v02521) + - [`firebase_messaging` - `v16.2.1`](#firebase_messaging---v1621) + - [`firebase_messaging_platform_interface` - `v4.7.10`](#firebase_messaging_platform_interface---v4710) + - [`firebase_messaging_web` - `v4.1.6`](#firebase_messaging_web---v416) + - [`firebase_ml_model_downloader` - `v0.4.2+1`](#firebase_ml_model_downloader---v0421) + - [`firebase_ml_model_downloader_platform_interface` - `v0.1.5+21`](#firebase_ml_model_downloader_platform_interface---v01521) + - [`firebase_performance` - `v0.11.4`](#firebase_performance---v0114) + - [`firebase_performance_web` - `v0.1.8+6`](#firebase_performance_web---v0186) + - [`firebase_remote_config` - `v6.5.0`](#firebase_remote_config---v650) + - [`firebase_remote_config_web` - `v1.10.7`](#firebase_remote_config_web---v1107) + - [`firebase_storage` - `v13.4.0`](#firebase_storage---v1340) + - [`firebase_storage_web` - `v3.11.6`](#firebase_storage_web---v3116) + +--- + +#### `cloud_firestore_platform_interface` - `v8.0.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `cloud_functions_platform_interface` - `v6.0.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_analytics_platform_interface` - `v6.0.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_app_check_platform_interface` - `v0.4.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FIX**(app_check): fix an issue with debug token that would sometime not be passed properly ([#18258](https://github.com/firebase/flutterfire/issues/18258)). ([b0bc6e8f](https://github.com/firebase/flutterfire/commit/b0bc6e8f0e92aed2f3da99725eff85b3cf358282)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_auth_platform_interface` - `v9.0.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + - **FEAT**(auth,android): add revokeAccessToken support for Android ([#18206](https://github.com/firebase/flutterfire/issues/18206)) ([#18207](https://github.com/firebase/flutterfire/issues/18207)). ([7e0a2227](https://github.com/firebase/flutterfire/commit/7e0a222700178a57d064c27b4ef62cefdda1e253)) + +#### `firebase_core_platform_interface` - `v7.0.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_database_platform_interface` - `v0.4.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_performance_platform_interface` - `v0.2.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_remote_config_platform_interface` - `v3.0.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_storage_platform_interface` - `v6.0.0` + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `_flutterfire_internals` - `v1.3.70` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `cloud_firestore` - `v6.4.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(firestore,web): update Timestamp handling in jsify and EncodeUtility ([#18264](https://github.com/firebase/flutterfire/issues/18264)). ([9783a448](https://github.com/firebase/flutterfire/commit/9783a448ff532568a5e46ecb927e7b1bc77a164c)) + - **FIX**(firestore,windows): fix CI issue ([#18218](https://github.com/firebase/flutterfire/issues/18218)). ([b9c8a9e2](https://github.com/firebase/flutterfire/commit/b9c8a9e2993187c782c94398136aac9bf5418061)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `cloud_firestore_web` - `v5.4.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(firestore,web): update Timestamp handling in jsify and EncodeUtility ([#18264](https://github.com/firebase/flutterfire/issues/18264)). ([9783a448](https://github.com/firebase/flutterfire/commit/9783a448ff532568a5e46ecb927e7b1bc77a164c)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `cloud_functions` - `v6.3.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `cloud_functions_web` - `v5.1.6` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(functions,web): dartify results from cloud function stream ([#18212](https://github.com/firebase/flutterfire/issues/18212)). ([9f32c614](https://github.com/firebase/flutterfire/commit/9f32c614a9fee53ceebc5540d91c76ba4fd91d8b)) + +#### `firebase_ai` - `v3.12.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**(firebaseai): ImageConfig and FinishReasons ([#18180](https://github.com/firebase/flutterfire/issues/18180)). ([7d1a8b1d](https://github.com/firebase/flutterfire/commit/7d1a8b1db4c5f585ba38d46df37330d3d17de774)) + - **FEAT**(firebaseai): live session resumption ([#18038](https://github.com/firebase/flutterfire/issues/18038)). ([829fd949](https://github.com/firebase/flutterfire/commit/829fd949bf1644ad9ce73dfb8ed416d89654f989)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + - **FEAT**(ai): add missing enum types ([#18198](https://github.com/firebase/flutterfire/issues/18198)). ([889af7c7](https://github.com/firebase/flutterfire/commit/889af7c7b8f7705a55cdd90e39d306858a2e9fd4)) + - **FEAT**(firebaseai): add Google Maps Grounding support ([#18144](https://github.com/firebase/flutterfire/issues/18144)). ([385d9337](https://github.com/firebase/flutterfire/commit/385d93372f749843ee3d8ac409a878fa149ba7ed)) + - **FEAT**(ai): add unexpectedToolCall finish reason and corresponding tests ([#18188](https://github.com/firebase/flutterfire/issues/18188)). ([27852720](https://github.com/firebase/flutterfire/commit/278527207a4fb35a5854dd3f0a9405da9f80877c)) + +#### `firebase_analytics` - `v12.4.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_analytics_web` - `v0.6.1+6` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_app_check` - `v0.4.4` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + +#### `firebase_app_check_web` - `v0.2.4+1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_app_installations` - `v0.4.2+1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_app_installations_platform_interface` - `v0.1.4+69` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_app_installations_web` - `v0.1.7+6` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_auth` - `v6.5.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FIX**(auth,apple): remove incorrect paths in Package.swift files search paths ([#18239](https://github.com/firebase/flutterfire/issues/18239)). ([7c2fa5b8](https://github.com/firebase/flutterfire/commit/7c2fa5b83201f2f68e031476dc37ad41809215f2)) + - **FIX**(auth,iOS): update import path for autogenerated messages ([#18227](https://github.com/firebase/flutterfire/issues/18227)). ([4351179d](https://github.com/firebase/flutterfire/commit/4351179d357eeab6b23ec66f45d558c02d3fde69)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + - **FEAT**(auth,android): add revokeAccessToken support for Android ([#18206](https://github.com/firebase/flutterfire/issues/18206)) ([#18207](https://github.com/firebase/flutterfire/issues/18207)). ([7e0a2227](https://github.com/firebase/flutterfire/commit/7e0a222700178a57d064c27b4ef62cefdda1e253)) + +#### `firebase_auth_web` - `v6.2.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_core` - `v4.8.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_core_web` - `v3.6.1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_crashlytics` - `v5.2.1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_crashlytics_platform_interface` - `v3.8.21` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_data_connect` - `v0.3.0+1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FIX**(fdc): block reconnecting if there are no active subscribers or pending unary calls. ([#18265](https://github.com/firebase/flutterfire/issues/18265)). ([330bbb83](https://github.com/firebase/flutterfire/commit/330bbb83399f37911f938a59dc660ed84a0c83a3)) + - **FIX**(fdc): remove unused logs ([#18197](https://github.com/firebase/flutterfire/issues/18197)). ([4c17ca87](https://github.com/firebase/flutterfire/commit/4c17ca870a78ae6afeaad6006ca68e7999711ffd)) + +#### `firebase_database` - `v12.4.0` + + - **REFACTOR**(database,android): simplify query handling by extracting queryFromModifiers method ([#18221](https://github.com/firebase/flutterfire/issues/18221)). ([65d9bb71](https://github.com/firebase/flutterfire/commit/65d9bb7104f59de82010e3e82fd0ddddbf9a2e23)) + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(database,android): fix a regression where rapidly opening and closing query streams on the same path could throw ([#18262](https://github.com/firebase/flutterfire/issues/18262)). ([e23347b6](https://github.com/firebase/flutterfire/commit/e23347b6ae96d2174c4c2b93fd60f40d31a221c7)) + - **FIX**(database,android): fix an issue where setPersistenceEnabled needed to be called first ([#18259](https://github.com/firebase/flutterfire/issues/18259)). ([11bdedfb](https://github.com/firebase/flutterfire/commit/11bdedfb356d2c84e352e26abfc79de4c5653089)) + - **FIX**(database): fix a regression with database localEvents handling ([#18257](https://github.com/firebase/flutterfire/issues/18257)). ([40fd2904](https://github.com/firebase/flutterfire/commit/40fd2904e4634d9257241c1c2e779aa5bfc61624)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_database_web` - `v0.2.7+7` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_in_app_messaging` - `v0.9.2+1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_in_app_messaging_platform_interface` - `v0.2.5+21` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_messaging` - `v16.2.1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(messaging,android): fix call race that could happen when using requestPermission ([#18256](https://github.com/firebase/flutterfire/issues/18256)). ([57d4c3d0](https://github.com/firebase/flutterfire/commit/57d4c3d050c6a9252390de6cac91a0ca1d5461e3)) + +#### `firebase_messaging_platform_interface` - `v4.7.10` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_messaging_web` - `v4.1.6` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_ml_model_downloader` - `v0.4.2+1` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_ml_model_downloader_platform_interface` - `v0.1.5+21` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_performance` - `v0.11.4` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_performance_web` - `v0.1.8+6` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_remote_config` - `v6.5.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_remote_config_web` - `v1.10.7` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +#### `firebase_storage` - `v13.4.0` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(storage,android): fix an issue that could happen when app would get detached from the engine ([#18255](https://github.com/firebase/flutterfire/issues/18255)). ([2771f550](https://github.com/firebase/flutterfire/commit/2771f5505ff0a53cc1bfb41afec3a0eb8781b8f8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +#### `firebase_storage_web` - `v3.11.6` + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + + +## 2026-04-13 - [BoM 4.12.0](https://github.com/firebase/flutterfire/blob/main/VERSIONS.md#flutter-bom-4120-2026-04-13) + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`firebase_app_check_platform_interface` - `v0.3.0`](#firebase_app_check_platform_interface---v030) + - [`firebase_data_connect` - `v0.3.0`](#firebase_data_connect---v030) + - [`_flutterfire_internals` - `v1.3.69`](#_flutterfire_internals---v1369) + - [`cloud_firestore` - `v6.3.0`](#cloud_firestore---v630) + - [`cloud_firestore_platform_interface` - `v7.2.0`](#cloud_firestore_platform_interface---v720) + - [`cloud_firestore_web` - `v5.3.0`](#cloud_firestore_web---v530) + - [`cloud_functions` - `v6.2.0`](#cloud_functions---v620) + - [`firebase_ai` - `v3.11.0`](#firebase_ai---v3110) + - [`firebase_analytics` - `v12.3.0`](#firebase_analytics---v1230) + - [`firebase_app_check` - `v0.4.3`](#firebase_app_check---v043) + - [`firebase_app_check_web` - `v0.2.4`](#firebase_app_check_web---v024) + - [`firebase_app_installations` - `v0.4.2`](#firebase_app_installations---v042) + - [`firebase_auth` - `v6.4.0`](#firebase_auth---v640) + - [`firebase_core` - `v4.7.0`](#firebase_core---v470) + - [`firebase_core_web` - `v3.6.0`](#firebase_core_web---v360) + - [`firebase_crashlytics` - `v5.2.0`](#firebase_crashlytics---v520) + - [`firebase_database` - `v12.3.0`](#firebase_database---v1230) + - [`firebase_in_app_messaging` - `v0.9.2`](#firebase_in_app_messaging---v092) + - [`firebase_messaging` - `v16.2.0`](#firebase_messaging---v1620) + - [`firebase_ml_model_downloader` - `v0.4.2`](#firebase_ml_model_downloader---v042) + - [`firebase_performance` - `v0.11.3`](#firebase_performance---v0113) + - [`firebase_remote_config` - `v6.4.0`](#firebase_remote_config---v640) + - [`firebase_storage` - `v13.3.0`](#firebase_storage---v1330) + - [`firebase_remote_config_web` - `v1.10.6`](#firebase_remote_config_web---v1106) + - [`firebase_in_app_messaging_platform_interface` - `v0.2.5+20`](#firebase_in_app_messaging_platform_interface---v02520) + - [`firebase_remote_config_platform_interface` - `v2.1.2`](#firebase_remote_config_platform_interface---v212) + - [`firebase_auth_platform_interface` - `v8.1.9`](#firebase_auth_platform_interface---v819) + - [`firebase_crashlytics_platform_interface` - `v3.8.20`](#firebase_crashlytics_platform_interface---v3820) + - [`firebase_messaging_web` - `v4.1.5`](#firebase_messaging_web---v415) + - [`firebase_messaging_platform_interface` - `v4.7.9`](#firebase_messaging_platform_interface---v479) + - [`firebase_app_installations_platform_interface` - `v0.1.4+68`](#firebase_app_installations_platform_interface---v01468) + - [`firebase_database_platform_interface` - `v0.3.1+1`](#firebase_database_platform_interface---v0311) + - [`firebase_analytics_platform_interface` - `v5.1.1`](#firebase_analytics_platform_interface---v511) + - [`firebase_app_installations_web` - `v0.1.7+5`](#firebase_app_installations_web---v0175) + - [`firebase_analytics_web` - `v0.6.1+5`](#firebase_analytics_web---v0615) + - [`firebase_performance_platform_interface` - `v0.1.6+7`](#firebase_performance_platform_interface---v0167) + - [`firebase_performance_web` - `v0.1.8+5`](#firebase_performance_web---v0185) + - [`firebase_storage_platform_interface` - `v5.2.20`](#firebase_storage_platform_interface---v5220) + - [`firebase_storage_web` - `v3.11.5`](#firebase_storage_web---v3115) + - [`firebase_auth_web` - `v6.1.5`](#firebase_auth_web---v615) + - [`firebase_database_web` - `v0.2.7+6`](#firebase_database_web---v0276) + - [`firebase_ml_model_downloader_platform_interface` - `v0.1.5+20`](#firebase_ml_model_downloader_platform_interface---v01520) + - [`cloud_functions_web` - `v5.1.5`](#cloud_functions_web---v515) + - [`cloud_functions_platform_interface` - `v5.8.12`](#cloud_functions_platform_interface---v5812) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `firebase_remote_config_web` - `v1.10.6` + - `firebase_in_app_messaging_platform_interface` - `v0.2.5+20` + - `firebase_remote_config_platform_interface` - `v2.1.2` + - `firebase_auth_platform_interface` - `v8.1.9` + - `firebase_crashlytics_platform_interface` - `v3.8.20` + - `firebase_messaging_web` - `v4.1.5` + - `firebase_messaging_platform_interface` - `v4.7.9` + - `firebase_app_installations_platform_interface` - `v0.1.4+68` + - `firebase_database_platform_interface` - `v0.3.1+1` + - `firebase_analytics_platform_interface` - `v5.1.1` + - `firebase_app_installations_web` - `v0.1.7+5` + - `firebase_analytics_web` - `v0.6.1+5` + - `firebase_performance_platform_interface` - `v0.1.6+7` + - `firebase_performance_web` - `v0.1.8+5` + - `firebase_storage_platform_interface` - `v5.2.20` + - `firebase_storage_web` - `v3.11.5` + - `firebase_auth_web` - `v6.1.5` + - `firebase_database_web` - `v0.2.7+6` + - `firebase_ml_model_downloader_platform_interface` - `v0.1.5+20` + - `cloud_functions_web` - `v5.1.5` + - `cloud_functions_platform_interface` - `v5.8.12` + +--- + +#### `firebase_app_check_platform_interface` - `v0.3.0` + + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + +#### `firebase_data_connect` - `v0.3.0` + + - **FEAT**(fdc): Streaming implementation for data connect ([#18174](https://github.com/firebase/flutterfire/issues/18174)). ([6ce6f6b2](https://github.com/firebase/flutterfire/commit/6ce6f6b2369b9d43e69b24b284d8ef816c430e31)) + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + +#### `_flutterfire_internals` - `v1.3.69` + + - **FIX**: improve error handling in _firebaseExceptionFromCoreFirebaseError ([#18177](https://github.com/firebase/flutterfire/issues/18177)). ([3c29048a](https://github.com/firebase/flutterfire/commit/3c29048a859b62f3f224b1fa3c8db61f78f63374)) + +#### `cloud_firestore` - `v6.3.0` + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `cloud_firestore_platform_interface` - `v7.2.0` + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + +#### `cloud_firestore_web` - `v5.3.0` + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + +#### `cloud_functions` - `v6.2.0` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_ai` - `v3.11.0` + + - **FEAT**(firebaseai): server prompt chat and function calling ([#17972](https://github.com/firebase/flutterfire/issues/17972)). ([4b8f2288](https://github.com/firebase/flutterfire/commit/4b8f22889808c0a55f4fc2abc52c72aa2d932379)) + - **FEAT**(firebaseai): add spm support for firebase_ai, and update example to running with spm ([#18159](https://github.com/firebase/flutterfire/issues/18159)). ([bb1e04f8](https://github.com/firebase/flutterfire/commit/bb1e04f8cd0f29cad3913af7bcf40744ffb2515a)) + - **FEAT**(firebaseai): deprecate imagen ([#18148](https://github.com/firebase/flutterfire/issues/18148)). ([99450317](https://github.com/firebase/flutterfire/commit/99450317d83f7b77ab192aed7071432785337789)) + - **FEAT**(firebaseai): Add ability for Schema class to export to json schema ([#18131](https://github.com/firebase/flutterfire/issues/18131)). ([5818a33c](https://github.com/firebase/flutterfire/commit/5818a33c060f775b4a00e11ad5ee04b71e939dad)) + +#### `firebase_analytics` - `v12.3.0` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_app_check` - `v0.4.3` + + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_app_check_web` - `v0.2.4` + + - **FIX**(app_check,web): fix an error that could occur when refreshing a token ([#18135](https://github.com/firebase/flutterfire/issues/18135)). ([6998e512](https://github.com/firebase/flutterfire/commit/6998e512ea5404a20ad81a0306aafaa607babc2a)) + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + +#### `firebase_app_installations` - `v0.4.2` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_auth` - `v6.4.0` + + - **FIX**(auth,ios): serialize Sign in with Apple to prevent crash on overlapping requests ([#18172](https://github.com/firebase/flutterfire/issues/18172)). ([752cbcaa](https://github.com/firebase/flutterfire/commit/752cbcaa57f887a8fea3bda728bb8482290fa049)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_core` - `v4.7.0` + + - **FEAT**(core): bump Firebase Android SDK to 34.12.0 ([#18185](https://github.com/firebase/flutterfire/issues/18185)). ([346a048f](https://github.com/firebase/flutterfire/commit/346a048f098090e6848fdd0f61a8bf7d01394676)) + - **FEAT**: bump Firebase iOS SDK to 12.12.0 ([#18187](https://github.com/firebase/flutterfire/issues/18187)). ([cc063bd9](https://github.com/firebase/flutterfire/commit/cc063bd9df1c59dd3bb8c25d067f8655bc268523)) + - **FEAT**: bump iOS SDK to version 12.11.0 ([#18161](https://github.com/firebase/flutterfire/issues/18161)). ([2664b2c2](https://github.com/firebase/flutterfire/commit/2664b2c2dab4d0147461ce4d3f7862267e880542)) + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + - **FEAT**: bump Firebase android SDK to 34.11.0 ([#18146](https://github.com/firebase/flutterfire/issues/18146)). ([2b50061a](https://github.com/firebase/flutterfire/commit/2b50061a689634957efba8bd17c196dd548a08a2)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_core_web` - `v3.6.0` + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + - **FEAT**: bump JS SDK to version 12.12.0 ([#18186](https://github.com/firebase/flutterfire/issues/18186)). ([3d943ed4](https://github.com/firebase/flutterfire/commit/3d943ed4154eb61617746825fc5c1c90f1e73d88)) + - **FEAT**: bump JS SDK to version 12.11.0 ([#18160](https://github.com/firebase/flutterfire/issues/18160)). ([b3ab0003](https://github.com/firebase/flutterfire/commit/b3ab00036c70debca59414ea236c5012fb841a63)) + +#### `firebase_crashlytics` - `v5.2.0` + + - **FIX**(crashlytics,android): fix an issue with deobfuscating flavored builds ([#18085](https://github.com/firebase/flutterfire/issues/18085)). ([55a7f6ff](https://github.com/firebase/flutterfire/commit/55a7f6ff17940487e29d8bc78779ca4cfce24b0c)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_database` - `v12.3.0` + + - **FEAT**(database,android): fix order issue ([#18142](https://github.com/firebase/flutterfire/issues/18142)). ([5dd661cb](https://github.com/firebase/flutterfire/commit/5dd661cb7b9efa9e02c1bc9233222860be8be7bd)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_in_app_messaging` - `v0.9.2` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_messaging` - `v16.2.0` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_ml_model_downloader` - `v0.4.2` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_performance` - `v0.11.3` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_remote_config` - `v6.4.0` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + +#### `firebase_storage` - `v13.3.0` + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + + ## 2026-03-23 - [BoM 4.11.0](https://github.com/firebase/flutterfire/blob/main/VERSIONS.md#flutter-bom-4110-2026-03-23) ### Changes diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 000000000000..2f36c8befc58 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,173 @@ +# AI Rules for Flutter + +You are an expert Flutter and Dart developer. Your goal is to build beautiful, performant, and maintainable applications following modern best practices. + +## Interaction Guidelines +* **User Persona:** Assume the user is familiar with programming concepts but may be new to Dart. +* **Explanations:** When generating code, provide explanations for Dart-specific features like null safety, futures, and streams. +* **Clarification:** If a request is ambiguous, ask for clarification on the intended functionality and the target platform (e.g., command-line, web, server). +* **Dependencies:** When suggesting new dependencies from `pub.dev`, explain their benefits. Use `pub_dev_search` if available. +* **Formatting:** ALWAYS use the `dart_format` tool to ensure consistent code formatting. +* **Fixes:** Use the `dart_fix` tool to automatically fix many common errors. +* **Linting:** Use the Dart linter with `flutter_lints` to catch common issues. + +## Flutter Style Guide +* **SOLID Principles:** Apply SOLID principles throughout the codebase. +* **Concise and Declarative:** Write concise, modern, technical Dart code. Prefer functional and declarative patterns. +* **Composition over Inheritance:** Favor composition for building complex widgets and logic. +* **Immutability:** Prefer immutable data structures. Widgets (especially `StatelessWidget`) should be immutable. +* **State Management:** Separate ephemeral state and app state. Use a state management solution for app state. +* **Widgets are for UI:** Everything in Flutter's UI is a widget. Compose complex UIs from smaller, reusable widgets. + +## Package Management +* **Pub Tool:** Use `pub` or `flutter pub add`. +* **Dev Dependencies:** Use `flutter pub add dev:`. +* **Overrides:** Use `flutter pub add override::`. +* **Removal:** `dart pub remove `. + +## Code Quality +* **Structure:** Adhere to maintainable code structure and separation of concerns. +* **Naming:** Avoid abbreviations. Use `PascalCase` (classes), `camelCase` (members), `snake_case` (files). +* **Conciseness:** Functions should be short (<20 lines) and single-purpose. +* **Error Handling:** Anticipate and handle potential errors. Don't let code fail silently. +* **Logging:** Use `dart:developer` `log` instead of `print`. + +## Dart Best Practices +* **Effective Dart:** Follow official guidelines. +* **Async/Await:** Use `Future`, `async`, `await` for operations. Use `Stream` for events. +* **Null Safety:** Write sound null-safe code. Avoid `!` operator unless guaranteed. +* **Pattern Matching:** Use switch expressions and pattern matching. +* **Records:** Use records for multiple return values. +* **Exception Handling:** Use custom exceptions for specific situations. +* **Arrow Functions:** Use `=>` for one-line functions. + +## Flutter Best Practices +* **Immutability:** Widgets are immutable. Rebuild, don't mutate. +* **Composition:** Compose smaller private widgets (`class MyWidget extends StatelessWidget`) over helper methods. +* **Lists:** Use `ListView.builder` or `SliverList` for performance. +* **Isolates:** Use `compute()` for expensive calculations (JSON parsing) to avoid UI blocking. +* **Const:** Use `const` constructors everywhere possible to reduce rebuilds. +* **Build Methods:** Avoid expensive ops (network) in `build()`. + +## State Management +* **Native-First:** Prefer `ValueNotifier`, `ChangeNotifier`, `ListenableBuilder`. +* **Restrictions:** Do NOT use Riverpod, Bloc, or GetX unless explicitly requested. +* **ChangeNotifier:** For state that is more complex or shared across multiple widgets, use `ChangeNotifier`. +* **MVVM:** When a more robust solution is needed, structure the app using the Model-View-ViewModel (MVVM) pattern. +* **Dependency Injection:** Use simple manual constructor dependency injection to make a class's dependencies explicit in its API, and to manage dependencies between different layers of the application. + +```dart +// Simple Local State +final ValueNotifier _counter = ValueNotifier(0); +ValueListenableBuilder( + valueListenable: _counter, + builder: (context, value, child) => Text('Count: $value'), +); +``` + +## Routing (GoRouter) +Use `go_router` for all navigation needs (deep linking, web). Ensure users are redirected to login when unauthorized. + +```dart +final GoRouter _router = GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (context, state) => const HomeScreen(), + routes: [ + GoRoute( + path: 'details/:id', + builder: (context, state) { + final String id = state.pathParameters['id']!; + return DetailScreen(id: id); + }, + ), + ], + ), + ], +); +MaterialApp.router(routerConfig: _router); +``` + +## Data Handling & Serialization +* **JSON:** Use `json_serializable` and `json_annotation`. +* **Naming:** Use `fieldRename: FieldRename.snake` for consistency. + +```dart +@JsonSerializable(fieldRename: FieldRename.snake) +class User { + final String firstName; + final String lastName; + User({required this.firstName, required this.lastName}); + factory User.fromJson(Map json) => _$UserFromJson(json); +} +``` + +## Visual Design & Theming (Material 3) +* **Visual Design:** Build beautiful and intuitive user interfaces that follow modern design guidelines. +* **Typography:** Stress and emphasize font sizes to ease understanding, e.g., hero text, section headlines. +* **Background:** Apply subtle noise texture to the main background to add a premium, tactile feel. +* **Shadows:** Multi-layered drop shadows create a strong sense of depth; cards have a soft, deep shadow to look "lifted." +* **Icons:** Incorporate icons to enhance the user’s understanding and the logical navigation of the app. +* **Interactive Elements:** Buttons, checkboxes, sliders, lists, charts, graphs, and other interactive elements have a shadow with elegant use of color to create a "glow" effect. +* **Centralized Theme:** Define a centralized `ThemeData` object to ensure a consistent application-wide style. +* **Light and Dark Themes:** Implement support for both light and dark themes using `theme` and `darkTheme`. +* **Color Scheme Generation:** Generate harmonious color palettes from a single color using `ColorScheme.fromSeed`. + +```dart +final ThemeData lightTheme = ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.deepPurple, + brightness: Brightness.light, + ), + textTheme: GoogleFonts.outfitTextTheme(), +); +``` + +## Layout Best Practices +* **Expanded:** Use to make a child widget fill the remaining available space along the main axis. +* **Flexible:** Use when you want a widget to shrink to fit, but not necessarily grow. Don't combine `Flexible` and `Expanded` in the same `Row` or `Column`. +* **Wrap:** Use when you have a series of widgets that would overflow a `Row` or `Column`, and you want them to move to the next line. +* **SingleChildScrollView:** Use when your content is intrinsically larger than the viewport, but is a fixed size. +* **ListView / GridView:** For long lists or grids of content, always use a builder constructor (`.builder`). +* **FittedBox:** Use to scale or fit a single child widget within its parent. +* **LayoutBuilder:** Use for complex, responsive layouts to make decisions based on the available space. +* **Positioned:** Use to precisely place a child within a `Stack` by anchoring it to the edges. +* **OverlayPortal:** Use to show UI elements (like custom dropdowns or tooltips) "on top" of everything else. + +```dart +// Network Image with Error Handler +Image.network( + 'https://example.com/img.png', + errorBuilder: (ctx, err, stack) => const Icon(Icons.error), + loadingBuilder: (ctx, child, prog) => prog == null ? child : const CircularProgressIndicator(), +); +``` + +## Documentation Philosophy +* **Comment wisely:** Use comments to explain why the code is written a certain way, not what the code does. The code itself should be self-explanatory. +* **Document for the user:** Write documentation with the reader in mind. If you had a question and found the answer, add it to the documentation where you first looked. +* **No useless documentation:** If the documentation only restates the obvious from the code's name, it's not helpful. +* **Consistency is key:** Use consistent terminology throughout your documentation. +* **Use `///` for doc comments:** This allows documentation generation tools to pick them up. +* **Start with a single-sentence summary:** The first sentence should be a concise, user-centric summary ending with a period. +* **Avoid redundancy:** Don't repeat information that's obvious from the code's context, like the class name or signature. +* **Public APIs are a priority:** Always document public APIs. + +## Accessibility +* **Contrast:** Ensure text has a contrast ratio of at least **4.5:1** against its background. +* **Dynamic Text Scaling:** Test your UI to ensure it remains usable when users increase the system font size. +* **Semantic Labels:** Use the `Semantics` widget to provide clear, descriptive labels for UI elements. +* **Screen Reader Testing:** Regularly test your app with TalkBack (Android) and VoiceOver (iOS). + +## Analysis Options +Strictly follow `flutter_lints`. + +```yaml +include: package:flutter_lints/flutter.yaml +linter: + rules: + avoid_print: true + prefer_single_quotes: true + always_use_package_imports: true +``` diff --git a/VERSIONS.md b/VERSIONS.md index 4210c4a223d3..e3bc373d0cc6 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -4,6 +4,234 @@ This document is listing all the compatible versions of the FlutterFire plugins. # Versions +## [Flutter BoM 4.16.1 (2026-06-22)](https://github.com/firebase/flutterfire/blob/main/CHANGELOG.md#2026-06-22) + +Install this version using FlutterFire CLI + +```bash +flutterfire install 4.16.1 +``` + +### Included Native Firebase SDK Versions +| Firebase SDK | Version | Link | +|--------------|---------|------| +| Android SDK | 34.15.0 | [Release Notes](https://firebase.google.com/support/release-notes/android) | +| iOS SDK | 12.15.0 | [Release Notes](https://firebase.google.com/support/release-notes/ios) | +| Web SDK | 12.15.0 | [Release Notes](https://firebase.google.com/support/release-notes/js) | +| Windows SDK | 13.5.0 | [Release Notes](https://firebase.google.com/support/release-notes/cpp-relnotes) | + +### FlutterFire Plugin Versions +| Plugin | Version | Dart Version | Flutter Version | +|--------|---------|--------------|-----------------| +| [cloud_firestore](https://pub.dev/packages/cloud_firestore/versions/6.6.0) | 6.6.0 | ^3.6.0 | >=3.27.0 | +| [cloud_functions](https://pub.dev/packages/cloud_functions/versions/6.3.3) | 6.3.3 | ^3.6.0 | >=3.27.0 | +| [firebase_ai](https://pub.dev/packages/firebase_ai/versions/3.13.1) | 3.13.1 | ^3.6.0 | >=3.16.0 | +| [firebase_analytics](https://pub.dev/packages/firebase_analytics/versions/12.4.3) | 12.4.3 | ^3.6.0 | >=3.27.0 | +| [firebase_app_check](https://pub.dev/packages/firebase_app_check/versions/0.4.5) | 0.4.5 | ^3.6.0 | >=3.27.0 | +| [firebase_app_installations](https://pub.dev/packages/firebase_app_installations/versions/0.4.2+4) | 0.4.2+4 | ^3.6.0 | >=3.27.0 | +| [firebase_auth](https://pub.dev/packages/firebase_auth/versions/6.5.4) | 6.5.4 | ^3.6.0 | >=3.16.0 | +| [firebase_core](https://pub.dev/packages/firebase_core/versions/4.11.0) | 4.11.0 | ^3.6.0 | >=3.27.0 | +| [firebase_crashlytics](https://pub.dev/packages/firebase_crashlytics/versions/5.2.4) | 5.2.4 | ^3.6.0 | >=3.27.0 | +| [firebase_data_connect](https://pub.dev/packages/firebase_data_connect/versions/0.3.0+5) | 0.3.0+5 | ^3.6.0 | >=3.27.0 | +| [firebase_database](https://pub.dev/packages/firebase_database/versions/12.4.4) | 12.4.4 | ^3.6.0 | >=3.27.0 | +| [firebase_in_app_messaging](https://pub.dev/packages/firebase_in_app_messaging/versions/0.9.2+4) | 0.9.2+4 | ^3.6.0 | >=3.27.0 | +| [firebase_messaging](https://pub.dev/packages/firebase_messaging/versions/16.4.1) | 16.4.1 | ^3.6.0 | >=3.27.0 | +| [firebase_ml_model_downloader](https://pub.dev/packages/firebase_ml_model_downloader/versions/0.4.2+4) | 0.4.2+4 | ^3.6.0 | >=3.27.0 | +| [firebase_performance](https://pub.dev/packages/firebase_performance/versions/0.11.4+3) | 0.11.4+3 | ^3.6.0 | >=3.27.0 | +| [firebase_remote_config](https://pub.dev/packages/firebase_remote_config/versions/6.5.3) | 6.5.3 | ^3.6.0 | >=3.27.0 | +| [firebase_storage](https://pub.dev/packages/firebase_storage/versions/13.4.3) | 13.4.3 | ^3.6.0 | >=3.27.0 | + + +## [Flutter BoM 4.16.0 (2026-06-17)](https://github.com/firebase/flutterfire/blob/main/CHANGELOG.md#2026-06-17) + +Install this version using FlutterFire CLI + +```bash +flutterfire install 4.16.0 +``` + +### Included Native Firebase SDK Versions +| Firebase SDK | Version | Link | +|--------------|---------|------| +| Android SDK | 34.15.0 | [Release Notes](https://firebase.google.com/support/release-notes/android) | +| iOS SDK | 12.15.0 | [Release Notes](https://firebase.google.com/support/release-notes/ios) | +| Web SDK | 12.15.0 | [Release Notes](https://firebase.google.com/support/release-notes/js) | +| Windows SDK | 13.5.0 | [Release Notes](https://firebase.google.com/support/release-notes/cpp-relnotes) | + +### FlutterFire Plugin Versions +| Plugin | Version | Dart Version | Flutter Version | +|--------|---------|--------------|-----------------| +| [cloud_firestore](https://pub.dev/packages/cloud_firestore/versions/6.6.0) | 6.6.0 | ^3.6.0 | >=3.27.0 | +| [cloud_functions](https://pub.dev/packages/cloud_functions/versions/6.3.3) | 6.3.3 | ^3.6.0 | >=3.27.0 | +| [firebase_ai](https://pub.dev/packages/firebase_ai/versions/3.13.0) | 3.13.0 | ^3.6.0 | >=3.16.0 | +| [firebase_analytics](https://pub.dev/packages/firebase_analytics/versions/12.4.3) | 12.4.3 | ^3.6.0 | >=3.27.0 | +| [firebase_app_check](https://pub.dev/packages/firebase_app_check/versions/0.4.5) | 0.4.5 | ^3.6.0 | >=3.27.0 | +| [firebase_app_installations](https://pub.dev/packages/firebase_app_installations/versions/0.4.2+4) | 0.4.2+4 | ^3.6.0 | >=3.27.0 | +| [firebase_auth](https://pub.dev/packages/firebase_auth/versions/6.5.3) | 6.5.3 | ^3.6.0 | >=3.16.0 | +| [firebase_core](https://pub.dev/packages/firebase_core/versions/4.11.0) | 4.11.0 | ^3.6.0 | >=3.27.0 | +| [firebase_crashlytics](https://pub.dev/packages/firebase_crashlytics/versions/5.2.4) | 5.2.4 | ^3.6.0 | >=3.27.0 | +| [firebase_data_connect](https://pub.dev/packages/firebase_data_connect/versions/0.3.0+4) | 0.3.0+4 | ^3.6.0 | >=3.27.0 | +| [firebase_database](https://pub.dev/packages/firebase_database/versions/12.4.3) | 12.4.3 | ^3.6.0 | >=3.27.0 | +| [firebase_in_app_messaging](https://pub.dev/packages/firebase_in_app_messaging/versions/0.9.2+4) | 0.9.2+4 | ^3.6.0 | >=3.27.0 | +| [firebase_messaging](https://pub.dev/packages/firebase_messaging/versions/16.4.0) | 16.4.0 | ^3.6.0 | >=3.27.0 | +| [firebase_ml_model_downloader](https://pub.dev/packages/firebase_ml_model_downloader/versions/0.4.2+4) | 0.4.2+4 | ^3.6.0 | >=3.27.0 | +| [firebase_performance](https://pub.dev/packages/firebase_performance/versions/0.11.4+3) | 0.11.4+3 | ^3.6.0 | >=3.27.0 | +| [firebase_remote_config](https://pub.dev/packages/firebase_remote_config/versions/6.5.3) | 6.5.3 | ^3.6.0 | >=3.27.0 | +| [firebase_storage](https://pub.dev/packages/firebase_storage/versions/13.4.3) | 13.4.3 | ^3.6.0 | >=3.27.0 | + + +## [Flutter BoM 4.15.0 (2026-06-01)](https://github.com/firebase/flutterfire/blob/main/CHANGELOG.md#2026-06-01) + +Install this version using FlutterFire CLI + +```bash +flutterfire install 4.15.0 +``` + +### Included Native Firebase SDK Versions +| Firebase SDK | Version | Link | +|--------------|---------|------| +| Android SDK | 34.14.0 | [Release Notes](https://firebase.google.com/support/release-notes/android) | +| iOS SDK | 12.14.0 | [Release Notes](https://firebase.google.com/support/release-notes/ios) | +| Web SDK | 12.14.0 | [Release Notes](https://firebase.google.com/support/release-notes/js) | +| Windows SDK | 13.5.0 | [Release Notes](https://firebase.google.com/support/release-notes/cpp-relnotes) | + +### FlutterFire Plugin Versions +| Plugin | Version | Dart Version | Flutter Version | +|--------|---------|--------------|-----------------| +| [cloud_firestore](https://pub.dev/packages/cloud_firestore/versions/6.5.0) | 6.5.0 | ^3.6.0 | >=3.27.0 | +| [cloud_functions](https://pub.dev/packages/cloud_functions/versions/6.3.2) | 6.3.2 | ^3.6.0 | >=3.27.0 | +| [firebase_ai](https://pub.dev/packages/firebase_ai/versions/3.12.2) | 3.12.2 | ^3.6.0 | >=3.16.0 | +| [firebase_analytics](https://pub.dev/packages/firebase_analytics/versions/12.4.2) | 12.4.2 | ^3.6.0 | >=3.27.0 | +| [firebase_app_check](https://pub.dev/packages/firebase_app_check/versions/0.4.4+2) | 0.4.4+2 | ^3.6.0 | >=3.27.0 | +| [firebase_app_installations](https://pub.dev/packages/firebase_app_installations/versions/0.4.2+3) | 0.4.2+3 | ^3.6.0 | >=3.27.0 | +| [firebase_auth](https://pub.dev/packages/firebase_auth/versions/6.5.2) | 6.5.2 | ^3.6.0 | >=3.16.0 | +| [firebase_core](https://pub.dev/packages/firebase_core/versions/4.10.0) | 4.10.0 | ^3.6.0 | >=3.27.0 | +| [firebase_crashlytics](https://pub.dev/packages/firebase_crashlytics/versions/5.2.3) | 5.2.3 | ^3.6.0 | >=3.27.0 | +| [firebase_data_connect](https://pub.dev/packages/firebase_data_connect/versions/0.3.0+3) | 0.3.0+3 | ^3.6.0 | >=3.27.0 | +| [firebase_database](https://pub.dev/packages/firebase_database/versions/12.4.2) | 12.4.2 | ^3.6.0 | >=3.27.0 | +| [firebase_in_app_messaging](https://pub.dev/packages/firebase_in_app_messaging/versions/0.9.2+3) | 0.9.2+3 | ^3.6.0 | >=3.27.0 | +| [firebase_messaging](https://pub.dev/packages/firebase_messaging/versions/16.3.0) | 16.3.0 | ^3.6.0 | >=3.27.0 | +| [firebase_ml_model_downloader](https://pub.dev/packages/firebase_ml_model_downloader/versions/0.4.2+3) | 0.4.2+3 | ^3.6.0 | >=3.27.0 | +| [firebase_performance](https://pub.dev/packages/firebase_performance/versions/0.11.4+2) | 0.11.4+2 | ^3.6.0 | >=3.27.0 | +| [firebase_remote_config](https://pub.dev/packages/firebase_remote_config/versions/6.5.2) | 6.5.2 | ^3.6.0 | >=3.27.0 | +| [firebase_storage](https://pub.dev/packages/firebase_storage/versions/13.4.2) | 13.4.2 | ^3.6.0 | >=3.27.0 | + + +## [Flutter BoM 4.14.0 (2026-05-14)](https://github.com/firebase/flutterfire/blob/main/CHANGELOG.md#2026-05-14) + +Install this version using FlutterFire CLI + +```bash +flutterfire install 4.14.0 +``` + +### Included Native Firebase SDK Versions +| Firebase SDK | Version | Link | +|--------------|---------|------| +| Android SDK | 34.13.0 | [Release Notes](https://firebase.google.com/support/release-notes/android) | +| iOS SDK | 12.13.0 | [Release Notes](https://firebase.google.com/support/release-notes/ios) | +| Web SDK | 12.13.0 | [Release Notes](https://firebase.google.com/support/release-notes/js) | +| Windows SDK | 13.5.0 | [Release Notes](https://firebase.google.com/support/release-notes/cpp-relnotes) | + +### FlutterFire Plugin Versions +| Plugin | Version | Dart Version | Flutter Version | +|--------|---------|--------------|-----------------| +| [cloud_firestore](https://pub.dev/packages/cloud_firestore/versions/6.4.1) | 6.4.1 | ^3.6.0 | >=3.27.0 | +| [cloud_functions](https://pub.dev/packages/cloud_functions/versions/6.3.1) | 6.3.1 | ^3.6.0 | >=3.27.0 | +| [firebase_ai](https://pub.dev/packages/firebase_ai/versions/3.12.1) | 3.12.1 | ^3.6.0 | >=3.16.0 | +| [firebase_analytics](https://pub.dev/packages/firebase_analytics/versions/12.4.1) | 12.4.1 | ^3.6.0 | >=3.27.0 | +| [firebase_app_check](https://pub.dev/packages/firebase_app_check/versions/0.4.4+1) | 0.4.4+1 | ^3.6.0 | >=3.27.0 | +| [firebase_app_installations](https://pub.dev/packages/firebase_app_installations/versions/0.4.2+2) | 0.4.2+2 | ^3.6.0 | >=3.27.0 | +| [firebase_auth](https://pub.dev/packages/firebase_auth/versions/6.5.1) | 6.5.1 | ^3.6.0 | >=3.16.0 | +| [firebase_core](https://pub.dev/packages/firebase_core/versions/4.9.0) | 4.9.0 | ^3.6.0 | >=3.27.0 | +| [firebase_crashlytics](https://pub.dev/packages/firebase_crashlytics/versions/5.2.2) | 5.2.2 | ^3.6.0 | >=3.27.0 | +| [firebase_data_connect](https://pub.dev/packages/firebase_data_connect/versions/0.3.0+2) | 0.3.0+2 | ^3.6.0 | >=3.27.0 | +| [firebase_database](https://pub.dev/packages/firebase_database/versions/12.4.1) | 12.4.1 | ^3.6.0 | >=3.27.0 | +| [firebase_in_app_messaging](https://pub.dev/packages/firebase_in_app_messaging/versions/0.9.2+2) | 0.9.2+2 | ^3.6.0 | >=3.27.0 | +| [firebase_messaging](https://pub.dev/packages/firebase_messaging/versions/16.2.2) | 16.2.2 | ^3.6.0 | >=3.27.0 | +| [firebase_ml_model_downloader](https://pub.dev/packages/firebase_ml_model_downloader/versions/0.4.2+2) | 0.4.2+2 | ^3.6.0 | >=3.27.0 | +| [firebase_performance](https://pub.dev/packages/firebase_performance/versions/0.11.4+1) | 0.11.4+1 | ^3.6.0 | >=3.27.0 | +| [firebase_remote_config](https://pub.dev/packages/firebase_remote_config/versions/6.5.1) | 6.5.1 | ^3.6.0 | >=3.27.0 | +| [firebase_storage](https://pub.dev/packages/firebase_storage/versions/13.4.1) | 13.4.1 | ^3.6.0 | >=3.27.0 | + + +## [Flutter BoM 4.13.0 (2026-05-11)](https://github.com/firebase/flutterfire/blob/main/CHANGELOG.md#2026-05-11) + +Install this version using FlutterFire CLI + +```bash +flutterfire install 4.13.0 +``` + +### Included Native Firebase SDK Versions +| Firebase SDK | Version | Link | +|--------------|---------|------| +| Android SDK | 34.12.0 | [Release Notes](https://firebase.google.com/support/release-notes/android) | +| iOS SDK | 12.12.0 | [Release Notes](https://firebase.google.com/support/release-notes/ios) | +| Web SDK | 12.12.0 | [Release Notes](https://firebase.google.com/support/release-notes/js) | +| Windows SDK | 13.5.0 | [Release Notes](https://firebase.google.com/support/release-notes/cpp-relnotes) | + +### FlutterFire Plugin Versions +| Plugin | Version | Dart Version | Flutter Version | +|--------|---------|--------------|-----------------| +| [cloud_firestore](https://pub.dev/packages/cloud_firestore/versions/6.4.0) | 6.4.0 | ^3.6.0 | >=3.27.0 | +| [cloud_functions](https://pub.dev/packages/cloud_functions/versions/6.3.0) | 6.3.0 | ^3.6.0 | >=3.27.0 | +| [firebase_ai](https://pub.dev/packages/firebase_ai/versions/3.12.0) | 3.12.0 | ^3.6.0 | >=3.16.0 | +| [firebase_analytics](https://pub.dev/packages/firebase_analytics/versions/12.4.0) | 12.4.0 | ^3.6.0 | >=3.27.0 | +| [firebase_app_check](https://pub.dev/packages/firebase_app_check/versions/0.4.4) | 0.4.4 | ^3.6.0 | >=3.27.0 | +| [firebase_app_installations](https://pub.dev/packages/firebase_app_installations/versions/0.4.2+1) | 0.4.2+1 | ^3.6.0 | >=3.27.0 | +| [firebase_auth](https://pub.dev/packages/firebase_auth/versions/6.5.0) | 6.5.0 | ^3.6.0 | >=3.16.0 | +| [firebase_core](https://pub.dev/packages/firebase_core/versions/4.8.0) | 4.8.0 | ^3.6.0 | >=3.27.0 | +| [firebase_crashlytics](https://pub.dev/packages/firebase_crashlytics/versions/5.2.1) | 5.2.1 | ^3.6.0 | >=3.27.0 | +| [firebase_data_connect](https://pub.dev/packages/firebase_data_connect/versions/0.3.0+1) | 0.3.0+1 | ^3.6.0 | >=3.27.0 | +| [firebase_database](https://pub.dev/packages/firebase_database/versions/12.4.0) | 12.4.0 | ^3.6.0 | >=3.27.0 | +| [firebase_in_app_messaging](https://pub.dev/packages/firebase_in_app_messaging/versions/0.9.2+1) | 0.9.2+1 | ^3.6.0 | >=3.27.0 | +| [firebase_messaging](https://pub.dev/packages/firebase_messaging/versions/16.2.1) | 16.2.1 | ^3.6.0 | >=3.27.0 | +| [firebase_ml_model_downloader](https://pub.dev/packages/firebase_ml_model_downloader/versions/0.4.2+1) | 0.4.2+1 | ^3.6.0 | >=3.27.0 | +| [firebase_performance](https://pub.dev/packages/firebase_performance/versions/0.11.4) | 0.11.4 | ^3.6.0 | >=3.27.0 | +| [firebase_remote_config](https://pub.dev/packages/firebase_remote_config/versions/6.5.0) | 6.5.0 | ^3.6.0 | >=3.27.0 | +| [firebase_storage](https://pub.dev/packages/firebase_storage/versions/13.4.0) | 13.4.0 | ^3.6.0 | >=3.27.0 | + + +## [Flutter BoM 4.12.0 (2026-04-13)](https://github.com/firebase/flutterfire/blob/main/CHANGELOG.md#2026-04-13) + +Install this version using FlutterFire CLI + +```bash +flutterfire install 4.12.0 +``` + +### Included Native Firebase SDK Versions +| Firebase SDK | Version | Link | +|--------------|---------|------| +| Android SDK | 34.12.0 | [Release Notes](https://firebase.google.com/support/release-notes/android) | +| iOS SDK | 12.12.0 | [Release Notes](https://firebase.google.com/support/release-notes/ios) | +| Web SDK | 12.12.0 | [Release Notes](https://firebase.google.com/support/release-notes/js) | +| Windows SDK | 13.5.0 | [Release Notes](https://firebase.google.com/support/release-notes/cpp-relnotes) | + +### FlutterFire Plugin Versions +| Plugin | Version | Dart Version | Flutter Version | +|--------|---------|--------------|-----------------| +| [cloud_firestore](https://pub.dev/packages/cloud_firestore/versions/6.3.0) | 6.3.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [cloud_functions](https://pub.dev/packages/cloud_functions/versions/6.2.0) | 6.2.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_ai](https://pub.dev/packages/firebase_ai/versions/3.11.0) | 3.11.0 | >=3.2.0 <4.0.0 | >=3.16.0 | +| [firebase_analytics](https://pub.dev/packages/firebase_analytics/versions/12.3.0) | 12.3.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_app_check](https://pub.dev/packages/firebase_app_check/versions/0.4.3) | 0.4.3 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_app_installations](https://pub.dev/packages/firebase_app_installations/versions/0.4.2) | 0.4.2 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_auth](https://pub.dev/packages/firebase_auth/versions/6.4.0) | 6.4.0 | >=3.2.0 <4.0.0 | >=3.16.0 | +| [firebase_core](https://pub.dev/packages/firebase_core/versions/4.7.0) | 4.7.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_crashlytics](https://pub.dev/packages/firebase_crashlytics/versions/5.2.0) | 5.2.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_data_connect](https://pub.dev/packages/firebase_data_connect/versions/0.3.0) | 0.3.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_database](https://pub.dev/packages/firebase_database/versions/12.3.0) | 12.3.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_in_app_messaging](https://pub.dev/packages/firebase_in_app_messaging/versions/0.9.2) | 0.9.2 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_messaging](https://pub.dev/packages/firebase_messaging/versions/16.2.0) | 16.2.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_ml_model_downloader](https://pub.dev/packages/firebase_ml_model_downloader/versions/0.4.2) | 0.4.2 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_performance](https://pub.dev/packages/firebase_performance/versions/0.11.3) | 0.11.3 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_remote_config](https://pub.dev/packages/firebase_remote_config/versions/6.4.0) | 6.4.0 | >=3.2.0 <4.0.0 | >=3.3.0 | +| [firebase_storage](https://pub.dev/packages/firebase_storage/versions/13.3.0) | 13.3.0 | >=3.2.0 <4.0.0 | >=3.3.0 | + + ## [Flutter BoM 4.11.0 (2026-03-23)](https://github.com/firebase/flutterfire/blob/main/CHANGELOG.md#2026-03-23) Install this version using FlutterFire CLI diff --git a/docs/analytics/_get-started.md b/docs/analytics/_get-started.md index 78bbe531e1e4..58bd944a7301 100644 --- a/docs/analytics/_get-started.md +++ b/docs/analytics/_get-started.md @@ -85,9 +85,10 @@ await FirebaseAnalytics.instance ## Using Analytics without Ad ID support (iOS) {:#without-ad-id} -If your app doesn't use IDFA, you can use `FirebaseAnalyticsWithoutAdIdSupport` -instead of the default `FirebaseAnalytics` iOS dependency to avoid App Store -review questions about advertising identifiers. +If your app doesn't use IDFA, use the IDFA-free Analytics iOS dependency +(`FirebaseAnalyticsCore` under SPM, or `FirebaseAnalytics/Core` under CocoaPods) +instead of the default `FirebaseAnalytics` dependency to avoid App Store review +questions about advertising identifiers. ### Swift Package Manager diff --git a/docs/auth/errors.md b/docs/auth/errors.md index 422acdfead97..0f4154ada219 100644 --- a/docs/auth/errors.md +++ b/docs/auth/errors.md @@ -97,3 +97,11 @@ try { } } ``` + +## `recaptcha-sdk-not-linked` (iOS phone auth) + +If `e.code` is **`recaptcha-sdk-not-linked`** during **`verifyPhoneNumber`** on **iOS**, the native layer expects **reCAPTCHA Enterprise** +to be linked or your **Identity Platform** project configuration must be adjusted. This is not fixed from Dart alone. + +See [Phone Authentication — iOS: reCAPTCHA SDK and Identity Platform](/docs/auth/phone-auth#ios-recaptcha-sdk-and-identity-platform) for +recommended setup, the Safari flow, and a documented **GCP / Identity Toolkit** workaround with trade-offs. diff --git a/docs/auth/federated-auth.md b/docs/auth/federated-auth.md index 3811c94f348a..b4d73a36b633 100644 --- a/docs/auth/federated-auth.md +++ b/docs/auth/federated-auth.md @@ -284,6 +284,16 @@ Future signInWithApple() async { } ``` +On Android platforms, obtain the access token then revoke the token using the +`revokeAccessToken()` API. + +```dart +// Keep the access token returned from Android platforms +String? accessToken = userCredential.credential?.accessToken; +// Revoke the access token +await FirebaseAuth.instance.revokeAccessToken(accessToken!); +``` + ## Apple Game Center (Apple only) {:#games} Ensure the "Game Center" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers). diff --git a/docs/auth/manage-users.md b/docs/auth/manage-users.md index 9584abf35454..5b52742175fa 100644 --- a/docs/auth/manage-users.md +++ b/docs/auth/manage-users.md @@ -162,6 +162,11 @@ await FirebaseAuth.instance .sendPasswordResetEmail(email: "user@example.com"); ``` +Note: If +[email enumeration protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) +is enabled for your Firebase project, `sendPasswordResetEmail()` may complete +without throwing an error even when the email address is not registered. This is +expected Firebase Auth behavior to prevent revealing whether an email exists. You can customize the email template that is used in Authentication section of the [Firebase console](https://console.firebase.google.com/), on the Email Templates page. See [Email Templates](https://support.google.com/firebase/answer/7000714) in diff --git a/docs/auth/phone-auth.md b/docs/auth/phone-auth.md index 55adadd06c95..e28a02103a64 100644 --- a/docs/auth/phone-auth.md +++ b/docs/auth/phone-auth.md @@ -26,12 +26,64 @@ Before starting with Phone Authentication, ensure you have followed these steps: your APNs authentication key is [configured with Firebase Cloud Messaging (FCM)](/docs/cloud-messaging/ios/certs). Additionally, you must [enable background modes](https://help.apple.com/xcode/mac/current/#/deve49d0ba96) for remote notifications. To view an in-depth explanation of this step, view the [Firebase iOS Phone Auth](/docs/auth/ios/phone-auth) documentation. + If verification fails with **`recaptcha-sdk-not-linked`** (*The reCAPTCHA SDK is not linked to your app*), see + [iOS: reCAPTCHA SDK and Identity Platform](#ios-recaptcha-sdk-and-identity-platform). 4. **Web**: Ensure that you have added your applications domain on the [Firebase console](https://console.firebase.google.com/), under **OAuth redirect domains**. **Note**; Phone number sign-in is only available for use on real devices and the web. To test your authentication flow on device emulators, please see [Testing](#testing). +## iOS: reCAPTCHA SDK and Identity Platform + +On **iOS**, phone sign-in can fail with `FirebaseAuthException` code **`recaptcha-sdk-not-linked`** (for example: *The reCAPTCHA SDK is not linked to your app*). +That error is raised by the **native Firebase iOS Auth** SDK when your Firebase / **Identity Platform** configuration expects **reCAPTCHA Enterprise** +for SMS-related verification, but the **reCAPTCHA Enterprise iOS SDK** is not linked in your Xcode project. + +The `firebase_auth` plugin does not inject a reCAPTCHA client from Dart—you must fix this in **native iOS** setup or in **Google Cloud / Firebase** configuration. + +### Recommended: Link reCAPTCHA Enterprise on iOS + +Add the **reCAPTCHA Enterprise** client to your app using Google’s guide: +[Instrument your iOS app with reCAPTCHA Enterprise](https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps) +(CocoaPods or Swift Package Manager), so Firebase Auth can satisfy the Enterprise requirement. + +### Safari-hosted challenge (`SFSafariViewController`) + +If the SDK uses a **Safari view controller**-hosted challenge instead of the Enterprise SDK, your app must handle the **return URL** +(for example with **`uni_links`** / **`app_links`**, or **`onOpenUrl`** / **`application:openURL:`** in the iOS runner), following standard +Flutter + Firebase iOS setup for OAuth-style callbacks. + +### When you don’t need reCAPTCHA Enterprise for phone auth + +[Google’s guide to reCAPTCHA SMS defense](https://cloud.google.com/identity-platform/docs/recaptcha-tfp) describes an **optional** Identity Platform +feature that depends on the **reCAPTCHA Enterprise API** and (on iOS) linking the [reCAPTCHA Enterprise client](https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps). +Standard Firebase phone authentication often **does not** require that flow—turning **reCAPTCHA Enterprise** on in Firebase / Google Cloud without also +shipping the native SDK can surface **`recaptcha-sdk-not-linked`**. + +### Disable reCAPTCHA SMS defense (Google-documented REST update) + +To **disable** reCAPTCHA SMS defense (for example when you cannot or won’t link the Enterprise SDK), Google documents a **`projects.updateConfig`** call +on **`projects/{project_id}/config`** with **`updateMask=recaptchaConfig`** and: + +- `recaptchaConfig.phoneEnforcementState`: **`OFF`** +- `recaptchaConfig.useSmsTollFraudProtection`: **`false`** + +Use Google’s **official steps** (including the **APIs Explorer** URL with `PROJECT_ID` replaced by your project ID): + +- [Disable reCAPTCHA SMS defense](https://cloud.google.com/identity-platform/docs/recaptcha-tfp#disable_recaptcha_sms_defense) +- REST reference: [`projects.updateConfig`](https://cloud.google.com/identity-platform/docs/reference/rest/v2/projects/updateConfig) + +Some developers report that **enabling then disabling** *Manage reCAPTCHA* / **reCAPTCHA Enterprise API** in the Firebase or Google Cloud UI can leave +**`recaptchaConfig`** expecting Enterprise until the **`OFF` / `false`** update is applied; see +[flutterfire#18171 (comment)](https://github.com/firebase/flutterfire/issues/18171#issuecomment-4195461765). + +**Caution:** Disabling SMS defense **reduces fraud protection**. Prefer keeping defense **on** and [linking reCAPTCHA Enterprise on iOS](#recommended-link-recaptcha-enterprise-on-ios) when your product allows. + +More context: [flutterfire#18171](https://github.com/firebase/flutterfire/issues/18171), +[flutterfire#17557](https://github.com/firebase/flutterfire/issues/17557), +[firebase-ios-sdk#15345](https://github.com/firebase/firebase-ios-sdk/issues/15345). + ## Usage The Firebase Authentication SDK for Flutter provides two individual ways to sign a user in with their phone number. Native (e.g. Android & iOS) platforms provide diff --git a/melos.yaml b/melos.yaml index 38eab295bfa1..c25d99b6bd86 100644 --- a/melos.yaml +++ b/melos.yaml @@ -21,7 +21,7 @@ command: dart run scripts/generate_dataconnect_version.dart && \ dart run scripts/generate_versions_web.dart && \ dart run scripts/generate_versions_spm.dart && \ - git add packages/firebase_data_connect/firebase_data_connect/lib/src/dataconnect_version.dart && git add packages/*/*_web/lib/src/*_version.dart && git add packages/*/*/ios/*/Package.swift packages/*/*/macos/*/Package.swift + git add packages/firebase_data_connect/firebase_data_connect/lib/src/dataconnect_version.dart && git add packages/*/*_web/lib/src/*_version.dart && git add packages/*/*/ios/*/Package.swift packages/*/*/macos/*/Package.swift && git add packages/*/*/ios/*/Sources/*/Constants.swift bootstrap: # It seems so that running "pub get" in parallel has some issues (like @@ -55,8 +55,7 @@ scripts: format-ci: run: | - dart pub global run flutter_plugin_tools format && \ - swiftformat . + dart pub global run flutter_plugin_tools format description: | Formats the code of all packages (Java, Objective-C, and Dart). - Requires `flutter_plugin_tools` (`pub global activate flutter_plugin_tools`). @@ -170,6 +169,13 @@ scripts: description: | Run all e2e tests for cloud_firestore. + test:e2e:pipeline: + run: | + cd packages/cloud_firestore/cloud_firestore/pipeline_example + flutter test integration_test/pipeline/pipeline_live_test.dart + description: | + Run pipeline e2e tests. + test:e2e:firebase_performance: run: | cd packages/firebase_performance/firebase_performance/example @@ -232,6 +238,7 @@ scripts: melos exec -- "flutter pub run pigeon --input ./pigeons/messages.dart" && \ melos run generate:pigeon:macos --no-select && \ melos run generate:pigeon:android --no-select && \ + melos run generate:pigeon:windows --no-select && \ melos run format-ci --no-select packageFilters: fileExists: 'pigeons/messages.dart' @@ -252,6 +259,13 @@ scripts: dirExists: 'android' description: Transform the method toList() into a public one to be used in EventChannel + generate:pigeon:windows: + run: | + melos exec -- "perl -i -pe 's{^(\s+::flutter::EncodableList ToEncodableList\(\) const;)\n}{ public:\n\$1\n private:\n}' ./windows/messages.g.h" + packageFilters: + fileExists: 'windows/messages.g.h' + description: Make ToEncodableList public so it can be called from plugin code. + # Additional cleanup lifecycle script, executed when `melos clean` is run. postclean: > melos exec -c 6 -- "flutter clean" diff --git a/packages/_flutterfire_internals/CHANGELOG.md b/packages/_flutterfire_internals/CHANGELOG.md index ff4690bcb279..b94305236a5a 100644 --- a/packages/_flutterfire_internals/CHANGELOG.md +++ b/packages/_flutterfire_internals/CHANGELOG.md @@ -1,3 +1,23 @@ +## 1.3.73 + + - Update a dependency to the latest release. + +## 1.3.72 + + - Update a dependency to the latest release. + +## 1.3.71 + + - Update a dependency to the latest release. + +## 1.3.70 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 1.3.69 + + - **FIX**: improve error handling in _firebaseExceptionFromCoreFirebaseError ([#18177](https://github.com/firebase/flutterfire/issues/18177)). ([3c29048a](https://github.com/firebase/flutterfire/commit/3c29048a859b62f3f224b1fa3c8db61f78f63374)) + ## 1.3.68 - Update a dependency to the latest release. diff --git a/packages/_flutterfire_internals/lib/_flutterfire_internals.dart b/packages/_flutterfire_internals/lib/_flutterfire_internals.dart index 649dabb767a2..b4c6b6c905fd 100644 --- a/packages/_flutterfire_internals/lib/_flutterfire_internals.dart +++ b/packages/_flutterfire_internals/lib/_flutterfire_internals.dart @@ -91,12 +91,14 @@ FirebaseException _firebaseExceptionFromCoreFirebaseError( /// /// See also https://github.com/dart-lang/sdk/issues/30741 bool _testException(Object? objectException) { - final exception = objectException! as core_interop.JSError; + if (objectException is! core_interop.JSError) { + return false; + } - final message = _safeConvertFromPossibleJSObject(exception.message); + final message = _safeConvertFromPossibleJSObject(objectException.message); // Firestore web does not contain `Firebase` in the message so we check the exception itself. return message.contains('Firebase') || - exception.toString().contains('FirebaseError'); + objectException.toString().contains('FirebaseError'); } /// Transforms internal errors in something more readable for end-users. diff --git a/packages/_flutterfire_internals/pubspec.yaml b/packages/_flutterfire_internals/pubspec.yaml index 485b78d0b208..226a77a68d01 100755 --- a/packages/_flutterfire_internals/pubspec.yaml +++ b/packages/_flutterfire_internals/pubspec.yaml @@ -2,16 +2,17 @@ name: _flutterfire_internals description: A package hosting Dart code shared between FlutterFire plugins. homepage: https://firebase.google.com/docs/firestore repository: https://github.com/firebase/flutterfire/tree/main/packages/_flutterfire_internals -version: 1.3.68 +version: 1.3.73 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: collection: ^1.0.0 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter meta: ^1.8.0 @@ -20,4 +21,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - test: any diff --git a/packages/_flutterfire_internals/test/guard_test.dart b/packages/_flutterfire_internals/test/guard_test.dart index 40df9658bf6e..7e8332d6e74f 100644 --- a/packages/_flutterfire_internals/test/guard_test.dart +++ b/packages/_flutterfire_internals/test/guard_test.dart @@ -52,6 +52,19 @@ void main() { expect(stack, current); } }); + + test( + 'propagates plain Dart errors from Futures (e.g. ArgumentError on web)', + () async { + await expectLater( + guardWebExceptions( + () => Future.error(ArgumentError('test')), + plugin: 'test', + codeParser: (c) => c, + ), + throwsA(isA()), + ); + }); }); } diff --git a/packages/cloud_firestore/analysis_options.yaml b/packages/cloud_firestore/analysis_options.yaml new file mode 100644 index 000000000000..5dcab3c843d2 --- /dev/null +++ b/packages/cloud_firestore/analysis_options.yaml @@ -0,0 +1,11 @@ +# Copyright 2026 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# in the LICENSE file. + +include: ../../analysis_options.yaml + +analyzer: + exclude: + - cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart + - cloud_firestore_platform_interface/test/pigeon/test_api.dart + - cloud_firestore_platform_interface/pigeons/messages.dart diff --git a/packages/cloud_firestore/cloud_firestore/CHANGELOG.md b/packages/cloud_firestore/cloud_firestore/CHANGELOG.md index 4f474ba4f5ec..050b12b70400 100644 --- a/packages/cloud_firestore/cloud_firestore/CHANGELOG.md +++ b/packages/cloud_firestore/cloud_firestore/CHANGELOG.md @@ -1,3 +1,28 @@ +## 6.6.0 + + - **FEAT**(firestore): add support for search in firestore pipeline ([#18312](https://github.com/firebase/flutterfire/issues/18312)). ([b3c835f7](https://github.com/firebase/flutterfire/commit/b3c835f7bae8684d4c98167d78a071d9ed88f980)) + +## 6.5.0 + + - **FIX**(firestore,ios): add forceIndex parameter to collection source stage initializations ([#18332](https://github.com/firebase/flutterfire/issues/18332)). ([1bf50d2f](https://github.com/firebase/flutterfire/commit/1bf50d2f5bbdcac29797268632e2ed8b7e344c7d)) + - **FEAT**(firestore): add support for new array expressions ([#18283](https://github.com/firebase/flutterfire/issues/18283)). ([2293dee0](https://github.com/firebase/flutterfire/commit/2293dee00767cc6eebd57a340eb2b72bdab41d52)) + +## 6.4.1 + + - Update a dependency to the latest release. + +## 6.4.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(firestore,web): update Timestamp handling in jsify and EncodeUtility ([#18264](https://github.com/firebase/flutterfire/issues/18264)). ([9783a448](https://github.com/firebase/flutterfire/commit/9783a448ff532568a5e46ecb927e7b1bc77a164c)) + - **FIX**(firestore,windows): fix CI issue ([#18218](https://github.com/firebase/flutterfire/issues/18218)). ([b9c8a9e2](https://github.com/firebase/flutterfire/commit/b9c8a9e2993187c782c94398136aac9bf5418061)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 6.3.0 + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 6.2.0 - **FIX**(firestore,windows): fix a crash happening when terminating the firestore instance ([#18069](https://github.com/firebase/flutterfire/issues/18069)). ([adef1872](https://github.com/firebase/flutterfire/commit/adef1872b523b77e2309f3d7400e5a5fdd95738c)) diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreException.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreException.java index 759eecaf4cf7..7caa39dc96e5 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreException.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreException.java @@ -10,30 +10,41 @@ public class FlutterFirebaseFirestoreException extends Exception { private static final String ERROR_ABORTED = - "The operation was aborted, typically due to a concurrency issue like transaction aborts, etc."; + "The operation was aborted, typically due to a concurrency issue like transaction aborts," + + " etc."; private static final String ERROR_ALREADY_EXISTS = "Some document that we attempted to create already exists."; private static final String ERROR_CANCELLED = "The operation was cancelled (typically by the caller)."; private static final String ERROR_DATA_LOSS = "Unrecoverable data loss or corruption."; private static final String ERROR_DEADLINE_EXCEEDED = - "Deadline expired before operation could complete. For operations that change the state of the system, this error may be returned even if the operation has completed successfully. For example, a successful response from a server could have been delayed long enough for the deadline to expire."; + "Deadline expired before operation could complete. For operations that change the state of" + + " the system, this error may be returned even if the operation has completed" + + " successfully. For example, a successful response from a server could have been" + + " delayed long enough for the deadline to expire."; private static final String ERROR_FAILED_PRECONDITION = - "Operation was rejected because the system is not in a state required for the operation's execution. If performing a query, ensure it has been indexed via the Firebase console."; + "Operation was rejected because the system is not in a state required for the operation's" + + " execution. If performing a query, ensure it has been indexed via the Firebase" + + " console."; private static final String ERROR_INTERNAL = - "Internal errors. Means some invariants expected by underlying system has been broken. If you see one of these errors, something is very broken."; + "Internal errors. Means some invariants expected by underlying system has been broken. If you" + + " see one of these errors, something is very broken."; private static final String ERROR_INVALID_ARGUMENT = - "Client specified an invalid argument. Note that this differs from failed-precondition. invalid-argument indicates arguments that are problematic regardless of the state of the system (e.g., an invalid field name)."; + "Client specified an invalid argument. Note that this differs from failed-precondition." + + " invalid-argument indicates arguments that are problematic regardless of the state of" + + " the system (e.g., an invalid field name)."; private static final String ERROR_NOT_FOUND = "Some requested document was not found."; private static final String ERROR_OUT_OF_RANGE = "Operation was attempted past the valid range."; private static final String ERROR_PERMISSION_DENIED = "The caller does not have permission to execute the specified operation."; private static final String ERROR_RESOURCE_EXHAUSTED = - "Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space."; + "Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file" + + " system is out of space."; private static final String ERROR_UNAUTHENTICATED = "The request does not have valid authentication credentials for the operation."; private static final String ERROR_UNAVAILABLE = - "The service is currently unavailable. This is a most likely a transient condition and may be corrected by retrying with a backoff."; + "The service is currently unavailable. This is a most likely a transient condition and may be" + + " corrected by retrying with a backoff."; private static final String ERROR_UNIMPLEMENTED = "Operation is not implemented or not supported/enabled."; private static final String ERROR_UNKNOWN = diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java index 0c10ec694e5f..a446a11f364e 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java @@ -82,7 +82,8 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) { writeValue(stream, appName); writeValue(stream, ((DocumentReference) value).getPath()); String databaseURL; - // There is no way of getting database URL from Firebase android SDK API so we cache it ourselves + // There is no way of getting database URL from Firebase android SDK API so we cache it + // ourselves synchronized (FlutterFirebaseFirestorePlugin.firestoreInstanceCache) { databaseURL = FlutterFirebaseFirestorePlugin.getCachedFirebaseFirestoreInstanceForKey(firestore) @@ -534,7 +535,8 @@ private Query readFirestoreQuery(ByteBuffer buffer) { } catch (Exception exception) { Log.e( "FLTFirestoreMsgCodec", - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this" + + " SDK.", exception); return null; } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java index fb420c95bee2..e377f2737121 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java @@ -28,6 +28,8 @@ import com.google.firebase.firestore.MemoryCacheSettings; import com.google.firebase.firestore.PersistentCacheIndexManager; import com.google.firebase.firestore.PersistentCacheSettings; +import com.google.firebase.firestore.Pipeline; +import com.google.firebase.firestore.PipelineResult; import com.google.firebase.firestore.Query; import com.google.firebase.firestore.QuerySnapshot; import com.google.firebase.firestore.SetOptions; @@ -52,6 +54,7 @@ import io.flutter.plugins.firebase.firestore.streamhandler.TransactionStreamHandler; import io.flutter.plugins.firebase.firestore.utils.ExceptionConverter; import io.flutter.plugins.firebase.firestore.utils.PigeonParser; +import io.flutter.plugins.firebase.firestore.utils.PipelineParser; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -74,8 +77,7 @@ public class FlutterFirebaseFirestorePlugin private static final String METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_firestore"; final StandardMethodCodec MESSAGE_CODEC = - new StandardMethodCodec( - io.flutter.plugins.firebase.firestore.FlutterFirebaseFirestoreMessageCodec.INSTANCE); + new StandardMethodCodec(GeneratedAndroidFirebaseFirestore.PigeonCodec.INSTANCE); private BinaryMessenger binaryMessenger; @@ -180,7 +182,7 @@ private void initInstance(BinaryMessenger messenger) { FlutterFirebasePluginRegistry.registerPlugin(METHOD_CHANNEL_NAME, this); - GeneratedAndroidFirebaseFirestore.FirebaseFirestoreHostApi.setup(binaryMessenger, this); + GeneratedAndroidFirebaseFirestore.FirebaseFirestoreHostApi.setUp(binaryMessenger, this); } @Override @@ -352,10 +354,10 @@ public void loadBundle( public void namedQueryGet( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull String name, - @NonNull GeneratedAndroidFirebaseFirestore.PigeonGetOptions options, + @NonNull GeneratedAndroidFirebaseFirestore.InternalGetOptions options, @NonNull GeneratedAndroidFirebaseFirestore.Result< - GeneratedAndroidFirebaseFirestore.PigeonQuerySnapshot> + GeneratedAndroidFirebaseFirestore.InternalQuerySnapshot> result) { cachedThreadPool.execute( @@ -367,7 +369,8 @@ public void namedQueryGet( if (query == null) { result.error( new NullPointerException( - "Named query has not been found. Please check it has been loaded properly via loadBundle().")); + "Named query has not been found. Please check it has been loaded properly via" + + " loadBundle().")); return; } @@ -388,13 +391,13 @@ public void namedQueryGet( @Override public void clearPersistence( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore firestore = getFirestoreFromPigeon(app); Tasks.await(firestore.clearPersistence()); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -404,13 +407,13 @@ public void clearPersistence( @Override public void disableNetwork( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore firestore = getFirestoreFromPigeon(app); Tasks.await(firestore.disableNetwork()); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -420,13 +423,13 @@ public void disableNetwork( @Override public void enableNetwork( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore firestore = getFirestoreFromPigeon(app); Tasks.await(firestore.enableNetwork()); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -436,14 +439,14 @@ public void enableNetwork( @Override public void terminate( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore firestore = getFirestoreFromPigeon(app); Tasks.await(firestore.terminate()); destroyCachedFirebaseFirestoreInstanceForKey(firestore); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -453,13 +456,13 @@ public void terminate( @Override public void waitForPendingWrites( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore firestore = getFirestoreFromPigeon(app); Tasks.await(firestore.waitForPendingWrites()); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -472,14 +475,14 @@ public void waitForPendingWrites( public void setIndexConfiguration( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull String indexConfiguration, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore firestore = getFirestoreFromPigeon(app); Tasks.await(firestore.setIndexConfiguration(indexConfiguration)); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -490,7 +493,7 @@ public void setIndexConfiguration( public void persistenceCacheIndexManagerRequest( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull GeneratedAndroidFirebaseFirestore.PersistenceCacheIndexManagerRequest request, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { PersistentCacheIndexManager indexManager = @@ -511,20 +514,20 @@ public void persistenceCacheIndexManagerRequest( Log.d(TAG, "`PersistentCacheIndexManager` is not available."); } - result.success(null); + result.success(); }); } @Override public void setLoggingEnabled( @NonNull Boolean loggingEnabled, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore.setLoggingEnabled(loggingEnabled); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -567,12 +570,12 @@ public void transactionCreate( @Override public void transactionStoreResult( @NonNull String transactionId, - @NonNull GeneratedAndroidFirebaseFirestore.PigeonTransactionResult resultType, - @Nullable List commands, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.InternalTransactionResult resultType, + @Nullable List commands, + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { Objects.requireNonNull(transactionHandlers.get(transactionId)) .receiveTransactionResponse(resultType, commands); - result.success(null); + result.success(); } @Override @@ -582,7 +585,7 @@ public void transactionGet( @NonNull String path, @NonNull GeneratedAndroidFirebaseFirestore.Result< - GeneratedAndroidFirebaseFirestore.PigeonDocumentSnapshot> + GeneratedAndroidFirebaseFirestore.InternalDocumentSnapshot> result) { cachedThreadPool.execute( () -> { @@ -613,7 +616,7 @@ public void transactionGet( public void documentReferenceSet( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull GeneratedAndroidFirebaseFirestore.DocumentReferenceRequest request, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { @@ -636,7 +639,8 @@ public void documentReferenceSet( setTask = documentReference.set(data); } - result.success(Tasks.await(setTask)); + Tasks.await(setTask); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -647,7 +651,7 @@ public void documentReferenceSet( public void documentReferenceUpdate( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull GeneratedAndroidFirebaseFirestore.DocumentReferenceRequest request, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { @@ -680,9 +684,9 @@ public void documentReferenceUpdate( flattenData.add(fieldPath); flattenData.add(data.get(fieldPath)); } - result.success( - Tasks.await( - documentReference.update(firstFieldPath, firstObject, flattenData.toArray()))); + Tasks.await( + documentReference.update(firstFieldPath, firstObject, flattenData.toArray())); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -695,7 +699,7 @@ public void documentReferenceGet( @NonNull GeneratedAndroidFirebaseFirestore.DocumentReferenceRequest request, @NonNull GeneratedAndroidFirebaseFirestore.Result< - GeneratedAndroidFirebaseFirestore.PigeonDocumentSnapshot> + GeneratedAndroidFirebaseFirestore.InternalDocumentSnapshot> result) { cachedThreadPool.execute( () -> { @@ -723,14 +727,15 @@ public void documentReferenceGet( public void documentReferenceDelete( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull GeneratedAndroidFirebaseFirestore.DocumentReferenceRequest request, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { DocumentReference documentReference = getFirestoreFromPigeon(app).document(request.getPath()); - result.success(Tasks.await(documentReference.delete())); + Tasks.await(documentReference.delete()); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -742,11 +747,11 @@ public void queryGet( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull String path, @NonNull Boolean isCollectionGroup, - @NonNull GeneratedAndroidFirebaseFirestore.PigeonQueryParameters parameters, - @NonNull GeneratedAndroidFirebaseFirestore.PigeonGetOptions options, + @NonNull GeneratedAndroidFirebaseFirestore.InternalQueryParameters parameters, + @NonNull GeneratedAndroidFirebaseFirestore.InternalGetOptions options, @NonNull GeneratedAndroidFirebaseFirestore.Result< - GeneratedAndroidFirebaseFirestore.PigeonQuerySnapshot> + GeneratedAndroidFirebaseFirestore.InternalQuerySnapshot> result) { cachedThreadPool.execute( () -> { @@ -760,7 +765,8 @@ public void queryGet( result.error( new GeneratedAndroidFirebaseFirestore.FlutterError( "invalid_query", - "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", + "An error occurred while parsing query arguments, see native logs for more" + + " information. Please report this issue.", null)); return; } @@ -781,7 +787,7 @@ public void queryGet( public void aggregateQuery( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull String path, - @NonNull GeneratedAndroidFirebaseFirestore.PigeonQueryParameters parameters, + @NonNull GeneratedAndroidFirebaseFirestore.InternalQueryParameters parameters, @NonNull GeneratedAndroidFirebaseFirestore.AggregateSource source, @NonNull List queries, @NonNull Boolean isCollectionGroup, @@ -873,16 +879,16 @@ public void aggregateQuery( @Override public void writeBatchCommit( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, - @NonNull List writes, - @NonNull GeneratedAndroidFirebaseFirestore.Result result) { + @NonNull List writes, + @NonNull GeneratedAndroidFirebaseFirestore.VoidResult result) { cachedThreadPool.execute( () -> { try { FirebaseFirestore firestore = getFirestoreFromPigeon(app); WriteBatch batch = firestore.batch(); - for (GeneratedAndroidFirebaseFirestore.PigeonTransactionCommand write : writes) { - GeneratedAndroidFirebaseFirestore.PigeonTransactionType type = + for (GeneratedAndroidFirebaseFirestore.InternalTransactionCommand write : writes) { + GeneratedAndroidFirebaseFirestore.InternalTransactionType type = Objects.requireNonNull(write.getType()); String path = Objects.requireNonNull(write.getPath()); DocumentReference documentReference = firestore.document(path); @@ -922,7 +928,7 @@ public void writeBatchCommit( @SuppressWarnings("unchecked") Map setData = (Map) (Map) Objects.requireNonNull(write.getData()); - GeneratedAndroidFirebaseFirestore.PigeonDocumentOption options = + GeneratedAndroidFirebaseFirestore.InternalDocumentOption options = Objects.requireNonNull(write.getOption()); if (options.getMerge() != null && options.getMerge()) { @@ -945,7 +951,7 @@ public void writeBatchCommit( } Tasks.await(batch.commit()); - result.success(null); + result.success(); } catch (Exception e) { ExceptionConverter.sendErrorToFlutter(result, e); } @@ -957,8 +963,8 @@ public void querySnapshot( @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, @NonNull String path, @NonNull Boolean isCollectionGroup, - @NonNull GeneratedAndroidFirebaseFirestore.PigeonQueryParameters parameters, - @NonNull GeneratedAndroidFirebaseFirestore.PigeonGetOptions options, + @NonNull GeneratedAndroidFirebaseFirestore.InternalQueryParameters parameters, + @NonNull GeneratedAndroidFirebaseFirestore.InternalGetOptions options, @NonNull Boolean includeMetadataChanges, @NonNull GeneratedAndroidFirebaseFirestore.ListenSource source, @NonNull GeneratedAndroidFirebaseFirestore.Result result) { @@ -969,7 +975,8 @@ public void querySnapshot( result.error( new GeneratedAndroidFirebaseFirestore.FlutterError( "invalid_query", - "An error occurred while parsing query arguments, see native logs for more information. Please report this issue.", + "An error occurred while parsing query arguments, see native logs for more" + + " information. Please report this issue.", null)); return; } @@ -982,7 +989,8 @@ public void querySnapshot( includeMetadataChanges, PigeonParser.parsePigeonServerTimestampBehavior( options.getServerTimestampBehavior()), - PigeonParser.parseListenSource(source)))); + PigeonParser.parseListenSource(source), + cachedThreadPool))); } @Override @@ -1007,4 +1015,65 @@ public void documentReferenceSnapshot( parameters.getServerTimestampBehavior()), PigeonParser.parseListenSource(source)))); } + + @Override + public void executePipeline( + @NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app, + @NonNull List> stages, + @Nullable Map options, + @NonNull + GeneratedAndroidFirebaseFirestore.Result< + GeneratedAndroidFirebaseFirestore.InternalPipelineSnapshot> + result) { + cachedThreadPool.execute( + () -> { + try { + FirebaseFirestore firestore = getFirestoreFromPigeon(app); + + // Execute pipeline using Android Firestore SDK + Pipeline.Snapshot snapshot = PipelineParser.executePipeline(firestore, stages, options); + + // Convert Pipeline.Snapshot to InternalPipelineSnapshot + List pipelineResults = + new ArrayList<>(); + + // Iterate through snapshot results + for (PipelineResult pipelineResult : snapshot.getResults()) { + GeneratedAndroidFirebaseFirestore.InternalPipelineResult.Builder resultBuilder = + new GeneratedAndroidFirebaseFirestore.InternalPipelineResult.Builder(); + if (pipelineResult.getRef() != null) { + resultBuilder.setDocumentPath(pipelineResult.getRef().getPath()); + } + + if (pipelineResult.getCreateTime() != null) { + resultBuilder.setCreateTime(pipelineResult.getCreateTime().toDate().getTime()); + } + if (pipelineResult.getUpdateTime() != null) { + resultBuilder.setUpdateTime(pipelineResult.getUpdateTime().toDate().getTime()); + } + + Map data = pipelineResult.getData(); + if (data != null) { + resultBuilder.setData(data); + } + + pipelineResults.add(resultBuilder.build()); + } + + // Build the snapshot + GeneratedAndroidFirebaseFirestore.InternalPipelineSnapshot.Builder snapshotBuilder = + new GeneratedAndroidFirebaseFirestore.InternalPipelineSnapshot.Builder(); + snapshotBuilder.setResults(pipelineResults); + + // Set execution time when available. Do not fabricate a value when null. + if (snapshot.getExecutionTime() != null) { + snapshotBuilder.setExecutionTime(snapshot.getExecutionTime().toDate().getTime()); + } + + result.success(snapshotBuilder.build()); + } catch (Exception e) { + ExceptionConverter.sendErrorToFlutter(result, e); + } + }); + } } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java index b67642961626..9e4321e264c9 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java @@ -1,11 +1,14 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.firebase.firestore; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.CLASS; + import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -13,14 +16,174 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MessageCodec; import java.io.ByteArrayOutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; /** Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class GeneratedAndroidFirebaseFirestore { + static boolean pigeonDoubleEquals(double a, double b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b)); + } + + static boolean pigeonFloatEquals(float a, float b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b)); + } + + static int pigeonDoubleHashCode(double d) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (d == 0.0) { + d = 0.0; + } + long bits = Double.doubleToLongBits(d); + return (int) (bits ^ (bits >>> 32)); + } + + static int pigeonFloatHashCode(float f) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (f == 0.0f) { + f = 0.0f; + } + return Float.floatToIntBits(f); + } + + static boolean pigeonDeepEquals(Object a, Object b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + if (a instanceof byte[] && b instanceof byte[]) { + return Arrays.equals((byte[]) a, (byte[]) b); + } + if (a instanceof int[] && b instanceof int[]) { + return Arrays.equals((int[]) a, (int[]) b); + } + if (a instanceof long[] && b instanceof long[]) { + return Arrays.equals((long[]) a, (long[]) b); + } + if (a instanceof double[] && b instanceof double[]) { + double[] da = (double[]) a; + double[] db = (double[]) b; + if (da.length != db.length) { + return false; + } + for (int i = 0; i < da.length; i++) { + if (!pigeonDoubleEquals(da[i], db[i])) { + return false; + } + } + return true; + } + if (a instanceof List && b instanceof List) { + List listA = (List) a; + List listB = (List) b; + if (listA.size() != listB.size()) { + return false; + } + for (int i = 0; i < listA.size(); i++) { + if (!pigeonDeepEquals(listA.get(i), listB.get(i))) { + return false; + } + } + return true; + } + if (a instanceof Map && b instanceof Map) { + Map mapA = (Map) a; + Map mapB = (Map) b; + if (mapA.size() != mapB.size()) { + return false; + } + for (Map.Entry entryA : mapA.entrySet()) { + Object keyA = entryA.getKey(); + Object valueA = entryA.getValue(); + boolean found = false; + for (Map.Entry entryB : mapB.entrySet()) { + Object keyB = entryB.getKey(); + if (pigeonDeepEquals(keyA, keyB)) { + Object valueB = entryB.getValue(); + if (pigeonDeepEquals(valueA, valueB)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + if (a instanceof Double && b instanceof Double) { + return pigeonDoubleEquals((double) a, (double) b); + } + if (a instanceof Float && b instanceof Float) { + return pigeonFloatEquals((float) a, (float) b); + } + return a.equals(b); + } + + static int pigeonDeepHashCode(Object value) { + if (value == null) { + return 0; + } + if (value instanceof byte[]) { + return Arrays.hashCode((byte[]) value); + } + if (value instanceof int[]) { + return Arrays.hashCode((int[]) value); + } + if (value instanceof long[]) { + return Arrays.hashCode((long[]) value); + } + if (value instanceof double[]) { + double[] da = (double[]) value; + int result = 1; + for (double d : da) { + result = 31 * result + pigeonDoubleHashCode(d); + } + return result; + } + if (value instanceof List) { + int result = 1; + for (Object item : (List) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Map) { + int result = 0; + for (Map.Entry entry : ((Map) value).entrySet()) { + result += + ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue())); + } + return result; + } + if (value instanceof Object[]) { + int result = 1; + for (Object item : (Object[]) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Double) { + return pigeonDoubleHashCode((double) value); + } + if (value instanceof Float) { + return pigeonFloatHashCode((float) value); + } + return value.hashCode(); + } /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ public static class FlutterError extends RuntimeException { @@ -40,7 +203,7 @@ public FlutterError(@NonNull String code, @Nullable String message, @Nullable Ob @NonNull protected static ArrayList wrapError(@NonNull Throwable exception) { - ArrayList errorList = new ArrayList(3); + ArrayList errorList = new ArrayList<>(3); if (exception instanceof FlutterError) { FlutterError error = (FlutterError) exception; errorList.add(error.code); @@ -55,6 +218,10 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { return errorList; } + @Target(METHOD) + @Retention(CLASS) + @interface CanIgnoreReturnValue {} + /** An enumeration of document change types. */ public enum DocumentChangeType { /** Indicates a new document was added to the set of documents matching the query. */ @@ -69,7 +236,7 @@ public enum DocumentChangeType { final int index; - private DocumentChangeType(final int index) { + DocumentChangeType(final int index) { this.index = index; } } @@ -99,7 +266,7 @@ public enum Source { final int index; - private Source(final int index) { + Source(final int index) { this.index = index; } } @@ -128,7 +295,7 @@ public enum ListenSource { final int index; - private ListenSource(final int index) { + ListenSource(final int index) { this.index = index; } } @@ -149,7 +316,7 @@ public enum ServerTimestampBehavior { final int index; - private ServerTimestampBehavior(final int index) { + ServerTimestampBehavior(final int index) { this.index = index; } } @@ -161,7 +328,7 @@ public enum AggregateSource { final int index; - private AggregateSource(final int index) { + AggregateSource(final int index) { this.index = index; } } @@ -177,23 +344,23 @@ public enum PersistenceCacheIndexManagerRequest { final int index; - private PersistenceCacheIndexManagerRequest(final int index) { + PersistenceCacheIndexManagerRequest(final int index) { this.index = index; } } - public enum PigeonTransactionResult { + public enum InternalTransactionResult { SUCCESS(0), FAILURE(1); final int index; - private PigeonTransactionResult(final int index) { + InternalTransactionResult(final int index) { this.index = index; } } - public enum PigeonTransactionType { + public enum InternalTransactionType { GET(0), UPDATE(1), SET(2), @@ -201,7 +368,7 @@ public enum PigeonTransactionType { final int index; - private PigeonTransactionType(final int index) { + InternalTransactionType(final int index) { this.index = index; } } @@ -213,13 +380,13 @@ public enum AggregateType { final int index; - private AggregateType(final int index) { + AggregateType(final int index) { this.index = index; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonFirebaseSettings { + public static final class InternalFirebaseSettings { private @Nullable Boolean persistenceEnabled; public @Nullable Boolean getPersistenceEnabled() { @@ -274,12 +441,43 @@ public void setIgnoreUndefinedProperties(@NonNull Boolean setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonFirebaseSettings() {} + InternalFirebaseSettings() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalFirebaseSettings that = (InternalFirebaseSettings) o; + return pigeonDeepEquals(persistenceEnabled, that.persistenceEnabled) + && pigeonDeepEquals(host, that.host) + && pigeonDeepEquals(sslEnabled, that.sslEnabled) + && pigeonDeepEquals(cacheSizeBytes, that.cacheSizeBytes) + && pigeonDeepEquals(ignoreUndefinedProperties, that.ignoreUndefinedProperties); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + persistenceEnabled, + host, + sslEnabled, + cacheSizeBytes, + ignoreUndefinedProperties + }; + return pigeonDeepHashCode(fields); + } public static final class Builder { private @Nullable Boolean persistenceEnabled; + @CanIgnoreReturnValue public @NonNull Builder setPersistenceEnabled(@Nullable Boolean setterArg) { this.persistenceEnabled = setterArg; return this; @@ -287,6 +485,7 @@ public static final class Builder { private @Nullable String host; + @CanIgnoreReturnValue public @NonNull Builder setHost(@Nullable String setterArg) { this.host = setterArg; return this; @@ -294,6 +493,7 @@ public static final class Builder { private @Nullable Boolean sslEnabled; + @CanIgnoreReturnValue public @NonNull Builder setSslEnabled(@Nullable Boolean setterArg) { this.sslEnabled = setterArg; return this; @@ -301,6 +501,7 @@ public static final class Builder { private @Nullable Long cacheSizeBytes; + @CanIgnoreReturnValue public @NonNull Builder setCacheSizeBytes(@Nullable Long setterArg) { this.cacheSizeBytes = setterArg; return this; @@ -308,13 +509,14 @@ public static final class Builder { private @Nullable Boolean ignoreUndefinedProperties; + @CanIgnoreReturnValue public @NonNull Builder setIgnoreUndefinedProperties(@NonNull Boolean setterArg) { this.ignoreUndefinedProperties = setterArg; return this; } - public @NonNull PigeonFirebaseSettings build() { - PigeonFirebaseSettings pigeonReturn = new PigeonFirebaseSettings(); + public @NonNull InternalFirebaseSettings build() { + InternalFirebaseSettings pigeonReturn = new InternalFirebaseSettings(); pigeonReturn.setPersistenceEnabled(persistenceEnabled); pigeonReturn.setHost(host); pigeonReturn.setSslEnabled(sslEnabled); @@ -326,7 +528,7 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(5); + ArrayList toListResult = new ArrayList<>(5); toListResult.add(persistenceEnabled); toListResult.add(host); toListResult.add(sslEnabled); @@ -335,22 +537,17 @@ public ArrayList toList() { return toListResult; } - static @NonNull PigeonFirebaseSettings fromList(@NonNull ArrayList list) { - PigeonFirebaseSettings pigeonResult = new PigeonFirebaseSettings(); - Object persistenceEnabled = list.get(0); + static @NonNull InternalFirebaseSettings fromList(@NonNull ArrayList pigeonVar_list) { + InternalFirebaseSettings pigeonResult = new InternalFirebaseSettings(); + Object persistenceEnabled = pigeonVar_list.get(0); pigeonResult.setPersistenceEnabled((Boolean) persistenceEnabled); - Object host = list.get(1); + Object host = pigeonVar_list.get(1); pigeonResult.setHost((String) host); - Object sslEnabled = list.get(2); + Object sslEnabled = pigeonVar_list.get(2); pigeonResult.setSslEnabled((Boolean) sslEnabled); - Object cacheSizeBytes = list.get(3); - pigeonResult.setCacheSizeBytes( - (cacheSizeBytes == null) - ? null - : ((cacheSizeBytes instanceof Integer) - ? (Integer) cacheSizeBytes - : (Long) cacheSizeBytes)); - Object ignoreUndefinedProperties = list.get(4); + Object cacheSizeBytes = pigeonVar_list.get(3); + pigeonResult.setCacheSizeBytes((Long) cacheSizeBytes); + Object ignoreUndefinedProperties = pigeonVar_list.get(4); pigeonResult.setIgnoreUndefinedProperties((Boolean) ignoreUndefinedProperties); return pigeonResult; } @@ -371,13 +568,13 @@ public void setAppName(@NonNull String setterArg) { this.appName = setterArg; } - private @NonNull PigeonFirebaseSettings settings; + private @NonNull InternalFirebaseSettings settings; - public @NonNull PigeonFirebaseSettings getSettings() { + public @NonNull InternalFirebaseSettings getSettings() { return settings; } - public void setSettings(@NonNull PigeonFirebaseSettings setterArg) { + public void setSettings(@NonNull InternalFirebaseSettings setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"settings\" is null."); } @@ -400,24 +597,47 @@ public void setDatabaseURL(@NonNull String setterArg) { /** Constructor is non-public to enforce null safety; use Builder. */ FirestorePigeonFirebaseApp() {} + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FirestorePigeonFirebaseApp that = (FirestorePigeonFirebaseApp) o; + return pigeonDeepEquals(appName, that.appName) + && pigeonDeepEquals(settings, that.settings) + && pigeonDeepEquals(databaseURL, that.databaseURL); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), appName, settings, databaseURL}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable String appName; + @CanIgnoreReturnValue public @NonNull Builder setAppName(@NonNull String setterArg) { this.appName = setterArg; return this; } - private @Nullable PigeonFirebaseSettings settings; + private @Nullable InternalFirebaseSettings settings; - public @NonNull Builder setSettings(@NonNull PigeonFirebaseSettings setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setSettings(@NonNull InternalFirebaseSettings setterArg) { this.settings = setterArg; return this; } private @Nullable String databaseURL; + @CanIgnoreReturnValue public @NonNull Builder setDatabaseURL(@NonNull String setterArg) { this.databaseURL = setterArg; return this; @@ -434,30 +654,27 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(3); + ArrayList toListResult = new ArrayList<>(3); toListResult.add(appName); - toListResult.add((settings == null) ? null : settings.toList()); + toListResult.add(settings); toListResult.add(databaseURL); return toListResult; } - static @NonNull FirestorePigeonFirebaseApp fromList(@NonNull ArrayList list) { + static @NonNull FirestorePigeonFirebaseApp fromList(@NonNull ArrayList pigeonVar_list) { FirestorePigeonFirebaseApp pigeonResult = new FirestorePigeonFirebaseApp(); - Object appName = list.get(0); + Object appName = pigeonVar_list.get(0); pigeonResult.setAppName((String) appName); - Object settings = list.get(1); - pigeonResult.setSettings( - (settings == null) - ? null - : PigeonFirebaseSettings.fromList((ArrayList) settings)); - Object databaseURL = list.get(2); + Object settings = pigeonVar_list.get(1); + pigeonResult.setSettings((InternalFirebaseSettings) settings); + Object databaseURL = pigeonVar_list.get(2); pigeonResult.setDatabaseURL((String) databaseURL); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonSnapshotMetadata { + public static final class InternalSnapshotMetadata { private @NonNull Boolean hasPendingWrites; public @NonNull Boolean getHasPendingWrites() { @@ -485,12 +702,32 @@ public void setIsFromCache(@NonNull Boolean setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonSnapshotMetadata() {} + InternalSnapshotMetadata() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalSnapshotMetadata that = (InternalSnapshotMetadata) o; + return pigeonDeepEquals(hasPendingWrites, that.hasPendingWrites) + && pigeonDeepEquals(isFromCache, that.isFromCache); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), hasPendingWrites, isFromCache}; + return pigeonDeepHashCode(fields); + } public static final class Builder { private @Nullable Boolean hasPendingWrites; + @CanIgnoreReturnValue public @NonNull Builder setHasPendingWrites(@NonNull Boolean setterArg) { this.hasPendingWrites = setterArg; return this; @@ -498,13 +735,14 @@ public static final class Builder { private @Nullable Boolean isFromCache; + @CanIgnoreReturnValue public @NonNull Builder setIsFromCache(@NonNull Boolean setterArg) { this.isFromCache = setterArg; return this; } - public @NonNull PigeonSnapshotMetadata build() { - PigeonSnapshotMetadata pigeonReturn = new PigeonSnapshotMetadata(); + public @NonNull InternalSnapshotMetadata build() { + InternalSnapshotMetadata pigeonReturn = new InternalSnapshotMetadata(); pigeonReturn.setHasPendingWrites(hasPendingWrites); pigeonReturn.setIsFromCache(isFromCache); return pigeonReturn; @@ -513,24 +751,24 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(2); + ArrayList toListResult = new ArrayList<>(2); toListResult.add(hasPendingWrites); toListResult.add(isFromCache); return toListResult; } - static @NonNull PigeonSnapshotMetadata fromList(@NonNull ArrayList list) { - PigeonSnapshotMetadata pigeonResult = new PigeonSnapshotMetadata(); - Object hasPendingWrites = list.get(0); + static @NonNull InternalSnapshotMetadata fromList(@NonNull ArrayList pigeonVar_list) { + InternalSnapshotMetadata pigeonResult = new InternalSnapshotMetadata(); + Object hasPendingWrites = pigeonVar_list.get(0); pigeonResult.setHasPendingWrites((Boolean) hasPendingWrites); - Object isFromCache = list.get(1); + Object isFromCache = pigeonVar_list.get(1); pigeonResult.setIsFromCache((Boolean) isFromCache); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonDocumentSnapshot { + public static final class InternalDocumentSnapshot { private @NonNull String path; public @NonNull String getPath() { @@ -554,13 +792,13 @@ public void setData(@Nullable Map setterArg) { this.data = setterArg; } - private @NonNull PigeonSnapshotMetadata metadata; + private @NonNull InternalSnapshotMetadata metadata; - public @NonNull PigeonSnapshotMetadata getMetadata() { + public @NonNull InternalSnapshotMetadata getMetadata() { return metadata; } - public void setMetadata(@NonNull PigeonSnapshotMetadata setterArg) { + public void setMetadata(@NonNull InternalSnapshotMetadata setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"metadata\" is null."); } @@ -568,12 +806,33 @@ public void setMetadata(@NonNull PigeonSnapshotMetadata setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonDocumentSnapshot() {} + InternalDocumentSnapshot() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalDocumentSnapshot that = (InternalDocumentSnapshot) o; + return pigeonDeepEquals(path, that.path) + && pigeonDeepEquals(data, that.data) + && pigeonDeepEquals(metadata, that.metadata); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), path, data, metadata}; + return pigeonDeepHashCode(fields); + } public static final class Builder { private @Nullable String path; + @CanIgnoreReturnValue public @NonNull Builder setPath(@NonNull String setterArg) { this.path = setterArg; return this; @@ -581,20 +840,22 @@ public static final class Builder { private @Nullable Map data; + @CanIgnoreReturnValue public @NonNull Builder setData(@Nullable Map setterArg) { this.data = setterArg; return this; } - private @Nullable PigeonSnapshotMetadata metadata; + private @Nullable InternalSnapshotMetadata metadata; - public @NonNull Builder setMetadata(@NonNull PigeonSnapshotMetadata setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setMetadata(@NonNull InternalSnapshotMetadata setterArg) { this.metadata = setterArg; return this; } - public @NonNull PigeonDocumentSnapshot build() { - PigeonDocumentSnapshot pigeonReturn = new PigeonDocumentSnapshot(); + public @NonNull InternalDocumentSnapshot build() { + InternalDocumentSnapshot pigeonReturn = new InternalDocumentSnapshot(); pigeonReturn.setPath(path); pigeonReturn.setData(data); pigeonReturn.setMetadata(metadata); @@ -604,30 +865,27 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(3); + ArrayList toListResult = new ArrayList<>(3); toListResult.add(path); toListResult.add(data); - toListResult.add((metadata == null) ? null : metadata.toList()); + toListResult.add(metadata); return toListResult; } - static @NonNull PigeonDocumentSnapshot fromList(@NonNull ArrayList list) { - PigeonDocumentSnapshot pigeonResult = new PigeonDocumentSnapshot(); - Object path = list.get(0); + static @NonNull InternalDocumentSnapshot fromList(@NonNull ArrayList pigeonVar_list) { + InternalDocumentSnapshot pigeonResult = new InternalDocumentSnapshot(); + Object path = pigeonVar_list.get(0); pigeonResult.setPath((String) path); - Object data = list.get(1); + Object data = pigeonVar_list.get(1); pigeonResult.setData((Map) data); - Object metadata = list.get(2); - pigeonResult.setMetadata( - (metadata == null) - ? null - : PigeonSnapshotMetadata.fromList((ArrayList) metadata)); + Object metadata = pigeonVar_list.get(2); + pigeonResult.setMetadata((InternalSnapshotMetadata) metadata); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonDocumentChange { + public static final class InternalDocumentChange { private @NonNull DocumentChangeType type; public @NonNull DocumentChangeType getType() { @@ -641,13 +899,13 @@ public void setType(@NonNull DocumentChangeType setterArg) { this.type = setterArg; } - private @NonNull PigeonDocumentSnapshot document; + private @NonNull InternalDocumentSnapshot document; - public @NonNull PigeonDocumentSnapshot getDocument() { + public @NonNull InternalDocumentSnapshot getDocument() { return document; } - public void setDocument(@NonNull PigeonDocumentSnapshot setterArg) { + public void setDocument(@NonNull InternalDocumentSnapshot setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"document\" is null."); } @@ -681,26 +939,50 @@ public void setNewIndex(@NonNull Long setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonDocumentChange() {} + InternalDocumentChange() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalDocumentChange that = (InternalDocumentChange) o; + return pigeonDeepEquals(type, that.type) + && pigeonDeepEquals(document, that.document) + && pigeonDeepEquals(oldIndex, that.oldIndex) + && pigeonDeepEquals(newIndex, that.newIndex); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), type, document, oldIndex, newIndex}; + return pigeonDeepHashCode(fields); + } public static final class Builder { private @Nullable DocumentChangeType type; + @CanIgnoreReturnValue public @NonNull Builder setType(@NonNull DocumentChangeType setterArg) { this.type = setterArg; return this; } - private @Nullable PigeonDocumentSnapshot document; + private @Nullable InternalDocumentSnapshot document; - public @NonNull Builder setDocument(@NonNull PigeonDocumentSnapshot setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setDocument(@NonNull InternalDocumentSnapshot setterArg) { this.document = setterArg; return this; } private @Nullable Long oldIndex; + @CanIgnoreReturnValue public @NonNull Builder setOldIndex(@NonNull Long setterArg) { this.oldIndex = setterArg; return this; @@ -708,13 +990,14 @@ public static final class Builder { private @Nullable Long newIndex; + @CanIgnoreReturnValue public @NonNull Builder setNewIndex(@NonNull Long setterArg) { this.newIndex = setterArg; return this; } - public @NonNull PigeonDocumentChange build() { - PigeonDocumentChange pigeonReturn = new PigeonDocumentChange(); + public @NonNull InternalDocumentChange build() { + InternalDocumentChange pigeonReturn = new InternalDocumentChange(); pigeonReturn.setType(type); pigeonReturn.setDocument(document); pigeonReturn.setOldIndex(oldIndex); @@ -725,72 +1008,63 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(4); - toListResult.add(type == null ? null : type.index); - toListResult.add((document == null) ? null : document.toList()); + ArrayList toListResult = new ArrayList<>(4); + toListResult.add(type); + toListResult.add(document); toListResult.add(oldIndex); toListResult.add(newIndex); return toListResult; } - static @NonNull PigeonDocumentChange fromList(@NonNull ArrayList list) { - PigeonDocumentChange pigeonResult = new PigeonDocumentChange(); - Object type = list.get(0); - pigeonResult.setType(DocumentChangeType.values()[(int) type]); - Object document = list.get(1); - pigeonResult.setDocument( - (document == null) - ? null - : PigeonDocumentSnapshot.fromList((ArrayList) document)); - Object oldIndex = list.get(2); - pigeonResult.setOldIndex( - (oldIndex == null) - ? null - : ((oldIndex instanceof Integer) ? (Integer) oldIndex : (Long) oldIndex)); - Object newIndex = list.get(3); - pigeonResult.setNewIndex( - (newIndex == null) - ? null - : ((newIndex instanceof Integer) ? (Integer) newIndex : (Long) newIndex)); + static @NonNull InternalDocumentChange fromList(@NonNull ArrayList pigeonVar_list) { + InternalDocumentChange pigeonResult = new InternalDocumentChange(); + Object type = pigeonVar_list.get(0); + pigeonResult.setType((DocumentChangeType) type); + Object document = pigeonVar_list.get(1); + pigeonResult.setDocument((InternalDocumentSnapshot) document); + Object oldIndex = pigeonVar_list.get(2); + pigeonResult.setOldIndex((Long) oldIndex); + Object newIndex = pigeonVar_list.get(3); + pigeonResult.setNewIndex((Long) newIndex); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonQuerySnapshot { - private @NonNull List documents; + public static final class InternalQuerySnapshot { + private @NonNull List documents; - public @NonNull List getDocuments() { + public @NonNull List getDocuments() { return documents; } - public void setDocuments(@NonNull List setterArg) { + public void setDocuments(@NonNull List setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"documents\" is null."); } this.documents = setterArg; } - private @NonNull List documentChanges; + private @NonNull List documentChanges; - public @NonNull List getDocumentChanges() { + public @NonNull List getDocumentChanges() { return documentChanges; } - public void setDocumentChanges(@NonNull List setterArg) { + public void setDocumentChanges(@NonNull List setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"documentChanges\" is null."); } this.documentChanges = setterArg; } - private @NonNull PigeonSnapshotMetadata metadata; + private @NonNull InternalSnapshotMetadata metadata; - public @NonNull PigeonSnapshotMetadata getMetadata() { + public @NonNull InternalSnapshotMetadata getMetadata() { return metadata; } - public void setMetadata(@NonNull PigeonSnapshotMetadata setterArg) { + public void setMetadata(@NonNull InternalSnapshotMetadata setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"metadata\" is null."); } @@ -798,33 +1072,56 @@ public void setMetadata(@NonNull PigeonSnapshotMetadata setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonQuerySnapshot() {} + InternalQuerySnapshot() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalQuerySnapshot that = (InternalQuerySnapshot) o; + return pigeonDeepEquals(documents, that.documents) + && pigeonDeepEquals(documentChanges, that.documentChanges) + && pigeonDeepEquals(metadata, that.metadata); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), documents, documentChanges, metadata}; + return pigeonDeepHashCode(fields); + } public static final class Builder { - private @Nullable List documents; + private @Nullable List documents; - public @NonNull Builder setDocuments(@NonNull List setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setDocuments(@NonNull List setterArg) { this.documents = setterArg; return this; } - private @Nullable List documentChanges; + private @Nullable List documentChanges; - public @NonNull Builder setDocumentChanges(@NonNull List setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setDocumentChanges(@NonNull List setterArg) { this.documentChanges = setterArg; return this; } - private @Nullable PigeonSnapshotMetadata metadata; + private @Nullable InternalSnapshotMetadata metadata; - public @NonNull Builder setMetadata(@NonNull PigeonSnapshotMetadata setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setMetadata(@NonNull InternalSnapshotMetadata setterArg) { this.metadata = setterArg; return this; } - public @NonNull PigeonQuerySnapshot build() { - PigeonQuerySnapshot pigeonReturn = new PigeonQuerySnapshot(); + public @NonNull InternalQuerySnapshot build() { + InternalQuerySnapshot pigeonReturn = new InternalQuerySnapshot(); pigeonReturn.setDocuments(documents); pigeonReturn.setDocumentChanges(documentChanges); pigeonReturn.setMetadata(metadata); @@ -834,30 +1131,253 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(3); + ArrayList toListResult = new ArrayList<>(3); toListResult.add(documents); toListResult.add(documentChanges); - toListResult.add((metadata == null) ? null : metadata.toList()); + toListResult.add(metadata); + return toListResult; + } + + static @NonNull InternalQuerySnapshot fromList(@NonNull ArrayList pigeonVar_list) { + InternalQuerySnapshot pigeonResult = new InternalQuerySnapshot(); + Object documents = pigeonVar_list.get(0); + pigeonResult.setDocuments((List) documents); + Object documentChanges = pigeonVar_list.get(1); + pigeonResult.setDocumentChanges((List) documentChanges); + Object metadata = pigeonVar_list.get(2); + pigeonResult.setMetadata((InternalSnapshotMetadata) metadata); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class InternalPipelineResult { + private @Nullable String documentPath; + + public @Nullable String getDocumentPath() { + return documentPath; + } + + public void setDocumentPath(@Nullable String setterArg) { + this.documentPath = setterArg; + } + + private @Nullable Long createTime; + + public @Nullable Long getCreateTime() { + return createTime; + } + + public void setCreateTime(@Nullable Long setterArg) { + this.createTime = setterArg; + } + + private @Nullable Long updateTime; + + public @Nullable Long getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(@Nullable Long setterArg) { + this.updateTime = setterArg; + } + + /** All fields in the result (from PipelineResult.data() on Android). */ + private @Nullable Map data; + + public @Nullable Map getData() { + return data; + } + + public void setData(@Nullable Map setterArg) { + this.data = setterArg; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalPipelineResult that = (InternalPipelineResult) o; + return pigeonDeepEquals(documentPath, that.documentPath) + && pigeonDeepEquals(createTime, that.createTime) + && pigeonDeepEquals(updateTime, that.updateTime) + && pigeonDeepEquals(data, that.data); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), documentPath, createTime, updateTime, data}; + return pigeonDeepHashCode(fields); + } + + public static final class Builder { + + private @Nullable String documentPath; + + @CanIgnoreReturnValue + public @NonNull Builder setDocumentPath(@Nullable String setterArg) { + this.documentPath = setterArg; + return this; + } + + private @Nullable Long createTime; + + @CanIgnoreReturnValue + public @NonNull Builder setCreateTime(@Nullable Long setterArg) { + this.createTime = setterArg; + return this; + } + + private @Nullable Long updateTime; + + @CanIgnoreReturnValue + public @NonNull Builder setUpdateTime(@Nullable Long setterArg) { + this.updateTime = setterArg; + return this; + } + + private @Nullable Map data; + + @CanIgnoreReturnValue + public @NonNull Builder setData(@Nullable Map setterArg) { + this.data = setterArg; + return this; + } + + public @NonNull InternalPipelineResult build() { + InternalPipelineResult pigeonReturn = new InternalPipelineResult(); + pigeonReturn.setDocumentPath(documentPath); + pigeonReturn.setCreateTime(createTime); + pigeonReturn.setUpdateTime(updateTime); + pigeonReturn.setData(data); + return pigeonReturn; + } + } + + @NonNull + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(4); + toListResult.add(documentPath); + toListResult.add(createTime); + toListResult.add(updateTime); + toListResult.add(data); + return toListResult; + } + + static @NonNull InternalPipelineResult fromList(@NonNull ArrayList pigeonVar_list) { + InternalPipelineResult pigeonResult = new InternalPipelineResult(); + Object documentPath = pigeonVar_list.get(0); + pigeonResult.setDocumentPath((String) documentPath); + Object createTime = pigeonVar_list.get(1); + pigeonResult.setCreateTime((Long) createTime); + Object updateTime = pigeonVar_list.get(2); + pigeonResult.setUpdateTime((Long) updateTime); + Object data = pigeonVar_list.get(3); + pigeonResult.setData((Map) data); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class InternalPipelineSnapshot { + private @NonNull List results; + + public @NonNull List getResults() { + return results; + } + + public void setResults(@NonNull List setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"results\" is null."); + } + this.results = setterArg; + } + + private @NonNull Long executionTime; + + public @NonNull Long getExecutionTime() { + return executionTime; + } + + public void setExecutionTime(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"executionTime\" is null."); + } + this.executionTime = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + InternalPipelineSnapshot() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalPipelineSnapshot that = (InternalPipelineSnapshot) o; + return pigeonDeepEquals(results, that.results) + && pigeonDeepEquals(executionTime, that.executionTime); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), results, executionTime}; + return pigeonDeepHashCode(fields); + } + + public static final class Builder { + + private @Nullable List results; + + @CanIgnoreReturnValue + public @NonNull Builder setResults(@NonNull List setterArg) { + this.results = setterArg; + return this; + } + + private @Nullable Long executionTime; + + @CanIgnoreReturnValue + public @NonNull Builder setExecutionTime(@NonNull Long setterArg) { + this.executionTime = setterArg; + return this; + } + + public @NonNull InternalPipelineSnapshot build() { + InternalPipelineSnapshot pigeonReturn = new InternalPipelineSnapshot(); + pigeonReturn.setResults(results); + pigeonReturn.setExecutionTime(executionTime); + return pigeonReturn; + } + } + + @NonNull + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(results); + toListResult.add(executionTime); return toListResult; } - static @NonNull PigeonQuerySnapshot fromList(@NonNull ArrayList list) { - PigeonQuerySnapshot pigeonResult = new PigeonQuerySnapshot(); - Object documents = list.get(0); - pigeonResult.setDocuments((List) documents); - Object documentChanges = list.get(1); - pigeonResult.setDocumentChanges((List) documentChanges); - Object metadata = list.get(2); - pigeonResult.setMetadata( - (metadata == null) - ? null - : PigeonSnapshotMetadata.fromList((ArrayList) metadata)); + static @NonNull InternalPipelineSnapshot fromList(@NonNull ArrayList pigeonVar_list) { + InternalPipelineSnapshot pigeonResult = new InternalPipelineSnapshot(); + Object results = pigeonVar_list.get(0); + pigeonResult.setResults((List) results); + Object executionTime = pigeonVar_list.get(1); + pigeonResult.setExecutionTime((Long) executionTime); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonGetOptions { + public static final class InternalGetOptions { private @NonNull Source source; public @NonNull Source getSource() { @@ -885,12 +1405,32 @@ public void setServerTimestampBehavior(@NonNull ServerTimestampBehavior setterAr } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonGetOptions() {} + InternalGetOptions() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalGetOptions that = (InternalGetOptions) o; + return pigeonDeepEquals(source, that.source) + && pigeonDeepEquals(serverTimestampBehavior, that.serverTimestampBehavior); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), source, serverTimestampBehavior}; + return pigeonDeepHashCode(fields); + } public static final class Builder { private @Nullable Source source; + @CanIgnoreReturnValue public @NonNull Builder setSource(@NonNull Source setterArg) { this.source = setterArg; return this; @@ -898,14 +1438,15 @@ public static final class Builder { private @Nullable ServerTimestampBehavior serverTimestampBehavior; + @CanIgnoreReturnValue public @NonNull Builder setServerTimestampBehavior( @NonNull ServerTimestampBehavior setterArg) { this.serverTimestampBehavior = setterArg; return this; } - public @NonNull PigeonGetOptions build() { - PigeonGetOptions pigeonReturn = new PigeonGetOptions(); + public @NonNull InternalGetOptions build() { + InternalGetOptions pigeonReturn = new InternalGetOptions(); pigeonReturn.setSource(source); pigeonReturn.setServerTimestampBehavior(serverTimestampBehavior); return pigeonReturn; @@ -914,25 +1455,24 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(2); - toListResult.add(source == null ? null : source.index); - toListResult.add(serverTimestampBehavior == null ? null : serverTimestampBehavior.index); + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(source); + toListResult.add(serverTimestampBehavior); return toListResult; } - static @NonNull PigeonGetOptions fromList(@NonNull ArrayList list) { - PigeonGetOptions pigeonResult = new PigeonGetOptions(); - Object source = list.get(0); - pigeonResult.setSource(Source.values()[(int) source]); - Object serverTimestampBehavior = list.get(1); - pigeonResult.setServerTimestampBehavior( - ServerTimestampBehavior.values()[(int) serverTimestampBehavior]); + static @NonNull InternalGetOptions fromList(@NonNull ArrayList pigeonVar_list) { + InternalGetOptions pigeonResult = new InternalGetOptions(); + Object source = pigeonVar_list.get(0); + pigeonResult.setSource((Source) source); + Object serverTimestampBehavior = pigeonVar_list.get(1); + pigeonResult.setServerTimestampBehavior((ServerTimestampBehavior) serverTimestampBehavior); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonDocumentOption { + public static final class InternalDocumentOption { private @Nullable Boolean merge; public @Nullable Boolean getMerge() { @@ -953,10 +1493,29 @@ public void setMergeFields(@Nullable List> setterArg) { this.mergeFields = setterArg; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalDocumentOption that = (InternalDocumentOption) o; + return pigeonDeepEquals(merge, that.merge) && pigeonDeepEquals(mergeFields, that.mergeFields); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), merge, mergeFields}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable Boolean merge; + @CanIgnoreReturnValue public @NonNull Builder setMerge(@Nullable Boolean setterArg) { this.merge = setterArg; return this; @@ -964,13 +1523,14 @@ public static final class Builder { private @Nullable List> mergeFields; + @CanIgnoreReturnValue public @NonNull Builder setMergeFields(@Nullable List> setterArg) { this.mergeFields = setterArg; return this; } - public @NonNull PigeonDocumentOption build() { - PigeonDocumentOption pigeonReturn = new PigeonDocumentOption(); + public @NonNull InternalDocumentOption build() { + InternalDocumentOption pigeonReturn = new InternalDocumentOption(); pigeonReturn.setMerge(merge); pigeonReturn.setMergeFields(mergeFields); return pigeonReturn; @@ -979,31 +1539,31 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(2); + ArrayList toListResult = new ArrayList<>(2); toListResult.add(merge); toListResult.add(mergeFields); return toListResult; } - static @NonNull PigeonDocumentOption fromList(@NonNull ArrayList list) { - PigeonDocumentOption pigeonResult = new PigeonDocumentOption(); - Object merge = list.get(0); + static @NonNull InternalDocumentOption fromList(@NonNull ArrayList pigeonVar_list) { + InternalDocumentOption pigeonResult = new InternalDocumentOption(); + Object merge = pigeonVar_list.get(0); pigeonResult.setMerge((Boolean) merge); - Object mergeFields = list.get(1); + Object mergeFields = pigeonVar_list.get(1); pigeonResult.setMergeFields((List>) mergeFields); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonTransactionCommand { - private @NonNull PigeonTransactionType type; + public static final class InternalTransactionCommand { + private @NonNull InternalTransactionType type; - public @NonNull PigeonTransactionType getType() { + public @NonNull InternalTransactionType getType() { return type; } - public void setType(@NonNull PigeonTransactionType setterArg) { + public void setType(@NonNull InternalTransactionType setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"type\" is null."); } @@ -1033,30 +1593,53 @@ public void setData(@Nullable Map setterArg) { this.data = setterArg; } - private @Nullable PigeonDocumentOption option; + private @Nullable InternalDocumentOption option; - public @Nullable PigeonDocumentOption getOption() { + public @Nullable InternalDocumentOption getOption() { return option; } - public void setOption(@Nullable PigeonDocumentOption setterArg) { + public void setOption(@Nullable InternalDocumentOption setterArg) { this.option = setterArg; } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonTransactionCommand() {} + InternalTransactionCommand() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalTransactionCommand that = (InternalTransactionCommand) o; + return pigeonDeepEquals(type, that.type) + && pigeonDeepEquals(path, that.path) + && pigeonDeepEquals(data, that.data) + && pigeonDeepEquals(option, that.option); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), type, path, data, option}; + return pigeonDeepHashCode(fields); + } public static final class Builder { - private @Nullable PigeonTransactionType type; + private @Nullable InternalTransactionType type; - public @NonNull Builder setType(@NonNull PigeonTransactionType setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setType(@NonNull InternalTransactionType setterArg) { this.type = setterArg; return this; } private @Nullable String path; + @CanIgnoreReturnValue public @NonNull Builder setPath(@NonNull String setterArg) { this.path = setterArg; return this; @@ -1064,20 +1647,22 @@ public static final class Builder { private @Nullable Map data; + @CanIgnoreReturnValue public @NonNull Builder setData(@Nullable Map setterArg) { this.data = setterArg; return this; } - private @Nullable PigeonDocumentOption option; + private @Nullable InternalDocumentOption option; - public @NonNull Builder setOption(@Nullable PigeonDocumentOption setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setOption(@Nullable InternalDocumentOption setterArg) { this.option = setterArg; return this; } - public @NonNull PigeonTransactionCommand build() { - PigeonTransactionCommand pigeonReturn = new PigeonTransactionCommand(); + public @NonNull InternalTransactionCommand build() { + InternalTransactionCommand pigeonReturn = new InternalTransactionCommand(); pigeonReturn.setType(type); pigeonReturn.setPath(path); pigeonReturn.setData(data); @@ -1088,25 +1673,24 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(4); - toListResult.add(type == null ? null : type.index); + ArrayList toListResult = new ArrayList<>(4); + toListResult.add(type); toListResult.add(path); toListResult.add(data); - toListResult.add((option == null) ? null : option.toList()); + toListResult.add(option); return toListResult; } - static @NonNull PigeonTransactionCommand fromList(@NonNull ArrayList list) { - PigeonTransactionCommand pigeonResult = new PigeonTransactionCommand(); - Object type = list.get(0); - pigeonResult.setType(PigeonTransactionType.values()[(int) type]); - Object path = list.get(1); + static @NonNull InternalTransactionCommand fromList(@NonNull ArrayList pigeonVar_list) { + InternalTransactionCommand pigeonResult = new InternalTransactionCommand(); + Object type = pigeonVar_list.get(0); + pigeonResult.setType((InternalTransactionType) type); + Object path = pigeonVar_list.get(1); pigeonResult.setPath((String) path); - Object data = list.get(2); + Object data = pigeonVar_list.get(2); pigeonResult.setData((Map) data); - Object option = list.get(3); - pigeonResult.setOption( - (option == null) ? null : PigeonDocumentOption.fromList((ArrayList) option)); + Object option = pigeonVar_list.get(3); + pigeonResult.setOption((InternalDocumentOption) option); return pigeonResult; } } @@ -1136,13 +1720,13 @@ public void setData(@Nullable Map setterArg) { this.data = setterArg; } - private @Nullable PigeonDocumentOption option; + private @Nullable InternalDocumentOption option; - public @Nullable PigeonDocumentOption getOption() { + public @Nullable InternalDocumentOption getOption() { return option; } - public void setOption(@Nullable PigeonDocumentOption setterArg) { + public void setOption(@Nullable InternalDocumentOption setterArg) { this.option = setterArg; } @@ -1169,10 +1753,34 @@ public void setServerTimestampBehavior(@Nullable ServerTimestampBehavior setterA /** Constructor is non-public to enforce null safety; use Builder. */ DocumentReferenceRequest() {} + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DocumentReferenceRequest that = (DocumentReferenceRequest) o; + return pigeonDeepEquals(path, that.path) + && pigeonDeepEquals(data, that.data) + && pigeonDeepEquals(option, that.option) + && pigeonDeepEquals(source, that.source) + && pigeonDeepEquals(serverTimestampBehavior, that.serverTimestampBehavior); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] {getClass(), path, data, option, source, serverTimestampBehavior}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable String path; + @CanIgnoreReturnValue public @NonNull Builder setPath(@NonNull String setterArg) { this.path = setterArg; return this; @@ -1180,20 +1788,23 @@ public static final class Builder { private @Nullable Map data; + @CanIgnoreReturnValue public @NonNull Builder setData(@Nullable Map setterArg) { this.data = setterArg; return this; } - private @Nullable PigeonDocumentOption option; + private @Nullable InternalDocumentOption option; - public @NonNull Builder setOption(@Nullable PigeonDocumentOption setterArg) { + @CanIgnoreReturnValue + public @NonNull Builder setOption(@Nullable InternalDocumentOption setterArg) { this.option = setterArg; return this; } private @Nullable Source source; + @CanIgnoreReturnValue public @NonNull Builder setSource(@Nullable Source setterArg) { this.source = setterArg; return this; @@ -1201,6 +1812,7 @@ public static final class Builder { private @Nullable ServerTimestampBehavior serverTimestampBehavior; + @CanIgnoreReturnValue public @NonNull Builder setServerTimestampBehavior( @Nullable ServerTimestampBehavior setterArg) { this.serverTimestampBehavior = setterArg; @@ -1220,37 +1832,33 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(5); + ArrayList toListResult = new ArrayList<>(5); toListResult.add(path); toListResult.add(data); - toListResult.add((option == null) ? null : option.toList()); - toListResult.add(source == null ? null : source.index); - toListResult.add(serverTimestampBehavior == null ? null : serverTimestampBehavior.index); + toListResult.add(option); + toListResult.add(source); + toListResult.add(serverTimestampBehavior); return toListResult; } - static @NonNull DocumentReferenceRequest fromList(@NonNull ArrayList list) { + static @NonNull DocumentReferenceRequest fromList(@NonNull ArrayList pigeonVar_list) { DocumentReferenceRequest pigeonResult = new DocumentReferenceRequest(); - Object path = list.get(0); + Object path = pigeonVar_list.get(0); pigeonResult.setPath((String) path); - Object data = list.get(1); + Object data = pigeonVar_list.get(1); pigeonResult.setData((Map) data); - Object option = list.get(2); - pigeonResult.setOption( - (option == null) ? null : PigeonDocumentOption.fromList((ArrayList) option)); - Object source = list.get(3); - pigeonResult.setSource(source == null ? null : Source.values()[(int) source]); - Object serverTimestampBehavior = list.get(4); - pigeonResult.setServerTimestampBehavior( - serverTimestampBehavior == null - ? null - : ServerTimestampBehavior.values()[(int) serverTimestampBehavior]); + Object option = pigeonVar_list.get(2); + pigeonResult.setOption((InternalDocumentOption) option); + Object source = pigeonVar_list.get(3); + pigeonResult.setSource((Source) source); + Object serverTimestampBehavior = pigeonVar_list.get(4); + pigeonResult.setServerTimestampBehavior((ServerTimestampBehavior) serverTimestampBehavior); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonQueryParameters { + public static final class InternalQueryParameters { private @Nullable List> where; public @Nullable List> getWhere() { @@ -1341,10 +1949,49 @@ public void setFilters(@Nullable Map setterArg) { this.filters = setterArg; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalQueryParameters that = (InternalQueryParameters) o; + return pigeonDeepEquals(where, that.where) + && pigeonDeepEquals(orderBy, that.orderBy) + && pigeonDeepEquals(limit, that.limit) + && pigeonDeepEquals(limitToLast, that.limitToLast) + && pigeonDeepEquals(startAt, that.startAt) + && pigeonDeepEquals(startAfter, that.startAfter) + && pigeonDeepEquals(endAt, that.endAt) + && pigeonDeepEquals(endBefore, that.endBefore) + && pigeonDeepEquals(filters, that.filters); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + where, + orderBy, + limit, + limitToLast, + startAt, + startAfter, + endAt, + endBefore, + filters + }; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable List> where; + @CanIgnoreReturnValue public @NonNull Builder setWhere(@Nullable List> setterArg) { this.where = setterArg; return this; @@ -1352,6 +1999,7 @@ public static final class Builder { private @Nullable List> orderBy; + @CanIgnoreReturnValue public @NonNull Builder setOrderBy(@Nullable List> setterArg) { this.orderBy = setterArg; return this; @@ -1359,6 +2007,7 @@ public static final class Builder { private @Nullable Long limit; + @CanIgnoreReturnValue public @NonNull Builder setLimit(@Nullable Long setterArg) { this.limit = setterArg; return this; @@ -1366,6 +2015,7 @@ public static final class Builder { private @Nullable Long limitToLast; + @CanIgnoreReturnValue public @NonNull Builder setLimitToLast(@Nullable Long setterArg) { this.limitToLast = setterArg; return this; @@ -1373,6 +2023,7 @@ public static final class Builder { private @Nullable List startAt; + @CanIgnoreReturnValue public @NonNull Builder setStartAt(@Nullable List setterArg) { this.startAt = setterArg; return this; @@ -1380,6 +2031,7 @@ public static final class Builder { private @Nullable List startAfter; + @CanIgnoreReturnValue public @NonNull Builder setStartAfter(@Nullable List setterArg) { this.startAfter = setterArg; return this; @@ -1387,6 +2039,7 @@ public static final class Builder { private @Nullable List endAt; + @CanIgnoreReturnValue public @NonNull Builder setEndAt(@Nullable List setterArg) { this.endAt = setterArg; return this; @@ -1394,6 +2047,7 @@ public static final class Builder { private @Nullable List endBefore; + @CanIgnoreReturnValue public @NonNull Builder setEndBefore(@Nullable List setterArg) { this.endBefore = setterArg; return this; @@ -1401,13 +2055,14 @@ public static final class Builder { private @Nullable Map filters; + @CanIgnoreReturnValue public @NonNull Builder setFilters(@Nullable Map setterArg) { this.filters = setterArg; return this; } - public @NonNull PigeonQueryParameters build() { - PigeonQueryParameters pigeonReturn = new PigeonQueryParameters(); + public @NonNull InternalQueryParameters build() { + InternalQueryParameters pigeonReturn = new InternalQueryParameters(); pigeonReturn.setWhere(where); pigeonReturn.setOrderBy(orderBy); pigeonReturn.setLimit(limit); @@ -1423,7 +2078,7 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(9); + ArrayList toListResult = new ArrayList<>(9); toListResult.add(where); toListResult.add(orderBy); toListResult.add(limit); @@ -1436,29 +2091,25 @@ public ArrayList toList() { return toListResult; } - static @NonNull PigeonQueryParameters fromList(@NonNull ArrayList list) { - PigeonQueryParameters pigeonResult = new PigeonQueryParameters(); - Object where = list.get(0); + static @NonNull InternalQueryParameters fromList(@NonNull ArrayList pigeonVar_list) { + InternalQueryParameters pigeonResult = new InternalQueryParameters(); + Object where = pigeonVar_list.get(0); pigeonResult.setWhere((List>) where); - Object orderBy = list.get(1); + Object orderBy = pigeonVar_list.get(1); pigeonResult.setOrderBy((List>) orderBy); - Object limit = list.get(2); - pigeonResult.setLimit( - (limit == null) ? null : ((limit instanceof Integer) ? (Integer) limit : (Long) limit)); - Object limitToLast = list.get(3); - pigeonResult.setLimitToLast( - (limitToLast == null) - ? null - : ((limitToLast instanceof Integer) ? (Integer) limitToLast : (Long) limitToLast)); - Object startAt = list.get(4); + Object limit = pigeonVar_list.get(2); + pigeonResult.setLimit((Long) limit); + Object limitToLast = pigeonVar_list.get(3); + pigeonResult.setLimitToLast((Long) limitToLast); + Object startAt = pigeonVar_list.get(4); pigeonResult.setStartAt((List) startAt); - Object startAfter = list.get(5); + Object startAfter = pigeonVar_list.get(5); pigeonResult.setStartAfter((List) startAfter); - Object endAt = list.get(6); + Object endAt = pigeonVar_list.get(6); pigeonResult.setEndAt((List) endAt); - Object endBefore = list.get(7); + Object endBefore = pigeonVar_list.get(7); pigeonResult.setEndBefore((List) endBefore); - Object filters = list.get(8); + Object filters = pigeonVar_list.get(8); pigeonResult.setFilters((Map) filters); return pigeonResult; } @@ -1492,10 +2143,29 @@ public void setField(@Nullable String setterArg) { /** Constructor is non-public to enforce null safety; use Builder. */ AggregateQuery() {} + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AggregateQuery that = (AggregateQuery) o; + return pigeonDeepEquals(type, that.type) && pigeonDeepEquals(field, that.field); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), type, field}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable AggregateType type; + @CanIgnoreReturnValue public @NonNull Builder setType(@NonNull AggregateType setterArg) { this.type = setterArg; return this; @@ -1503,6 +2173,7 @@ public static final class Builder { private @Nullable String field; + @CanIgnoreReturnValue public @NonNull Builder setField(@Nullable String setterArg) { this.field = setterArg; return this; @@ -1518,17 +2189,17 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(2); - toListResult.add(type == null ? null : type.index); + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(type); toListResult.add(field); return toListResult; } - static @NonNull AggregateQuery fromList(@NonNull ArrayList list) { + static @NonNull AggregateQuery fromList(@NonNull ArrayList pigeonVar_list) { AggregateQuery pigeonResult = new AggregateQuery(); - Object type = list.get(0); - pigeonResult.setType(AggregateType.values()[(int) type]); - Object field = list.get(1); + Object type = pigeonVar_list.get(0); + pigeonResult.setType((AggregateType) type); + Object field = pigeonVar_list.get(1); pigeonResult.setField((String) field); return pigeonResult; } @@ -1572,10 +2243,31 @@ public void setValue(@Nullable Double setterArg) { /** Constructor is non-public to enforce null safety; use Builder. */ AggregateQueryResponse() {} + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AggregateQueryResponse that = (AggregateQueryResponse) o; + return pigeonDeepEquals(type, that.type) + && pigeonDeepEquals(field, that.field) + && pigeonDeepEquals(value, that.value); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), type, field, value}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable AggregateType type; + @CanIgnoreReturnValue public @NonNull Builder setType(@NonNull AggregateType setterArg) { this.type = setterArg; return this; @@ -1583,6 +2275,7 @@ public static final class Builder { private @Nullable String field; + @CanIgnoreReturnValue public @NonNull Builder setField(@Nullable String setterArg) { this.field = setterArg; return this; @@ -1590,6 +2283,7 @@ public static final class Builder { private @Nullable Double value; + @CanIgnoreReturnValue public @NonNull Builder setValue(@Nullable Double setterArg) { this.value = setterArg; return this; @@ -1606,67 +2300,116 @@ public static final class Builder { @NonNull public ArrayList toList() { - ArrayList toListResult = new ArrayList(3); - toListResult.add(type == null ? null : type.index); + ArrayList toListResult = new ArrayList<>(3); + toListResult.add(type); toListResult.add(field); toListResult.add(value); return toListResult; } - static @NonNull AggregateQueryResponse fromList(@NonNull ArrayList list) { + static @NonNull AggregateQueryResponse fromList(@NonNull ArrayList pigeonVar_list) { AggregateQueryResponse pigeonResult = new AggregateQueryResponse(); - Object type = list.get(0); - pigeonResult.setType(AggregateType.values()[(int) type]); - Object field = list.get(1); + Object type = pigeonVar_list.get(0); + pigeonResult.setType((AggregateType) type); + Object field = pigeonVar_list.get(1); pigeonResult.setField((String) field); - Object value = list.get(2); + Object value = pigeonVar_list.get(2); pigeonResult.setValue((Double) value); return pigeonResult; } } - public interface Result { - @SuppressWarnings("UnknownNullness") - void success(T result); - - void error(@NonNull Throwable error); - } + public static class PigeonCodec extends FlutterFirebaseFirestoreMessageCodec { + public static final PigeonCodec INSTANCE = new PigeonCodec(); - private static class FirebaseFirestoreHostApiCodec extends FlutterFirebaseFirestoreMessageCodec { - public static final FirebaseFirestoreHostApiCodec INSTANCE = - new FirebaseFirestoreHostApiCodec(); - - private FirebaseFirestoreHostApiCodec() {} + private PigeonCodec() {} @Override protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 128: - return AggregateQuery.fromList((ArrayList) readValue(buffer)); case (byte) 129: - return AggregateQueryResponse.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null ? null : DocumentChangeType.values()[((Long) value).intValue()]; + } case (byte) 130: - return DocumentReferenceRequest.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null ? null : Source.values()[((Long) value).intValue()]; + } case (byte) 131: - return FirestorePigeonFirebaseApp.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null ? null : ListenSource.values()[((Long) value).intValue()]; + } case (byte) 132: - return PigeonDocumentChange.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null + ? null + : ServerTimestampBehavior.values()[((Long) value).intValue()]; + } case (byte) 133: - return PigeonDocumentOption.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null ? null : AggregateSource.values()[((Long) value).intValue()]; + } case (byte) 134: - return PigeonDocumentSnapshot.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null + ? null + : PersistenceCacheIndexManagerRequest.values()[((Long) value).intValue()]; + } case (byte) 135: - return PigeonFirebaseSettings.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null + ? null + : InternalTransactionResult.values()[((Long) value).intValue()]; + } case (byte) 136: - return PigeonGetOptions.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null + ? null + : InternalTransactionType.values()[((Long) value).intValue()]; + } case (byte) 137: - return PigeonQueryParameters.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null ? null : AggregateType.values()[((Long) value).intValue()]; + } case (byte) 138: - return PigeonQuerySnapshot.fromList((ArrayList) readValue(buffer)); + return InternalFirebaseSettings.fromList((ArrayList) readValue(buffer)); case (byte) 139: - return PigeonSnapshotMetadata.fromList((ArrayList) readValue(buffer)); + return FirestorePigeonFirebaseApp.fromList((ArrayList) readValue(buffer)); case (byte) 140: - return PigeonTransactionCommand.fromList((ArrayList) readValue(buffer)); + return InternalSnapshotMetadata.fromList((ArrayList) readValue(buffer)); + case (byte) 141: + return InternalDocumentSnapshot.fromList((ArrayList) readValue(buffer)); + case (byte) 142: + return InternalDocumentChange.fromList((ArrayList) readValue(buffer)); + case (byte) 143: + return InternalQuerySnapshot.fromList((ArrayList) readValue(buffer)); + case (byte) 144: + return InternalPipelineResult.fromList((ArrayList) readValue(buffer)); + case (byte) 145: + return InternalPipelineSnapshot.fromList((ArrayList) readValue(buffer)); + case (byte) 146: + return InternalGetOptions.fromList((ArrayList) readValue(buffer)); + case (byte) 147: + return InternalDocumentOption.fromList((ArrayList) readValue(buffer)); + case (byte) 148: + return InternalTransactionCommand.fromList((ArrayList) readValue(buffer)); + case (byte) 149: + return DocumentReferenceRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 150: + return InternalQueryParameters.fromList((ArrayList) readValue(buffer)); + case (byte) 151: + return AggregateQuery.fromList((ArrayList) readValue(buffer)); + case (byte) 152: + return AggregateQueryResponse.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); } @@ -1674,51 +2417,112 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { @Override protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof AggregateQuery) { - stream.write(128); - writeValue(stream, ((AggregateQuery) value).toList()); - } else if (value instanceof AggregateQueryResponse) { + if (value instanceof DocumentChangeType) { stream.write(129); - writeValue(stream, ((AggregateQueryResponse) value).toList()); - } else if (value instanceof DocumentReferenceRequest) { + writeValue(stream, value == null ? null : ((DocumentChangeType) value).index); + } else if (value instanceof Source) { stream.write(130); - writeValue(stream, ((DocumentReferenceRequest) value).toList()); - } else if (value instanceof FirestorePigeonFirebaseApp) { + writeValue(stream, value == null ? null : ((Source) value).index); + } else if (value instanceof ListenSource) { stream.write(131); - writeValue(stream, ((FirestorePigeonFirebaseApp) value).toList()); - } else if (value instanceof PigeonDocumentChange) { + writeValue(stream, value == null ? null : ((ListenSource) value).index); + } else if (value instanceof ServerTimestampBehavior) { stream.write(132); - writeValue(stream, ((PigeonDocumentChange) value).toList()); - } else if (value instanceof PigeonDocumentOption) { + writeValue(stream, value == null ? null : ((ServerTimestampBehavior) value).index); + } else if (value instanceof AggregateSource) { stream.write(133); - writeValue(stream, ((PigeonDocumentOption) value).toList()); - } else if (value instanceof PigeonDocumentSnapshot) { + writeValue(stream, value == null ? null : ((AggregateSource) value).index); + } else if (value instanceof PersistenceCacheIndexManagerRequest) { stream.write(134); - writeValue(stream, ((PigeonDocumentSnapshot) value).toList()); - } else if (value instanceof PigeonFirebaseSettings) { + writeValue( + stream, value == null ? null : ((PersistenceCacheIndexManagerRequest) value).index); + } else if (value instanceof InternalTransactionResult) { stream.write(135); - writeValue(stream, ((PigeonFirebaseSettings) value).toList()); - } else if (value instanceof PigeonGetOptions) { + writeValue(stream, value == null ? null : ((InternalTransactionResult) value).index); + } else if (value instanceof InternalTransactionType) { stream.write(136); - writeValue(stream, ((PigeonGetOptions) value).toList()); - } else if (value instanceof PigeonQueryParameters) { + writeValue(stream, value == null ? null : ((InternalTransactionType) value).index); + } else if (value instanceof AggregateType) { stream.write(137); - writeValue(stream, ((PigeonQueryParameters) value).toList()); - } else if (value instanceof PigeonQuerySnapshot) { + writeValue(stream, value == null ? null : ((AggregateType) value).index); + } else if (value instanceof InternalFirebaseSettings) { stream.write(138); - writeValue(stream, ((PigeonQuerySnapshot) value).toList()); - } else if (value instanceof PigeonSnapshotMetadata) { + writeValue(stream, ((InternalFirebaseSettings) value).toList()); + } else if (value instanceof FirestorePigeonFirebaseApp) { stream.write(139); - writeValue(stream, ((PigeonSnapshotMetadata) value).toList()); - } else if (value instanceof PigeonTransactionCommand) { + writeValue(stream, ((FirestorePigeonFirebaseApp) value).toList()); + } else if (value instanceof InternalSnapshotMetadata) { stream.write(140); - writeValue(stream, ((PigeonTransactionCommand) value).toList()); + writeValue(stream, ((InternalSnapshotMetadata) value).toList()); + } else if (value instanceof InternalDocumentSnapshot) { + stream.write(141); + writeValue(stream, ((InternalDocumentSnapshot) value).toList()); + } else if (value instanceof InternalDocumentChange) { + stream.write(142); + writeValue(stream, ((InternalDocumentChange) value).toList()); + } else if (value instanceof InternalQuerySnapshot) { + stream.write(143); + writeValue(stream, ((InternalQuerySnapshot) value).toList()); + } else if (value instanceof InternalPipelineResult) { + stream.write(144); + writeValue(stream, ((InternalPipelineResult) value).toList()); + } else if (value instanceof InternalPipelineSnapshot) { + stream.write(145); + writeValue(stream, ((InternalPipelineSnapshot) value).toList()); + } else if (value instanceof InternalGetOptions) { + stream.write(146); + writeValue(stream, ((InternalGetOptions) value).toList()); + } else if (value instanceof InternalDocumentOption) { + stream.write(147); + writeValue(stream, ((InternalDocumentOption) value).toList()); + } else if (value instanceof InternalTransactionCommand) { + stream.write(148); + writeValue(stream, ((InternalTransactionCommand) value).toList()); + } else if (value instanceof DocumentReferenceRequest) { + stream.write(149); + writeValue(stream, ((DocumentReferenceRequest) value).toList()); + } else if (value instanceof InternalQueryParameters) { + stream.write(150); + writeValue(stream, ((InternalQueryParameters) value).toList()); + } else if (value instanceof AggregateQuery) { + stream.write(151); + writeValue(stream, ((AggregateQuery) value).toList()); + } else if (value instanceof AggregateQueryResponse) { + stream.write(152); + writeValue(stream, ((AggregateQueryResponse) value).toList()); } else { super.writeValue(stream, value); } } } + /** Asynchronous error handling return type for non-nullable API method returns. */ + public interface Result { + /** Success case callback method for handling returns. */ + void success(@NonNull T result); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + + /** Asynchronous error handling return type for nullable API method returns. */ + public interface NullableResult { + /** Success case callback method for handling returns. */ + void success(@Nullable T result); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + + /** Asynchronous error handling return type for void API method returns. */ + public interface VoidResult { + /** Success case callback method for handling returns. */ + void success(); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FirebaseFirestoreHostApi { @@ -1730,26 +2534,25 @@ void loadBundle( void namedQueryGet( @NonNull FirestorePigeonFirebaseApp app, @NonNull String name, - @NonNull PigeonGetOptions options, - @NonNull Result result); + @NonNull InternalGetOptions options, + @NonNull Result result); - void clearPersistence(@NonNull FirestorePigeonFirebaseApp app, @NonNull Result result); + void clearPersistence(@NonNull FirestorePigeonFirebaseApp app, @NonNull VoidResult result); - void disableNetwork(@NonNull FirestorePigeonFirebaseApp app, @NonNull Result result); + void disableNetwork(@NonNull FirestorePigeonFirebaseApp app, @NonNull VoidResult result); - void enableNetwork(@NonNull FirestorePigeonFirebaseApp app, @NonNull Result result); + void enableNetwork(@NonNull FirestorePigeonFirebaseApp app, @NonNull VoidResult result); - void terminate(@NonNull FirestorePigeonFirebaseApp app, @NonNull Result result); + void terminate(@NonNull FirestorePigeonFirebaseApp app, @NonNull VoidResult result); - void waitForPendingWrites( - @NonNull FirestorePigeonFirebaseApp app, @NonNull Result result); + void waitForPendingWrites(@NonNull FirestorePigeonFirebaseApp app, @NonNull VoidResult result); void setIndexConfiguration( @NonNull FirestorePigeonFirebaseApp app, @NonNull String indexConfiguration, - @NonNull Result result); + @NonNull VoidResult result); - void setLoggingEnabled(@NonNull Boolean loggingEnabled, @NonNull Result result); + void setLoggingEnabled(@NonNull Boolean loggingEnabled, @NonNull VoidResult result); void snapshotsInSyncSetup( @NonNull FirestorePigeonFirebaseApp app, @NonNull Result result); @@ -1762,48 +2565,48 @@ void transactionCreate( void transactionStoreResult( @NonNull String transactionId, - @NonNull PigeonTransactionResult resultType, - @Nullable List commands, - @NonNull Result result); + @NonNull InternalTransactionResult resultType, + @Nullable List commands, + @NonNull VoidResult result); void transactionGet( @NonNull FirestorePigeonFirebaseApp app, @NonNull String transactionId, @NonNull String path, - @NonNull Result result); + @NonNull Result result); void documentReferenceSet( @NonNull FirestorePigeonFirebaseApp app, @NonNull DocumentReferenceRequest request, - @NonNull Result result); + @NonNull VoidResult result); void documentReferenceUpdate( @NonNull FirestorePigeonFirebaseApp app, @NonNull DocumentReferenceRequest request, - @NonNull Result result); + @NonNull VoidResult result); void documentReferenceGet( @NonNull FirestorePigeonFirebaseApp app, @NonNull DocumentReferenceRequest request, - @NonNull Result result); + @NonNull Result result); void documentReferenceDelete( @NonNull FirestorePigeonFirebaseApp app, @NonNull DocumentReferenceRequest request, - @NonNull Result result); + @NonNull VoidResult result); void queryGet( @NonNull FirestorePigeonFirebaseApp app, @NonNull String path, @NonNull Boolean isCollectionGroup, - @NonNull PigeonQueryParameters parameters, - @NonNull PigeonGetOptions options, - @NonNull Result result); + @NonNull InternalQueryParameters parameters, + @NonNull InternalGetOptions options, + @NonNull Result result); void aggregateQuery( @NonNull FirestorePigeonFirebaseApp app, @NonNull String path, - @NonNull PigeonQueryParameters parameters, + @NonNull InternalQueryParameters parameters, @NonNull AggregateSource source, @NonNull List queries, @NonNull Boolean isCollectionGroup, @@ -1811,15 +2614,15 @@ void aggregateQuery( void writeBatchCommit( @NonNull FirestorePigeonFirebaseApp app, - @NonNull List writes, - @NonNull Result result); + @NonNull List writes, + @NonNull VoidResult result); void querySnapshot( @NonNull FirestorePigeonFirebaseApp app, @NonNull String path, @NonNull Boolean isCollectionGroup, - @NonNull PigeonQueryParameters parameters, - @NonNull PigeonGetOptions options, + @NonNull InternalQueryParameters parameters, + @NonNull InternalGetOptions options, @NonNull Boolean includeMetadataChanges, @NonNull ListenSource source, @NonNull Result result); @@ -1834,28 +2637,44 @@ void documentReferenceSnapshot( void persistenceCacheIndexManagerRequest( @NonNull FirestorePigeonFirebaseApp app, @NonNull PersistenceCacheIndexManagerRequest request, - @NonNull Result result); + @NonNull VoidResult result); + + void executePipeline( + @NonNull FirestorePigeonFirebaseApp app, + @NonNull List> stages, + @Nullable Map options, + @NonNull Result result); /** The codec used by FirebaseFirestoreHostApi. */ static @NonNull MessageCodec getCodec() { - return FirebaseFirestoreHostApiCodec.INSTANCE; + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `FirebaseFirestoreHostApi` to handle messages through the * `binaryMessenger`. */ - static void setup( + static void setUp( @NonNull BinaryMessenger binaryMessenger, @Nullable FirebaseFirestoreHostApi api) { + setUp(binaryMessenger, "", api); + } + + static void setUp( + @NonNull BinaryMessenger binaryMessenger, + @NonNull String messageChannelSuffix, + @Nullable FirebaseFirestoreHostApi api) { + messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); byte[] bundleArg = (byte[]) args.get(1); @@ -1882,19 +2701,20 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); String nameArg = (String) args.get(1); - PigeonGetOptions optionsArg = (PigeonGetOptions) args.get(2); - Result resultCallback = - new Result() { - public void success(PigeonQuerySnapshot result) { + InternalGetOptions optionsArg = (InternalGetOptions) args.get(2); + Result resultCallback = + new Result() { + public void success(InternalQuerySnapshot result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -1915,17 +2735,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -1946,17 +2767,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -1977,17 +2799,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2008,17 +2831,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2039,17 +2863,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2070,18 +2895,19 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); String indexConfigurationArg = (String) args.get(1); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2102,17 +2928,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; Boolean loggingEnabledArg = (Boolean) args.get(0); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2133,12 +2960,13 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); Result resultCallback = @@ -2164,16 +2992,17 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); - Number timeoutArg = (Number) args.get(1); - Number maxAttemptsArg = (Number) args.get(2); + Long timeoutArg = (Long) args.get(1); + Long maxAttemptsArg = (Long) args.get(2); Result resultCallback = new Result() { public void success(String result) { @@ -2187,11 +3016,7 @@ public void error(Throwable error) { } }; - api.transactionCreate( - appArg, - (timeoutArg == null) ? null : timeoutArg.longValue(), - (maxAttemptsArg == null) ? null : maxAttemptsArg.longValue(), - resultCallback); + api.transactionCreate(appArg, timeoutArg, maxAttemptsArg, resultCallback); }); } else { channel.setMessageHandler(null); @@ -2201,21 +3026,21 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String transactionIdArg = (String) args.get(0); - PigeonTransactionResult resultTypeArg = - PigeonTransactionResult.values()[(int) args.get(1)]; - List commandsArg = - (List) args.get(2); - Result resultCallback = - new Result() { - public void success(Void result) { + InternalTransactionResult resultTypeArg = (InternalTransactionResult) args.get(1); + List commandsArg = + (List) args.get(2); + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2237,19 +3062,20 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); String transactionIdArg = (String) args.get(1); String pathArg = (String) args.get(2); - Result resultCallback = - new Result() { - public void success(PigeonDocumentSnapshot result) { + Result resultCallback = + new Result() { + public void success(InternalDocumentSnapshot result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -2270,18 +3096,19 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); DocumentReferenceRequest requestArg = (DocumentReferenceRequest) args.get(1); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2302,18 +3129,19 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); DocumentReferenceRequest requestArg = (DocumentReferenceRequest) args.get(1); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2334,18 +3162,19 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); DocumentReferenceRequest requestArg = (DocumentReferenceRequest) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonDocumentSnapshot result) { + Result resultCallback = + new Result() { + public void success(InternalDocumentSnapshot result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -2366,18 +3195,19 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); DocumentReferenceRequest requestArg = (DocumentReferenceRequest) args.get(1); - Result resultCallback = - new Result() { - public void success(Void result) { + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2398,21 +3228,22 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); String pathArg = (String) args.get(1); Boolean isCollectionGroupArg = (Boolean) args.get(2); - PigeonQueryParameters parametersArg = (PigeonQueryParameters) args.get(3); - PigeonGetOptions optionsArg = (PigeonGetOptions) args.get(4); - Result resultCallback = - new Result() { - public void success(PigeonQuerySnapshot result) { + InternalQueryParameters parametersArg = (InternalQueryParameters) args.get(3); + InternalGetOptions optionsArg = (InternalGetOptions) args.get(4); + Result resultCallback = + new Result() { + public void success(InternalQuerySnapshot result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -2439,17 +3270,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); String pathArg = (String) args.get(1); - PigeonQueryParameters parametersArg = (PigeonQueryParameters) args.get(2); - AggregateSource sourceArg = AggregateSource.values()[(int) args.get(3)]; + InternalQueryParameters parametersArg = (InternalQueryParameters) args.get(2); + AggregateSource sourceArg = (AggregateSource) args.get(3); List queriesArg = (List) args.get(4); Boolean isCollectionGroupArg = (Boolean) args.get(5); Result> resultCallback = @@ -2482,19 +3314,20 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); - List writesArg = - (List) args.get(1); - Result resultCallback = - new Result() { - public void success(Void result) { + List writesArg = + (List) args.get(1); + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2515,20 +3348,21 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); String pathArg = (String) args.get(1); Boolean isCollectionGroupArg = (Boolean) args.get(2); - PigeonQueryParameters parametersArg = (PigeonQueryParameters) args.get(3); - PigeonGetOptions optionsArg = (PigeonGetOptions) args.get(4); + InternalQueryParameters parametersArg = (InternalQueryParameters) args.get(3); + InternalGetOptions optionsArg = (InternalGetOptions) args.get(4); Boolean includeMetadataChangesArg = (Boolean) args.get(5); - ListenSource sourceArg = ListenSource.values()[(int) args.get(6)]; + ListenSource sourceArg = (ListenSource) args.get(6); Result resultCallback = new Result() { public void success(String result) { @@ -2560,17 +3394,18 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); DocumentReferenceRequest parametersArg = (DocumentReferenceRequest) args.get(1); Boolean includeMetadataChangesArg = (Boolean) args.get(2); - ListenSource sourceArg = ListenSource.values()[(int) args.get(3)]; + ListenSource sourceArg = (ListenSource) args.get(3); Result resultCallback = new Result() { public void success(String result) { @@ -2595,19 +3430,20 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest", + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest" + + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); PersistenceCacheIndexManagerRequest requestArg = - PersistenceCacheIndexManagerRequest.values()[(int) args.get(1)]; - Result resultCallback = - new Result() { - public void success(Void result) { + (PersistenceCacheIndexManagerRequest) args.get(1); + VoidResult resultCallback = + new VoidResult() { + public void success() { wrapped.add(0, null); reply.reply(wrapped); } @@ -2624,6 +3460,40 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + ArrayList args = (ArrayList) message; + FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0); + List> stagesArg = (List>) args.get(1); + Map optionsArg = (Map) args.get(2); + Result resultCallback = + new Result() { + public void success(InternalPipelineSnapshot result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.executePipeline(appArg, stagesArg, optionsArg, resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } } } } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/DocumentSnapshotsStreamHandler.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/DocumentSnapshotsStreamHandler.java index 270966a3176e..b2e1245d2e87 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/DocumentSnapshotsStreamHandler.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/DocumentSnapshotsStreamHandler.java @@ -62,9 +62,13 @@ public void onListen(Object arguments, EventSink events) { onCancel(null); } else { + // Emit the Pigeon object directly; the Pigeon-aware codec on the + // MessageChannel serializes it end-to-end. Pigeon 26 no longer flattens + // nested types via `.toList()`, so calling `.toList()` here would send a + // raw list that the Dart side can no longer decode. events.success( - PigeonParser.toPigeonDocumentSnapshot(documentSnapshot, serverTimestampBehavior) - .toList()); + PigeonParser.toPigeonDocumentSnapshot( + documentSnapshot, serverTimestampBehavior)); } }); } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/OnTransactionResultListener.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/OnTransactionResultListener.java index 665858ec6290..e0974cad01e0 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/OnTransactionResultListener.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/OnTransactionResultListener.java @@ -12,6 +12,6 @@ /** callback when a transaction result has been computed. */ public interface OnTransactionResultListener { void receiveTransactionResponse( - GeneratedAndroidFirebaseFirestore.PigeonTransactionResult resultType, - List commands); + GeneratedAndroidFirebaseFirestore.InternalTransactionResult resultType, + List commands); } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/QuerySnapshotsStreamHandler.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/QuerySnapshotsStreamHandler.java index c48f3941d8ca..8aade3f8a9c2 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/QuerySnapshotsStreamHandler.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/QuerySnapshotsStreamHandler.java @@ -8,7 +8,8 @@ import static io.flutter.plugins.firebase.firestore.FlutterFirebaseFirestorePlugin.DEFAULT_ERROR_CODE; -import com.google.firebase.firestore.DocumentChange; +import android.os.Handler; +import android.os.Looper; import com.google.firebase.firestore.DocumentSnapshot; import com.google.firebase.firestore.ListenSource; import com.google.firebase.firestore.ListenerRegistration; @@ -19,29 +20,33 @@ import io.flutter.plugin.common.EventChannel.StreamHandler; import io.flutter.plugins.firebase.firestore.utils.ExceptionConverter; import io.flutter.plugins.firebase.firestore.utils.PigeonParser; -import java.util.ArrayList; import java.util.Map; +import java.util.concurrent.Executor; public class QuerySnapshotsStreamHandler implements StreamHandler { ListenerRegistration listenerRegistration; + private final Handler mainHandler = new Handler(Looper.getMainLooper()); Query query; MetadataChanges metadataChanges; DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior; ListenSource source; + Executor snapshotExecutor; public QuerySnapshotsStreamHandler( Query query, Boolean includeMetadataChanges, DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior, - ListenSource source) { + ListenSource source, + Executor snapshotExecutor) { this.query = query; this.metadataChanges = includeMetadataChanges ? MetadataChanges.INCLUDE : MetadataChanges.EXCLUDE; this.serverTimestampBehavior = serverTimestampBehavior; this.source = source; + this.snapshotExecutor = snapshotExecutor; } @Override @@ -49,6 +54,7 @@ public void onListen(Object arguments, EventSink events) { SnapshotListenOptions.Builder optionsBuilder = new SnapshotListenOptions.Builder(); optionsBuilder.setMetadataChanges(metadataChanges); optionsBuilder.setSource(source); + optionsBuilder.setExecutor(snapshotExecutor); listenerRegistration = query.addSnapshotListener( @@ -56,33 +62,21 @@ public void onListen(Object arguments, EventSink events) { (querySnapshot, exception) -> { if (exception != null) { Map exceptionDetails = ExceptionConverter.createDetails(exception); - events.error(DEFAULT_ERROR_CODE, exception.getMessage(), exceptionDetails); - events.endOfStream(); + mainHandler.post( + () -> { + events.error(DEFAULT_ERROR_CODE, exception.getMessage(), exceptionDetails); + events.endOfStream(); + }); onCancel(null); } else { - ArrayList toListResult = new ArrayList(3); - ArrayList documents = - new ArrayList(querySnapshot.getDocuments().size()); - ArrayList documentChanges = - new ArrayList(querySnapshot.getDocumentChanges().size()); - for (DocumentSnapshot documentSnapshot : querySnapshot.getDocuments()) { - documents.add( - PigeonParser.toPigeonDocumentSnapshot( - documentSnapshot, serverTimestampBehavior) - .toList()); - } - for (DocumentChange documentChange : querySnapshot.getDocumentChanges()) { - documentChanges.add( - PigeonParser.toPigeonDocumentChange(documentChange, serverTimestampBehavior) - .toList()); - } - toListResult.add(documents); - toListResult.add(documentChanges); - toListResult.add( - PigeonParser.toPigeonSnapshotMetadata(querySnapshot.getMetadata()).toList()); - - events.success(toListResult); + // Emit the Pigeon object directly; the Pigeon-aware codec serializes + // nested `InternalDocumentSnapshot` / `InternalDocumentChange` / + // `InternalSnapshotMetadata` with their proper type codes. Pigeon 26 + // no longer flattens nested types via `.toList()`. + Object pigeonSnapshot = + PigeonParser.toPigeonQuerySnapshot(querySnapshot, serverTimestampBehavior); + mainHandler.post(() -> events.success(pigeonSnapshot)); } }); } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/TransactionStreamHandler.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/TransactionStreamHandler.java index 67c6c1fac5bb..32f16bc2555b 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/TransactionStreamHandler.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/TransactionStreamHandler.java @@ -59,8 +59,8 @@ public TransactionStreamHandler( } final Semaphore semaphore = new Semaphore(0); - private GeneratedAndroidFirebaseFirestore.PigeonTransactionResult resultType; - private List commands; + private GeneratedAndroidFirebaseFirestore.InternalTransactionResult resultType; + private List commands; final Handler mainLooper = new Handler(Looper.getMainLooper()); @@ -91,11 +91,13 @@ public void onListen(Object arguments, EventSink events) { return FlutterFirebaseFirestoreTransactionResult.complete(); } - if (resultType == GeneratedAndroidFirebaseFirestore.PigeonTransactionResult.FAILURE) { + if (resultType + == GeneratedAndroidFirebaseFirestore.InternalTransactionResult.FAILURE) { return FlutterFirebaseFirestoreTransactionResult.complete(); } - for (GeneratedAndroidFirebaseFirestore.PigeonTransactionCommand command : commands) { + for (GeneratedAndroidFirebaseFirestore.InternalTransactionCommand command : + commands) { DocumentReference documentReference = firestore.document(command.getPath()); switch (command.getType()) { @@ -129,7 +131,7 @@ public void onListen(Object arguments, EventSink events) { } case SET: { - GeneratedAndroidFirebaseFirestore.PigeonDocumentOption options = + GeneratedAndroidFirebaseFirestore.InternalDocumentOption options = Objects.requireNonNull(command.getOption()); SetOptions setOptions = null; @@ -187,8 +189,8 @@ public void onCancel(Object arguments) { @Override public void receiveTransactionResponse( - GeneratedAndroidFirebaseFirestore.PigeonTransactionResult resultType, - List commands) { + GeneratedAndroidFirebaseFirestore.InternalTransactionResult resultType, + List commands) { this.resultType = resultType; this.commands = commands; semaphore.release(); diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExceptionConverter.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExceptionConverter.java index 49fb299dd04d..a7538cd73bfb 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExceptionConverter.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExceptionConverter.java @@ -63,4 +63,14 @@ public static void sendErrorToFlutter( exception != null ? exception.getMessage() : null, exceptionDetails)); } + + public static void sendErrorToFlutter( + GeneratedAndroidFirebaseFirestore.VoidResult result, Exception exception) { + Map exceptionDetails = ExceptionConverter.createDetails(exception); + result.error( + new GeneratedAndroidFirebaseFirestore.FlutterError( + DEFAULT_ERROR_CODE, + exception != null ? exception.getMessage() : null, + exceptionDetails)); + } } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExpressionHelpers.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExpressionHelpers.java new file mode 100644 index 000000000000..86c0877a902c --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExpressionHelpers.java @@ -0,0 +1,159 @@ +/* + * Copyright 2026, the Chromium project authors. Please see the AUTHORS file + * for details. 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.firebase.firestore.utils; + +import androidx.annotation.NonNull; +import com.google.firebase.Timestamp; +import com.google.firebase.firestore.Blob; +import com.google.firebase.firestore.DocumentReference; +import com.google.firebase.firestore.GeoPoint; +import com.google.firebase.firestore.VectorValue; +import com.google.firebase.firestore.pipeline.BooleanExpression; +import com.google.firebase.firestore.pipeline.Expression; +import java.util.List; +import java.util.Map; + +/** Helper utilities for parsing expressions and handling common patterns. */ +class ExpressionHelpers { + + /** + * Parses an "and" expression from a list of expression maps. Uses Expression.and() with varargs + * signature. + * + * @param exprMaps List of expression maps to combine with AND + * @param parser Reference to ExpressionParsers for recursive parsing + */ + @SuppressWarnings("unchecked") + static BooleanExpression parseAndExpression( + @NonNull List> exprMaps, @NonNull ExpressionParsers parser) { + if (exprMaps == null || exprMaps.isEmpty()) { + throw new IllegalArgumentException("'and' requires at least one expression"); + } + + BooleanExpression first = parser.parseBooleanExpression(exprMaps.get(0)); + if (exprMaps.size() == 1) { + return first; + } + + BooleanExpression[] rest = new BooleanExpression[exprMaps.size() - 1]; + for (int i = 1; i < exprMaps.size(); i++) { + rest[i - 1] = parser.parseBooleanExpression(exprMaps.get(i)); + } + return Expression.and(first, rest); + } + + /** + * Parses an "or" expression from a list of expression maps. Uses Expression.or() with varargs + * signature. + * + * @param exprMaps List of expression maps to combine with OR + * @param parser Reference to ExpressionParsers for recursive parsing + */ + @SuppressWarnings("unchecked") + static BooleanExpression parseOrExpression( + @NonNull List> exprMaps, @NonNull ExpressionParsers parser) { + if (exprMaps == null || exprMaps.isEmpty()) { + throw new IllegalArgumentException("'or' requires at least one expression"); + } + + BooleanExpression first = parser.parseBooleanExpression(exprMaps.get(0)); + if (exprMaps.size() == 1) { + return first; + } + + BooleanExpression[] rest = new BooleanExpression[exprMaps.size() - 1]; + for (int i = 1; i < exprMaps.size(); i++) { + rest[i - 1] = parser.parseBooleanExpression(exprMaps.get(i)); + } + return Expression.or(first, rest); + } + + /** + * Parses a "xor" expression from a list of expression maps. + * + * @param exprMaps List of expression maps to combine with XOR + * @param parser Reference to ExpressionParsers for recursive parsing + */ + @SuppressWarnings("unchecked") + static BooleanExpression parseXorExpression( + @NonNull List> exprMaps, @NonNull ExpressionParsers parser) { + if (exprMaps == null || exprMaps.isEmpty()) { + throw new IllegalArgumentException("'xor' requires at least one expression"); + } + + BooleanExpression first = parser.parseBooleanExpression(exprMaps.get(0)); + if (exprMaps.size() == 1) { + return first; + } + + BooleanExpression[] rest = new BooleanExpression[exprMaps.size() - 1]; + for (int i = 1; i < exprMaps.size(); i++) { + rest[i - 1] = parser.parseBooleanExpression(exprMaps.get(i)); + } + return Expression.xor(first, rest); + } + + /** + * Parses a constant value based on its type to match Android SDK constant() overloads. Valid + * types: String, Number, Boolean, Date, Timestamp, GeoPoint, byte[], Blob, DocumentReference, + * VectorValue + */ + static Expression parseConstantValue(Object value) { + + if (value == null) { + return Expression.nullValue(); + } + + if (value instanceof String) { + return Expression.constant((String) value); + } else if (value instanceof Number) { + return Expression.constant((Number) value); + } else if (value instanceof Boolean) { + return Expression.constant((Boolean) value); + } else if (value instanceof java.util.Date) { + return Expression.constant((java.util.Date) value); + } else if (value instanceof Timestamp) { + return Expression.constant((Timestamp) value); + } else if (value instanceof GeoPoint) { + return Expression.constant((GeoPoint) value); + } else if (value instanceof byte[]) { + return Expression.constant((byte[]) value); + } else if (value instanceof List) { + // Handle List from Dart which comes as List or List + // This represents byte[] (byte array) for constant expressions + @SuppressWarnings("unchecked") + List list = (List) value; + // Check if all elements are numbers (for byte array) + boolean isByteArray = true; + for (Object item : list) { + if (!(item instanceof Number)) { + isByteArray = false; + break; + } + } + if (isByteArray && !list.isEmpty()) { + byte[] byteArray = new byte[list.size()]; + for (int i = 0; i < list.size(); i++) { + byteArray[i] = ((Number) list.get(i)).byteValue(); + } + return Expression.constant(byteArray); + } + // If not a byte array, fall through to error + } else if (value instanceof Blob) { + return Expression.constant((Blob) value); + } else if (value instanceof DocumentReference) { + return Expression.constant((DocumentReference) value); + } else if (value instanceof VectorValue) { + return Expression.constant((VectorValue) value); + } + + throw new IllegalArgumentException( + "Constant value must be one of: String, Number, Boolean, Date, Timestamp, " + + "GeoPoint, byte[], Blob, DocumentReference, or VectorValue. Got: " + + value.getClass().getName()); + } +} diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExpressionParsers.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExpressionParsers.java new file mode 100644 index 000000000000..836d79a39eb7 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/ExpressionParsers.java @@ -0,0 +1,736 @@ +/* + * Copyright 2026, the Chromium project authors. Please see the AUTHORS file + * for details. 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.firebase.firestore.utils; + +import android.util.Log; +import androidx.annotation.NonNull; +import com.google.firebase.firestore.FirebaseFirestore; +import com.google.firebase.firestore.pipeline.AggregateFunction; +import com.google.firebase.firestore.pipeline.AggregateOptions; +import com.google.firebase.firestore.pipeline.AggregateStage; +import com.google.firebase.firestore.pipeline.AliasedAggregate; +import com.google.firebase.firestore.pipeline.BooleanExpression; +import com.google.firebase.firestore.pipeline.Expression; +import com.google.firebase.firestore.pipeline.FindNearestStage; +import com.google.firebase.firestore.pipeline.Selectable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Parses Dart pipeline expression maps into Android {@link Expression} / {@link BooleanExpression} + * types. {@link #parseBooleanExpression}'s default delegates to {@link #parseExpression} when the + * name is a value expression that yields a boolean (e.g. aliased comparisons). + */ +class ExpressionParsers { + private static final String TAG = "ExpressionParsers"; + + private final FirebaseFirestore firestore; + + ExpressionParsers(@NonNull FirebaseFirestore firestore) { + this.firestore = firestore; + } + + /** Binary operation on two expressions. Used instead of BiFunction for API 23 compatibility. */ + private interface BinaryExpressionOp { + R apply(Expression left, Expression right); + } + + @SuppressWarnings("unchecked") + private static Map argsOf(@NonNull Map expressionMap) { + Map args = (Map) expressionMap.get("args"); + return args != null ? args : new HashMap<>(); + } + + @SuppressWarnings("unchecked") + private Expression parseChild(@NonNull Map args, @NonNull String key) { + return parseExpression((Map) args.get(key)); + } + + /** Parses a list of nested expression maps (e.g. {@code values}) to {@link Expression}s. */ + private List parseExpressionMaps(@NonNull List> maps) { + Expression[] out = new Expression[maps.size()]; + for (int i = 0; i < maps.size(); i++) { + out[i] = parseExpression(maps.get(i)); + } + return Arrays.asList(out); + } + + private BooleanExpression parseBinaryComparisonNamed( + @NonNull String name, @NonNull Map args) { + switch (name) { + case "equal": + return parseBinaryComparison(args, (left, right) -> left.equal(right)); + case "not_equal": + return parseBinaryComparison(args, (left, right) -> left.notEqual(right)); + case "greater_than": + return parseBinaryComparison(args, (left, right) -> left.greaterThan(right)); + case "greater_than_or_equal": + return parseBinaryComparison(args, (left, right) -> left.greaterThanOrEqual(right)); + case "less_than": + return parseBinaryComparison(args, (left, right) -> left.lessThan(right)); + case "less_than_or_equal": + return parseBinaryComparison(args, (left, right) -> left.lessThanOrEqual(right)); + default: + throw new IllegalArgumentException("Not a binary comparison expression: " + name); + } + } + + @SuppressWarnings("unchecked") + private BooleanExpression parseEqualAny(@NonNull Map args) { + Map valueMap = (Map) args.get("value"); + List> valuesMaps = (List>) args.get("values"); + Expression value = parseExpression(valueMap); + return value.equalAny(parseExpressionMaps(valuesMaps)); + } + + @SuppressWarnings("unchecked") + private BooleanExpression parseNotEqualAny(@NonNull Map args) { + Map valueMap = (Map) args.get("value"); + List> valuesMaps = (List>) args.get("values"); + Expression value = parseExpression(valueMap); + return value.notEqualAny(parseExpressionMaps(valuesMaps)); + } + + @SuppressWarnings("unchecked") + private BooleanExpression parseArrayContainsElement(@NonNull Map args) { + Map arrayMap = (Map) args.get("array"); + Map elementMap = (Map) args.get("element"); + Expression array = parseExpression(arrayMap); + Expression element = parseExpression(elementMap); + return array.arrayContains(element); + } + + /** Parses an expression from a map representation. */ + @SuppressWarnings("unchecked") + Expression parseExpression(@NonNull Map expressionMap) { + String name = (String) expressionMap.get("name"); + if (name == null) { + if (expressionMap.containsKey("field_name")) { + String fieldName = (String) expressionMap.get("field_name"); + return Expression.field(fieldName); + } + Map argsCheck = (Map) expressionMap.get("args"); + if (argsCheck != null && argsCheck.containsKey("field")) { + String fieldName = (String) argsCheck.get("field"); + return Expression.field(fieldName); + } + throw new IllegalArgumentException("Expression must have a 'name' field"); + } + + Map args = argsOf(expressionMap); + + switch (name) { + case "null": + return Expression.nullValue(); + case "field": + { + String fieldName = (String) args.get("field"); + if (fieldName == null) { + throw new IllegalArgumentException("Field expression must have a 'field' argument"); + } + return Expression.field(fieldName); + } + case "constant": + { + Object value = args.get("value"); + if (value instanceof Map) { + Map valueMap = (Map) value; + String path = (String) valueMap.get("path"); + return Expression.constant(firestore.document(path)); + } + return ExpressionHelpers.parseConstantValue(value); + } + case "alias": + { + Map exprMap = (Map) args.get("expression"); + String alias = (String) args.get("alias"); + Expression expr = parseExpression(exprMap); + return expr.alias(alias); + } + case "equal": + case "not_equal": + case "greater_than": + case "greater_than_or_equal": + case "less_than": + case "less_than_or_equal": + return parseBinaryComparisonNamed(name, args); + case "add": + return parseBinaryOperation(args, (left, right) -> left.add(right)); + case "subtract": + return parseBinaryOperation(args, (left, right) -> left.subtract(right)); + case "multiply": + return parseBinaryOperation(args, (left, right) -> left.multiply(right)); + case "divide": + return parseBinaryOperation(args, (left, right) -> left.divide(right)); + case "modulo": + return parseBinaryOperation(args, (left, right) -> left.mod(right)); + case "and": + { + List> exprMaps = (List>) args.get("expressions"); + return ExpressionHelpers.parseAndExpression(exprMaps, this); + } + case "or": + { + List> exprMaps = (List>) args.get("expressions"); + return ExpressionHelpers.parseOrExpression(exprMaps, this); + } + case "xor": + { + List> exprMaps = (List>) args.get("expressions"); + return ExpressionHelpers.parseXorExpression(exprMaps, this); + } + case "not": + { + Map exprMap = (Map) args.get("expression"); + BooleanExpression expr = parseBooleanExpression(exprMap); + return Expression.not(expr); + } + case "concat": + { + List> exprMaps = (List>) args.get("expressions"); + if (exprMaps == null || exprMaps.size() < 2) { + throw new IllegalArgumentException("concat requires at least two expressions"); + } + Expression first = parseExpression(exprMaps.get(0)); + Expression second = parseExpression(exprMaps.get(1)); + if (exprMaps.size() == 2) { + return Expression.concat(first, second); + } + Object[] others = new Object[exprMaps.size() - 2]; + for (int i = 2; i < exprMaps.size(); i++) { + others[i - 2] = parseExpression(exprMaps.get(i)); + } + return Expression.concat(first, second, others); + } + case "length": + return Expression.length(parseChild(args, "expression")); + case "to_lower_case": + return Expression.toLower(parseChild(args, "expression")); + case "to_upper_case": + return Expression.toUpper(parseChild(args, "expression")); + case "trim": + return Expression.trim(parseChild(args, "expression")); + case "substring": + { + Map exprMap = (Map) args.get("expression"); + Map startMap = (Map) args.get("start"); + Map endMap = (Map) args.get("end"); + Expression stringExpr = parseExpression(exprMap); + Expression startExpr = parseExpression(startMap); + Expression endExpr = parseExpression(endMap); + Expression lengthExpr = Expression.subtract(endExpr, startExpr); + return Expression.substring(stringExpr, startExpr, lengthExpr); + } + case "split": + { + Map valueMap = (Map) args.get("expression"); + Map delimiterMap = (Map) args.get("delimiter"); + return Expression.split(parseExpression(valueMap), parseExpression(delimiterMap)); + } + case "join": + { + Map arrayMap = (Map) args.get("expression"); + Map delimiterMap = (Map) args.get("delimiter"); + return Expression.join(parseExpression(arrayMap), parseExpression(delimiterMap)); + } + case "abs": + return Expression.abs(parseChild(args, "expression")); + case "negate": + { + Expression expr = parseChild(args, "expression"); + return Expression.subtract(Expression.constant(0), expr); + } + case "array_concat": + { + Map firstMap = (Map) args.get("first"); + Map secondMap = (Map) args.get("second"); + return Expression.arrayConcat(parseExpression(firstMap), parseExpression(secondMap)); + } + case "array_concat_multiple": + { + List> arrays = (List>) args.get("arrays"); + if (arrays == null || arrays.size() < 2) { + throw new IllegalArgumentException( + "array_concat_multiple requires at least two arrays"); + } + Expression result = + Expression.arrayConcat( + parseExpression(arrays.get(0)), parseExpression(arrays.get(1))); + for (int i = 2; i < arrays.size(); i++) { + result = result.arrayConcat(parseExpression(arrays.get(i))); + } + return result; + } + case "array_length": + return Expression.arrayLength(parseChild(args, "expression")); + case "array_reverse": + return Expression.arrayReverse(parseChild(args, "expression")); + case "array_sum": + return Expression.arraySum(parseChild(args, "expression")); + case "array_slice": + { + Expression array = parseChild(args, "expression"); + Expression offset = parseChild(args, "offset"); + Map lengthMap = (Map) args.get("length"); + if (lengthMap == null) { + return array.arraySliceToEnd(offset); + } + return array.arraySlice(offset, parseExpression(lengthMap)); + } + case "array_filter": + { + Expression array = parseChild(args, "expression"); + String alias = (String) args.get("alias"); + Map filterMap = (Map) args.get("filter"); + if (alias == null || filterMap == null) { + throw new IllegalArgumentException("array_filter requires alias and filter"); + } + return array.arrayFilter(alias, parseBooleanExpression(filterMap)); + } + case "array_transform": + { + Expression array = parseChild(args, "expression"); + String elementAlias = (String) args.get("element_alias"); + Map transformMap = (Map) args.get("transform"); + if (elementAlias == null || transformMap == null) { + throw new IllegalArgumentException( + "array_transform requires element_alias and transform"); + } + return array.arrayTransform(elementAlias, parseExpression(transformMap)); + } + case "array_transform_with_index": + { + Expression array = parseChild(args, "expression"); + String elementAlias = (String) args.get("element_alias"); + String indexAlias = (String) args.get("index_alias"); + Map transformMap = (Map) args.get("transform"); + if (elementAlias == null || indexAlias == null || transformMap == null) { + throw new IllegalArgumentException( + "array_transform_with_index requires element_alias, index_alias, and transform"); + } + return array.arrayTransformWithIndex( + elementAlias, indexAlias, parseExpression(transformMap)); + } + case "if_absent": + { + Map exprMap = (Map) args.get("expression"); + Map elseMap = (Map) args.get("else"); + return Expression.ifAbsent(parseExpression(exprMap), parseExpression(elseMap)); + } + case "if_error": + { + Map exprMap = (Map) args.get("expression"); + Map catchMap = (Map) args.get("catch"); + return Expression.ifError(parseExpression(exprMap), parseExpression(catchMap)); + } + case "conditional": + { + Map conditionMap = (Map) args.get("condition"); + Map thenMap = (Map) args.get("then"); + Map elseMap = (Map) args.get("else"); + BooleanExpression condition = parseBooleanExpression(conditionMap); + Expression thenExpr = parseExpression(thenMap); + Expression elseExpr = parseExpression(elseMap); + return Expression.conditional(condition, thenExpr, elseExpr); + } + case "document_id": + return Expression.documentId(parseChild(args, "expression")); + case "document_id_from_ref": + { + String path = (String) args.get("doc_ref"); + if (path == null) { + throw new IllegalArgumentException("document_id_from_ref requires 'doc_ref' argument"); + } + return Expression.documentId(firestore.document(path)); + } + case "collection_id": + return Expression.collectionId(parseChild(args, "expression")); + case "map_get": + { + Map mapMap = (Map) args.get("map"); + Map keyMap = (Map) args.get("key"); + return Expression.mapGet(parseExpression(mapMap), parseExpression(keyMap)); + } + case "current_timestamp": + return Expression.currentTimestamp(); + case "timestamp_add": + { + Map timestampMap = (Map) args.get("timestamp"); + String unit = (String) args.get("unit"); + Map amountMap = (Map) args.get("amount"); + if (unit == null || amountMap == null) { + throw new IllegalArgumentException("timestamp_add requires 'unit' and 'amount'"); + } + Expression timestampExpr = parseExpression(timestampMap); + Expression amountExpr = parseExpression(amountMap); + return Expression.timestampAdd(timestampExpr, Expression.constant(unit), amountExpr); + } + case "timestamp_subtract": + { + Map timestampMap = (Map) args.get("timestamp"); + String unit = (String) args.get("unit"); + Map amountMap = (Map) args.get("amount"); + if (unit == null || amountMap == null) { + throw new IllegalArgumentException("timestamp_subtract requires 'unit' and 'amount'"); + } + Expression timestampExpr = parseExpression(timestampMap); + Expression amountExpr = parseExpression(amountMap); + return Expression.timestampSubtract(timestampExpr, Expression.constant(unit), amountExpr); + } + case "timestamp_truncate": + { + Map timestampMap = (Map) args.get("timestamp"); + String unit = (String) args.get("unit"); + if (unit == null) { + throw new IllegalArgumentException("timestamp_truncate requires 'unit'"); + } + return Expression.timestampTruncate(parseExpression(timestampMap), unit); + } + case "array": + { + List elements = (List) args.get("elements"); + if (elements == null) { + throw new IllegalArgumentException("array requires 'elements'"); + } + Object[] parsed = new Object[elements.size()]; + for (int i = 0; i < elements.size(); i++) { + Object el = elements.get(i); + if (el instanceof Map) { + parsed[i] = parseExpression((Map) el); + } else { + parsed[i] = ExpressionHelpers.parseConstantValue(el); + } + } + return Expression.array(Arrays.asList(parsed)); + } + case "map": + { + Map data = (Map) args.get("data"); + if (data == null) { + throw new IllegalArgumentException("map requires 'data'"); + } + Map parsed = new HashMap<>(); + for (Map.Entry e : data.entrySet()) { + Object v = e.getValue(); + if (v instanceof Map) { + @SuppressWarnings("unchecked") + Map nested = (Map) v; + if (nested.containsKey("name") && nested.containsKey("args")) { + parsed.put(e.getKey(), parseExpression(nested)); + } else { + parsed.put(e.getKey(), v); + } + } else { + parsed.put(e.getKey(), ExpressionHelpers.parseConstantValue(v)); + } + } + return Expression.map(parsed); + } + case "bit_and": + return parseBinaryOperation(args, (left, right) -> left.bitAnd(right)); + case "bit_or": + return parseBinaryOperation(args, (left, right) -> left.bitOr(right)); + case "bit_xor": + return parseBinaryOperation(args, (left, right) -> left.bitXor(right)); + case "bit_not": + return parseChild(args, "expression").bitNot(); + case "bit_left_shift": + { + Map exprMap = (Map) args.get("expression"); + Map amountMap = (Map) args.get("amount"); + return parseExpression(exprMap).bitLeftShift(parseExpression(amountMap)); + } + case "bit_right_shift": + { + Map exprMap = (Map) args.get("expression"); + Map amountMap = (Map) args.get("amount"); + return parseExpression(exprMap).bitRightShift(parseExpression(amountMap)); + } + case "is_absent": + return parseIsAbsent(args); + case "is_error": + return parseIsError(args); + case "exists": + return parseExists(args); + case "as_boolean": + return parseAsBoolean(args); + case "array_contains_all": + return parseArrayContainsAll(args); + case "array_contains_any": + return parseArrayContainsAny(args); + case "document_matches": + return parseDocumentMatches(args); + default: + Log.w(TAG, "Unsupported expression type: " + name); + throw new UnsupportedOperationException("Expression type not yet implemented: " + name); + } + } + + @SuppressWarnings("unchecked") + private BooleanExpression parseBinaryComparison( + @NonNull Map args, @NonNull BinaryExpressionOp operation) { + Map leftMap = (Map) args.get("left"); + Map rightMap = (Map) args.get("right"); + Expression left = parseExpression(leftMap); + Expression right = parseExpression(rightMap); + return operation.apply(left, right); + } + + @SuppressWarnings("unchecked") + private Expression parseBinaryOperation( + @NonNull Map args, @NonNull BinaryExpressionOp operation) { + Map leftMap = (Map) args.get("left"); + Map rightMap = (Map) args.get("right"); + Expression left = parseExpression(leftMap); + Expression right = parseExpression(rightMap); + return operation.apply(left, right); + } + + private BooleanExpression parseIsAbsent(@NonNull Map args) { + return parseChild(args, "expression").isAbsent(); + } + + private BooleanExpression parseIsError(@NonNull Map args) { + return parseChild(args, "expression").isError(); + } + + private BooleanExpression parseExists(@NonNull Map args) { + return parseChild(args, "expression").exists(); + } + + private BooleanExpression parseAsBoolean(@NonNull Map args) { + return parseChild(args, "expression").asBoolean(); + } + + @SuppressWarnings("unchecked") + private BooleanExpression parseArrayContainsAll(@NonNull Map args) { + Map arrayMap = (Map) args.get("array"); + Expression array = parseExpression(arrayMap); + if (args.get("values") != null) { + List> valuesMaps = (List>) args.get("values"); + return array.arrayContainsAll(parseExpressionMaps(valuesMaps)); + } + Map arrayExprMap = (Map) args.get("array_expression"); + Expression arrayExpr = parseExpression(arrayExprMap); + return array.arrayContainsAll(arrayExpr); + } + + @SuppressWarnings("unchecked") + private BooleanExpression parseArrayContainsAny(@NonNull Map args) { + Map arrayMap = (Map) args.get("array"); + List> valuesMaps = (List>) args.get("values"); + Expression array = parseExpression(arrayMap); + return array.arrayContainsAny(parseExpressionMaps(valuesMaps)); + } + + @SuppressWarnings("unchecked") + BooleanExpression parseBooleanExpression(@NonNull Map expressionMap) { + String name = (String) expressionMap.get("name"); + if (name == null) { + throw new IllegalArgumentException("BooleanExpression must have a 'name' field"); + } + + Map args = argsOf(expressionMap); + + switch (name) { + case "equal": + case "not_equal": + case "greater_than": + case "greater_than_or_equal": + case "less_than": + case "less_than_or_equal": + return parseBinaryComparisonNamed(name, args); + case "and": + { + List> exprMaps = (List>) args.get("expressions"); + return ExpressionHelpers.parseAndExpression(exprMaps, this); + } + case "or": + { + List> exprMaps = (List>) args.get("expressions"); + return ExpressionHelpers.parseOrExpression(exprMaps, this); + } + case "xor": + { + List> exprMaps = (List>) args.get("expressions"); + return ExpressionHelpers.parseXorExpression(exprMaps, this); + } + case "not": + { + Map exprMap = (Map) args.get("expression"); + BooleanExpression expr = parseBooleanExpression(exprMap); + return expr.not(); + } + case "is_absent": + return parseIsAbsent(args); + case "is_error": + return parseIsError(args); + case "exists": + return parseExists(args); + case "array_contains": + return parseArrayContainsElement(args); + case "array_contains_all": + return parseArrayContainsAll(args); + case "array_contains_any": + return parseArrayContainsAny(args); + case "equal_any": + return parseEqualAny(args); + case "not_equal_any": + return parseNotEqualAny(args); + case "as_boolean": + return parseAsBoolean(args); + case "document_matches": + return parseDocumentMatches(args); + default: + Expression expr = parseExpression(expressionMap); + if (expr instanceof BooleanExpression) { + return (BooleanExpression) expr; + } + Log.w(TAG, "Expression type '" + name + "' is not a BooleanExpression, attempting cast"); + throw new IllegalArgumentException( + "Expression type '" + name + "' cannot be used as a BooleanExpression"); + } + } + + @SuppressWarnings("unchecked") + Selectable parseSelectable(@NonNull Map expressionMap) { + Expression expr = parseExpression(expressionMap); + if (!(expr instanceof Selectable)) { + throw new IllegalArgumentException( + "Expression must be a Selectable (Field or AliasedExpression). Got: " + + expressionMap.get("name")); + } + return (Selectable) expr; + } + + private BooleanExpression parseDocumentMatches(@NonNull Map args) { + String query = (String) args.get("query"); + if (query == null) { + throw new IllegalArgumentException("document_matches requires a 'query' argument"); + } + return Expression.documentMatches(query); + } + + @SuppressWarnings("unchecked") + AggregateFunction parseAggregateFunction(@NonNull Map aggregateMap) { + String functionName = (String) aggregateMap.get("function"); + if (functionName == null) { + functionName = (String) aggregateMap.get("name"); + } + Map args = (Map) aggregateMap.get("args"); + Expression expr = null; + if (args != null) { + Map exprMap = (Map) args.get("expression"); + expr = parseExpression(exprMap); + } + + switch (functionName) { + case "sum": + return AggregateFunction.sum(expr); + case "average": + return AggregateFunction.average(expr); + case "count": + return AggregateFunction.count(expr); + case "count_distinct": + return AggregateFunction.countDistinct(expr); + case "minimum": + return AggregateFunction.minimum(expr); + case "maximum": + return AggregateFunction.maximum(expr); + case "count_all": + return AggregateFunction.countAll(); + default: + throw new IllegalArgumentException("Unknown aggregate function: " + functionName); + } + } + + @SuppressWarnings("unchecked") + AliasedAggregate parseAliasedAggregate(@NonNull Map aggregateMap) { + String name = (String) aggregateMap.get("name"); + if ("alias".equals(name)) { + Map args = (Map) aggregateMap.get("args"); + String alias = (String) args.get("alias"); + Map aggregateFunctionMap = + (Map) args.get("aggregate_function"); + + AggregateFunction function = parseAggregateFunction(aggregateFunctionMap); + return function.alias(alias); + } + + String alias = (String) aggregateMap.get("alias"); + if (alias != null) { + AggregateFunction function = parseAggregateFunction(aggregateMap); + return function.alias(alias); + } + + throw new IllegalArgumentException( + "Aggregate function must have an alias. Expected AliasedAggregateFunction format."); + } + + @SuppressWarnings("unchecked") + AggregateStage parseAggregateStage(@NonNull Map stageMap) { + List> accumulatorMaps = + (List>) stageMap.get("accumulators"); + if (accumulatorMaps == null || accumulatorMaps.isEmpty()) { + throw new IllegalArgumentException("AggregateStage must have at least one accumulator"); + } + + AliasedAggregate[] accumulators = new AliasedAggregate[accumulatorMaps.size()]; + for (int i = 0; i < accumulatorMaps.size(); i++) { + accumulators[i] = parseAliasedAggregate(accumulatorMaps.get(i)); + } + + AggregateStage aggregateStage; + if (accumulators.length == 1) { + aggregateStage = AggregateStage.withAccumulators(accumulators[0]); + } else { + AliasedAggregate[] rest = new AliasedAggregate[accumulators.length - 1]; + System.arraycopy(accumulators, 1, rest, 0, rest.length); + aggregateStage = AggregateStage.withAccumulators(accumulators[0], rest); + } + + List> groupMaps = (List>) stageMap.get("groups"); + if (groupMaps != null && !groupMaps.isEmpty()) { + Selectable firstGroup = parseSelectable(groupMaps.get(0)); + + if (groupMaps.size() == 1) { + aggregateStage = aggregateStage.withGroups(firstGroup); + } else { + Object[] additionalGroups = new Object[groupMaps.size() - 1]; + for (int i = 1; i < groupMaps.size(); i++) { + Expression groupExpr = parseExpression(groupMaps.get(i)); + additionalGroups[i - 1] = groupExpr; + } + aggregateStage = aggregateStage.withGroups(firstGroup, additionalGroups); + } + } + + return aggregateStage; + } + + AggregateOptions parseAggregateOptions(@NonNull Map optionsMap) { + return new AggregateOptions(); + } + + FindNearestStage.DistanceMeasure parseDistanceMeasure(@NonNull String dartEnumName) { + switch (dartEnumName) { + case "cosine": + return FindNearestStage.DistanceMeasure.COSINE; + case "euclidean": + return FindNearestStage.DistanceMeasure.EUCLIDEAN; + case "dotProduct": + return FindNearestStage.DistanceMeasure.DOT_PRODUCT; + default: + throw new IllegalArgumentException( + "Unknown distance measure: " + + dartEnumName + + ". Expected: cosine, euclidean, or dotProduct"); + } + } +} diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PigeonParser.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PigeonParser.java index 84ca398a85f3..027c9c01ed43 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PigeonParser.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PigeonParser.java @@ -56,11 +56,11 @@ public static DocumentSnapshot.ServerTimestampBehavior parsePigeonServerTimestam } } - public static GeneratedAndroidFirebaseFirestore.PigeonQuerySnapshot toPigeonQuerySnapshot( + public static GeneratedAndroidFirebaseFirestore.InternalQuerySnapshot toPigeonQuerySnapshot( com.google.firebase.firestore.QuerySnapshot querySnapshot, DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior) { - GeneratedAndroidFirebaseFirestore.PigeonQuerySnapshot.Builder pigeonQuerySnapshot = - new GeneratedAndroidFirebaseFirestore.PigeonQuerySnapshot.Builder(); + GeneratedAndroidFirebaseFirestore.InternalQuerySnapshot.Builder pigeonQuerySnapshot = + new GeneratedAndroidFirebaseFirestore.InternalQuerySnapshot.Builder(); pigeonQuerySnapshot.setMetadata(toPigeonSnapshotMetadata(querySnapshot.getMetadata())); pigeonQuerySnapshot.setDocumentChanges( toPigeonDocumentChanges(querySnapshot.getDocumentChanges(), serverTimestampBehavior)); @@ -69,20 +69,20 @@ public static GeneratedAndroidFirebaseFirestore.PigeonQuerySnapshot toPigeonQuer return pigeonQuerySnapshot.build(); } - public static GeneratedAndroidFirebaseFirestore.PigeonSnapshotMetadata toPigeonSnapshotMetadata( + public static GeneratedAndroidFirebaseFirestore.InternalSnapshotMetadata toPigeonSnapshotMetadata( com.google.firebase.firestore.SnapshotMetadata snapshotMetadata) { - GeneratedAndroidFirebaseFirestore.PigeonSnapshotMetadata.Builder pigeonSnapshotMetadata = - new GeneratedAndroidFirebaseFirestore.PigeonSnapshotMetadata.Builder(); + GeneratedAndroidFirebaseFirestore.InternalSnapshotMetadata.Builder pigeonSnapshotMetadata = + new GeneratedAndroidFirebaseFirestore.InternalSnapshotMetadata.Builder(); pigeonSnapshotMetadata.setHasPendingWrites(snapshotMetadata.hasPendingWrites()); pigeonSnapshotMetadata.setIsFromCache(snapshotMetadata.isFromCache()); return pigeonSnapshotMetadata.build(); } - public static List + public static List toPigeonDocumentChanges( List documentChanges, DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior) { - List pigeonDocumentChanges = + List pigeonDocumentChanges = new ArrayList<>(documentChanges.size()); for (com.google.firebase.firestore.DocumentChange documentChange : documentChanges) { pigeonDocumentChanges.add(toPigeonDocumentChange(documentChange, serverTimestampBehavior)); @@ -90,11 +90,11 @@ public static GeneratedAndroidFirebaseFirestore.PigeonSnapshotMetadata toPigeonS return pigeonDocumentChanges; } - public static GeneratedAndroidFirebaseFirestore.PigeonDocumentChange toPigeonDocumentChange( + public static GeneratedAndroidFirebaseFirestore.InternalDocumentChange toPigeonDocumentChange( com.google.firebase.firestore.DocumentChange documentChange, DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior) { - GeneratedAndroidFirebaseFirestore.PigeonDocumentChange.Builder pigeonDocumentChange = - new GeneratedAndroidFirebaseFirestore.PigeonDocumentChange.Builder(); + GeneratedAndroidFirebaseFirestore.InternalDocumentChange.Builder pigeonDocumentChange = + new GeneratedAndroidFirebaseFirestore.InternalDocumentChange.Builder(); pigeonDocumentChange.setType(toPigeonDocumentChangeType(documentChange.getType())); pigeonDocumentChange.setOldIndex((long) documentChange.getOldIndex()); pigeonDocumentChange.setNewIndex((long) documentChange.getNewIndex()); @@ -129,22 +129,22 @@ public static ListenSource parseListenSource( } } - public static GeneratedAndroidFirebaseFirestore.PigeonDocumentSnapshot toPigeonDocumentSnapshot( + public static GeneratedAndroidFirebaseFirestore.InternalDocumentSnapshot toPigeonDocumentSnapshot( com.google.firebase.firestore.DocumentSnapshot documentSnapshot, DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior) { - GeneratedAndroidFirebaseFirestore.PigeonDocumentSnapshot.Builder pigeonDocumentSnapshot = - new GeneratedAndroidFirebaseFirestore.PigeonDocumentSnapshot.Builder(); + GeneratedAndroidFirebaseFirestore.InternalDocumentSnapshot.Builder pigeonDocumentSnapshot = + new GeneratedAndroidFirebaseFirestore.InternalDocumentSnapshot.Builder(); pigeonDocumentSnapshot.setMetadata(toPigeonSnapshotMetadata(documentSnapshot.getMetadata())); pigeonDocumentSnapshot.setData(documentSnapshot.getData(serverTimestampBehavior)); pigeonDocumentSnapshot.setPath(documentSnapshot.getReference().getPath()); return pigeonDocumentSnapshot.build(); } - public static List + public static List toPigeonDocumentSnapshots( List documentSnapshots, DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior) { - List pigeonDocumentSnapshots = + List pigeonDocumentSnapshots = new ArrayList<>(documentSnapshots.size()); for (com.google.firebase.firestore.DocumentSnapshot documentSnapshot : documentSnapshots) { pigeonDocumentSnapshots.add( @@ -165,7 +165,7 @@ public static Query parseQuery( FirebaseFirestore firestore, @NonNull String path, boolean isCollectionGroup, - GeneratedAndroidFirebaseFirestore.PigeonQueryParameters parameters) { + GeneratedAndroidFirebaseFirestore.InternalQueryParameters parameters) { try { Query query; if (isCollectionGroup) { @@ -261,7 +261,8 @@ public static Query parseQuery( } catch (Exception exception) { Log.e( "FLTFirestoreMsgCodec", - "An error occurred while parsing query arguments, this is most likely an error with this SDK.", + "An error occurred while parsing query arguments, this is most likely an error with this" + + " SDK.", exception); return null; } diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PipelineParser.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PipelineParser.java new file mode 100644 index 000000000000..6029a28f7ef8 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PipelineParser.java @@ -0,0 +1,140 @@ +/* + * Copyright 2026, the Chromium project authors. Please see the AUTHORS file + * for details. 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.firebase.firestore.utils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.Tasks; +import com.google.firebase.firestore.DocumentReference; +import com.google.firebase.firestore.FirebaseFirestore; +import com.google.firebase.firestore.Pipeline; +import com.google.firebase.firestore.Pipeline.Snapshot; +import com.google.firebase.firestore.PipelineSource; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class PipelineParser { + private static final String TAG = "PipelineParser"; + + /** + * Executes a pipeline from a list of stage maps. + * + * @param firestore The Firestore instance + * @param stages List of stage maps, each with 'stage' and 'args' fields + * @param options Optional execution options + * @return The pipeline snapshot result + */ + public static Snapshot executePipeline( + @NonNull FirebaseFirestore firestore, + @NonNull List> stages, + @Nullable Map options) + throws Exception { + Pipeline pipeline = buildPipeline(firestore, stages); + Task task; + if (options != null && !options.isEmpty()) { + Pipeline.ExecuteOptions executeOptions = parseExecuteOptions(options); + task = pipeline.execute(executeOptions); + } else { + task = pipeline.execute(); + } + return Tasks.await(task); + } + + private static Pipeline.ExecuteOptions parseExecuteOptions(@NonNull Map options) { + Pipeline.ExecuteOptions executeOptions = new Pipeline.ExecuteOptions(); + Object indexModeObj = options.get("indexMode"); + if (indexModeObj instanceof String) { + String indexModeStr = (String) indexModeObj; + if ("recommended".equalsIgnoreCase(indexModeStr)) { + executeOptions = + executeOptions.withIndexMode(Pipeline.ExecuteOptions.IndexMode.RECOMMENDED); + } + } + return executeOptions; + } + + /** + * Builds a Pipeline from a list of stage maps without executing it. Used when a stage (e.g. + * union) requires another pipeline as an argument. + */ + @SuppressWarnings("unchecked") + public static Pipeline buildPipeline( + @NonNull FirebaseFirestore firestore, @NonNull List> stages) { + if (stages.isEmpty()) { + throw new IllegalArgumentException("Pipeline must have at least one stage (source)."); + } + ExpressionParsers expressionParsers = new ExpressionParsers(firestore); + PipelineStageHandlers stageHandlers = new PipelineStageHandlers(expressionParsers); + PipelineSource pipelineSource = firestore.pipeline(); + Pipeline pipeline = null; + + for (int i = 0; i < stages.size(); i++) { + Map stageMap = stages.get(i); + String stageName = (String) stageMap.get("stage"); + if (stageName == null) { + throw new IllegalArgumentException("Stage must have a 'stage' field"); + } + + Map args = (Map) stageMap.get("args"); + + if (i == 0) { + pipeline = applySourceStage(pipelineSource, stageName, args, firestore); + } else { + pipeline = stageHandlers.applyStage(pipeline, stageName, args, firestore); + } + } + + return pipeline; + } + + /** + * Applies a source stage (collection, collection_group, documents, database) to PipelineSource. + * These are the only stages that can be the first stage and return a Pipeline instance. + */ + @SuppressWarnings("unchecked") + private static Pipeline applySourceStage( + @NonNull PipelineSource pipelineSource, + @NonNull String stageName, + @Nullable Map args, + @NonNull FirebaseFirestore firestore) { + if (args == null && !"database".equals(stageName)) { + throw new IllegalArgumentException("Stage args must not be null for stage: " + stageName); + } + switch (stageName) { + case "collection": + { + String path = (String) args.get("path"); + return pipelineSource.collection(path); + } + case "collection_group": + { + String path = (String) args.get("path"); + return pipelineSource.collectionGroup(path); + } + case "database": + { + return pipelineSource.database(); + } + case "documents": + { + List> docMaps = (List>) args; + List docRefs = new ArrayList<>(); + for (Map docMap : docMaps) { + String docPath = (String) docMap.get("path"); + docRefs.add(firestore.document(docPath)); + } + return pipelineSource.documents(docRefs.toArray(new DocumentReference[0])); + } + default: + throw new IllegalArgumentException( + "First stage must be one of: collection, collection_group, documents, database. Got: " + + stageName); + } + } +} diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PipelineStageHandlers.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PipelineStageHandlers.java new file mode 100644 index 000000000000..78222ecef7e9 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/utils/PipelineStageHandlers.java @@ -0,0 +1,420 @@ +/* + * Copyright 2026, the Chromium project authors. Please see the AUTHORS file + * for details. 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.firebase.firestore.utils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.firebase.firestore.FirebaseFirestore; +import com.google.firebase.firestore.Pipeline; +import com.google.firebase.firestore.pipeline.AggregateOptions; +import com.google.firebase.firestore.pipeline.AggregateStage; +import com.google.firebase.firestore.pipeline.AliasedAggregate; +import com.google.firebase.firestore.pipeline.BooleanExpression; +import com.google.firebase.firestore.pipeline.Expression; +import com.google.firebase.firestore.pipeline.Field; +import com.google.firebase.firestore.pipeline.FindNearestOptions; +import com.google.firebase.firestore.pipeline.FindNearestStage; +import com.google.firebase.firestore.pipeline.Ordering; +import com.google.firebase.firestore.pipeline.SampleStage; +import com.google.firebase.firestore.pipeline.SearchStage; +import com.google.firebase.firestore.pipeline.Selectable; +import com.google.firebase.firestore.pipeline.UnnestOptions; +import java.util.List; +import java.util.Map; + +/** Handles parsing and applying pipeline stages to Pipeline instances. */ +class PipelineStageHandlers { + private final ExpressionParsers parsers; + + PipelineStageHandlers(@NonNull ExpressionParsers parsers) { + this.parsers = parsers; + } + + /** Applies a pipeline stage to a Pipeline instance. */ + @SuppressWarnings("unchecked") + Pipeline applyStage( + @NonNull Pipeline pipeline, + @NonNull String stageName, + @Nullable Map args, + @NonNull FirebaseFirestore firestore) { + switch (stageName) { + case "where": + return handleWhere(pipeline, args); + case "limit": + return handleLimit(pipeline, args); + case "offset": + return handleOffset(pipeline, args); + case "sort": + return handleSort(pipeline, args); + case "select": + return handleSelect(pipeline, args); + case "add_fields": + return handleAddFields(pipeline, args); + case "remove_fields": + return handleRemoveFields(pipeline, args); + case "distinct": + return handleDistinct(pipeline, args); + case "aggregate": + return handleAggregate(pipeline, args); + case "aggregate_with_options": + return handleAggregateWithOptions(pipeline, args); + case "unnest": + return handleUnnest(pipeline, args); + case "replace_with": + return handleReplaceWith(pipeline, args); + case "union": + return handleUnion(pipeline, args, firestore); + case "sample": + return handleSample(pipeline, args); + case "find_nearest": + return handleFindNearest(pipeline, args); + case "search": + return handleSearch(pipeline, args); + default: + throw new IllegalArgumentException("Unknown pipeline stage: " + stageName); + } + } + + private Pipeline handleWhere(@NonNull Pipeline pipeline, @Nullable Map args) { + Map expressionMap = (Map) args.get("expression"); + BooleanExpression booleanExpression = parsers.parseBooleanExpression(expressionMap); + return pipeline.where(booleanExpression); + } + + private Pipeline handleLimit(@NonNull Pipeline pipeline, @Nullable Map args) { + Number limit = (Number) args.get("limit"); + return pipeline.limit(limit.intValue()); + } + + private Pipeline handleOffset(@NonNull Pipeline pipeline, @Nullable Map args) { + Number offset = (Number) args.get("offset"); + return pipeline.offset(offset.intValue()); + } + + @SuppressWarnings("unchecked") + private Pipeline handleSort(@NonNull Pipeline pipeline, @Nullable Map args) { + List> orderingMaps = (List>) args.get("orderings"); + if (orderingMaps == null || orderingMaps.isEmpty()) { + throw new IllegalArgumentException("'sort' requires at least one ordering"); + } + + Map firstMap = orderingMaps.get(0); + Expression expression = + parsers.parseExpression((Map) firstMap.get("expression")); + String direction = (String) firstMap.get("order_direction"); + Ordering firstOrdering = + "asc".equals(direction) ? expression.ascending() : expression.descending(); + + if (orderingMaps.size() == 1) { + return pipeline.sort(firstOrdering); + } + + Ordering[] additionalOrderings = new Ordering[orderingMaps.size() - 1]; + for (int i = 1; i < orderingMaps.size(); i++) { + Map map = orderingMaps.get(i); + expression = parsers.parseExpression((Map) map.get("expression")); + direction = (String) map.get("order_direction"); + additionalOrderings[i - 1] = + "asc".equals(direction) ? expression.ascending() : expression.descending(); + } + return pipeline.sort(firstOrdering, additionalOrderings); + } + + private Pipeline handleSelect(@NonNull Pipeline pipeline, @Nullable Map args) { + List> expressionMaps = (List>) args.get("expressions"); + + if (expressionMaps == null || expressionMaps.isEmpty()) { + throw new IllegalArgumentException("'select' requires at least one expression"); + } + + // Parse first expression as Selectable + Selectable firstSelection = parsers.parseSelectable(expressionMaps.get(0)); + + // Parse remaining expressions as varargs + if (expressionMaps.size() == 1) { + return pipeline.select(firstSelection); + } + + Object[] additionalSelections = new Object[expressionMaps.size() - 1]; + for (int i = 1; i < expressionMaps.size(); i++) { + Expression expr = parsers.parseExpression(expressionMaps.get(i)); + // Additional selections can be Selectable or any Object + additionalSelections[i - 1] = expr; + } + + return pipeline.select(firstSelection, additionalSelections); + } + + private Pipeline handleAddFields(@NonNull Pipeline pipeline, @Nullable Map args) { + List> expressionMaps = (List>) args.get("expressions"); + + if (expressionMaps == null || expressionMaps.isEmpty()) { + throw new IllegalArgumentException("'add_fields' requires at least one expression"); + } + + // Parse first expression as Selectable + Selectable firstField = parsers.parseSelectable(expressionMaps.get(0)); + + // Parse remaining expressions as Selectable varargs + if (expressionMaps.size() == 1) { + return pipeline.addFields(firstField); + } + + Selectable[] additionalFields = new Selectable[expressionMaps.size() - 1]; + for (int i = 1; i < expressionMaps.size(); i++) { + additionalFields[i - 1] = parsers.parseSelectable(expressionMaps.get(i)); + } + + return pipeline.addFields(firstField, additionalFields); + } + + private Pipeline handleRemoveFields( + @NonNull Pipeline pipeline, @Nullable Map args) { + List fieldPaths = (List) args.get("field_paths"); + + if (fieldPaths == null || fieldPaths.isEmpty()) { + throw new IllegalArgumentException("'remove_fields' requires at least one field path"); + } + + // Convert first field path string to Field + Field firstField = Expression.field(fieldPaths.get(0)); + + // Convert remaining field paths to Field varargs + if (fieldPaths.size() == 1) { + return pipeline.removeFields(firstField); + } + + Field[] additionalFields = new Field[fieldPaths.size() - 1]; + for (int i = 1; i < fieldPaths.size(); i++) { + additionalFields[i - 1] = Expression.field(fieldPaths.get(i)); + } + + return pipeline.removeFields(firstField, additionalFields); + } + + private Pipeline handleDistinct(@NonNull Pipeline pipeline, @Nullable Map args) { + List> expressionMaps = (List>) args.get("expressions"); + + if (expressionMaps == null || expressionMaps.isEmpty()) { + throw new IllegalArgumentException("'distinct' requires at least one expression"); + } + + // Parse first expression as Selectable + Selectable firstGroup = parsers.parseSelectable(expressionMaps.get(0)); + + // Parse remaining expressions as varargs (can be Selectable or Any) + if (expressionMaps.size() == 1) { + return pipeline.distinct(firstGroup); + } + + Object[] additionalGroups = new Object[expressionMaps.size() - 1]; + for (int i = 1; i < expressionMaps.size(); i++) { + Expression expr = parsers.parseExpression(expressionMaps.get(i)); + // Additional groups can be Selectable or any Object + additionalGroups[i - 1] = expr; + } + + return pipeline.distinct(firstGroup, additionalGroups); + } + + @SuppressWarnings("unchecked") + private Pipeline handleAggregate(@NonNull Pipeline pipeline, @Nullable Map args) { + List> aggregateMaps = + (List>) args.get("aggregate_functions"); + + if (aggregateMaps == null || aggregateMaps.isEmpty()) { + throw new IllegalArgumentException("'aggregate' requires at least one aggregate function"); + } + + AliasedAggregate firstAccumulator = parsers.parseAliasedAggregate(aggregateMaps.get(0)); + + if (aggregateMaps.size() == 1) { + return pipeline.aggregate(firstAccumulator); + } + + AliasedAggregate[] additionalAccumulators = new AliasedAggregate[aggregateMaps.size() - 1]; + for (int i = 1; i < aggregateMaps.size(); i++) { + additionalAccumulators[i - 1] = parsers.parseAliasedAggregate(aggregateMaps.get(i)); + } + + return pipeline.aggregate(firstAccumulator, additionalAccumulators); + } + + @SuppressWarnings("unchecked") + private Pipeline handleAggregateWithOptions( + @NonNull Pipeline pipeline, @Nullable Map args) { + Map aggregateStageMap = (Map) args.get("aggregate_stage"); + + AggregateStage aggregateStage = parsers.parseAggregateStage(aggregateStageMap); + + Map optionsMap = (Map) args.get("options"); + if (optionsMap != null && !optionsMap.isEmpty()) { + AggregateOptions options = parsers.parseAggregateOptions(optionsMap); + return pipeline.aggregate(aggregateStage, options); + } + return pipeline.aggregate(aggregateStage); + } + + private Pipeline handleUnnest(@NonNull Pipeline pipeline, @Nullable Map args) { + Map expressionMap = (Map) args.get("expression"); + Selectable expression = parsers.parseSelectable(expressionMap); + String indexField = (String) args.get("index_field"); + if (indexField != null) { + return pipeline.unnest(expression, new UnnestOptions().withIndexField(indexField)); + } else { + return pipeline.unnest(expression); + } + } + + private Pipeline handleReplaceWith( + @NonNull Pipeline pipeline, @Nullable Map args) { + Map expressionMap = (Map) args.get("expression"); + Expression expression = parsers.parseExpression(expressionMap); + return pipeline.replaceWith(expression); + } + + @SuppressWarnings("unchecked") + private Pipeline handleUnion( + @NonNull Pipeline pipeline, + @Nullable Map args, + @NonNull FirebaseFirestore firestore) { + List> nestedStages = (List>) args.get("pipeline"); + if (nestedStages == null || nestedStages.isEmpty()) { + throw new IllegalArgumentException("'union' requires a non-empty 'pipeline' argument"); + } + Pipeline otherPipeline = PipelineParser.buildPipeline(firestore, nestedStages); + return pipeline.union(otherPipeline); + } + + private Pipeline handleSample(@NonNull Pipeline pipeline, @Nullable Map args) { + // Sample stage parsing + Map sampleMap = (Map) args; + // Parse sample configuration + String type = (String) sampleMap.get("type"); + if ("percentage".equals(type)) { + double value = ((Number) sampleMap.get("value")).doubleValue(); + return pipeline.sample(SampleStage.withPercentage(value)); + } else { + int value = ((Number) sampleMap.get("value")).intValue(); + return pipeline.sample(SampleStage.withDocLimit(value)); + } + } + + @SuppressWarnings("unchecked") + private Pipeline handleFindNearest( + @NonNull Pipeline pipeline, @Nullable Map args) { + String vectorField = (String) args.get("vector_field"); + List vectorValue = (List) args.get("vector_value"); + String distanceMeasureStr = (String) args.get("distance_measure"); + Number limitObj = (Number) args.get("limit"); + + if (distanceMeasureStr == null) { + throw new IllegalArgumentException("'find_nearest' requires a 'distance_measure' argument"); + } + + // Convert Dart enum name to Android enum value + FindNearestStage.DistanceMeasure distanceMeasure = + parsers.parseDistanceMeasure(distanceMeasureStr); + + // Convert vector value to double array + double[] vectorArray = new double[vectorValue.size()]; + for (int i = 0; i < vectorValue.size(); i++) { + vectorArray[i] = vectorValue.get(i).doubleValue(); + } + + Field fieldExpr = Expression.field(vectorField); + + if (limitObj != null) { + return pipeline.findNearest( + vectorField, + Expression.vector(vectorArray), + distanceMeasure, + new FindNearestOptions().withLimit(limitObj.intValue())); + } else { + return pipeline.findNearest(fieldExpr, vectorArray, distanceMeasure); + } + } + + @SuppressWarnings("unchecked") + private Pipeline handleSearch(@NonNull Pipeline pipeline, @Nullable Map args) { + if (args == null) { + throw new IllegalArgumentException("'search' requires arguments"); + } + + String queryType = (String) args.get("query_type"); + Object query = args.get("query"); + SearchStage searchStage; + if ("string".equals(queryType)) { + searchStage = SearchStage.withQuery((String) query); + } else if ("expression".equals(queryType)) { + BooleanExpression expressionQuery = + parsers.parseBooleanExpression((Map) query); + searchStage = SearchStage.withQuery(expressionQuery); + } else { + throw new IllegalArgumentException( + "'search' requires query_type to be either 'string' or 'expression'"); + } + + List> sortMaps = (List>) args.get("sort"); + if (sortMaps != null && !sortMaps.isEmpty()) { + Ordering firstOrdering = parseOrdering(sortMaps.get(0)); + if (sortMaps.size() == 1) { + searchStage = searchStage.withSort(firstOrdering); + } else { + Ordering[] additionalOrderings = new Ordering[sortMaps.size() - 1]; + for (int i = 1; i < sortMaps.size(); i++) { + additionalOrderings[i - 1] = parseOrdering(sortMaps.get(i)); + } + searchStage = searchStage.withSort(firstOrdering, additionalOrderings); + } + } + + List> addFieldMaps = (List>) args.get("add_fields"); + if (addFieldMaps != null && !addFieldMaps.isEmpty()) { + Selectable firstField = parsers.parseSelectable(addFieldMaps.get(0)); + if (addFieldMaps.size() == 1) { + searchStage = searchStage.withAddFields(firstField); + } else { + Selectable[] additionalFields = new Selectable[addFieldMaps.size() - 1]; + for (int i = 1; i < addFieldMaps.size(); i++) { + additionalFields[i - 1] = parsers.parseSelectable(addFieldMaps.get(i)); + } + searchStage = searchStage.withAddFields(firstField, additionalFields); + } + } + + String languageCode = (String) args.get("language_code"); + if (languageCode != null) { + searchStage = searchStage.withLanguageCode(languageCode); + } + + Number limit = (Number) args.get("limit"); + if (limit != null) { + searchStage = searchStage.withLimit(limit.longValue()); + } + + Number offset = (Number) args.get("offset"); + if (offset != null) { + searchStage = searchStage.withOffset(offset.longValue()); + } + + Number retrievalDepth = (Number) args.get("retrieval_depth"); + if (retrievalDepth != null) { + searchStage = searchStage.withRetrievalDepth(retrievalDepth.longValue()); + } + + return pipeline.search(searchStage); + } + + @SuppressWarnings("unchecked") + private Ordering parseOrdering(@NonNull Map orderingMap) { + Expression expression = + parsers.parseExpression((Map) orderingMap.get("expression")); + String direction = (String) orderingMap.get("order_direction"); + return "asc".equals(direction) ? expression.ascending() : expression.descending(); + } +} diff --git a/packages/cloud_firestore/cloud_firestore/example/android/app/build.gradle b/packages/cloud_firestore/cloud_firestore/example/android/app/build.gradle index cf782a8a5d88..b572f8ff646f 100644 --- a/packages/cloud_firestore/cloud_firestore/example/android/app/build.gradle +++ b/packages/cloud_firestore/cloud_firestore/example/android/app/build.gradle @@ -45,7 +45,7 @@ android { applicationId = "io.flutter.plugins.firebase.firestore.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = 23 + minSdkVersion = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName diff --git a/packages/cloud_firestore/cloud_firestore/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/firestore/example/MainActivity.kt b/packages/cloud_firestore/cloud_firestore/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/firestore/example/MainActivity.kt index 47ab27f36c9a..18b6218e85dd 100644 --- a/packages/cloud_firestore/cloud_firestore/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/firestore/example/MainActivity.kt +++ b/packages/cloud_firestore/cloud_firestore/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/firestore/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.firestore.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/cloud_firestore/cloud_firestore/example/android/gradle.properties b/packages/cloud_firestore/cloud_firestore/example/android/gradle.properties index 3c0f502f334a..6bb94725c175 100644 --- a/packages/cloud_firestore/cloud_firestore/example/android/gradle.properties +++ b/packages/cloud_firestore/cloud_firestore/example/android/gradle.properties @@ -1,4 +1,8 @@ org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true -androidGradlePluginVersion=8.3.0 \ No newline at end of file +androidGradlePluginVersion=8.3.0 +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart index 5e570f983331..9063b00f15b6 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart @@ -88,51 +88,62 @@ void runDocumentReferenceTests() { }); }); - test('listens to a single response from cache', () async { - DocumentReference> document = - await initializeTest('document-snapshot'); - Stream>> stream = - document.snapshots(source: ListenSource.cache); - StreamSubscription>>? - subscription; - - subscription = stream.listen( - expectAsync1( - (DocumentSnapshot> snapshot) { - expect(snapshot.exists, isFalse); - }, - reason: 'Stream should only have been called once.', - ), - ); - - addTearDown(() async { - await subscription?.cancel(); - }); - }); + test( + 'listens to a single response from cache', + () async { + DocumentReference> document = + await initializeTest('document-snapshot'); + Stream>> stream = + document.snapshots(source: ListenSource.cache); + StreamSubscription>>? + subscription; + + subscription = stream.listen( + expectAsync1( + (DocumentSnapshot> snapshot) { + expect(snapshot.exists, isFalse); + }, + reason: 'Stream should only have been called once.', + ), + ); - test('listens to a document from cache', () async { - DocumentReference> document = - await initializeTest('document-snapshot-cache'); - await document.set({'foo': 'bar'}); - Stream>> stream = - document.snapshots(source: ListenSource.cache); - StreamSubscription>>? - subscription; + addTearDown(() async { + await subscription?.cancel(); + }); + }, + // Listening from cache is not supported on Windows (see + // DocumentReference.snapshots in cloud_firestore). + skip: defaultTargetPlatform == TargetPlatform.windows, + ); - subscription = stream.listen( - expectAsync1( - (DocumentSnapshot> snapshot) { - expect(snapshot.exists, isTrue); - expect(snapshot.data(), equals({'foo': 'bar'})); - }, - reason: 'Stream should only have been called once.', - ), - ); + test( + 'listens to a document from cache', + () async { + DocumentReference> document = + await initializeTest('document-snapshot-cache'); + await document.set({'foo': 'bar'}); + Stream>> stream = + document.snapshots(source: ListenSource.cache); + StreamSubscription>>? + subscription; + + subscription = stream.listen( + expectAsync1( + (DocumentSnapshot> snapshot) { + expect(snapshot.exists, isTrue); + expect(snapshot.data(), equals({'foo': 'bar'})); + }, + reason: 'Stream should only have been called once.', + ), + ); - addTearDown(() async { - await subscription?.cancel(); - }); - }); + addTearDown(() async { + await subscription?.cancel(); + }); + }, + // Listening from cache is not supported on Windows. + skip: defaultTargetPlatform == TargetPlatform.windows, + ); test('listens to multiple documents', () async { DocumentReference> doc1 = @@ -408,7 +419,8 @@ void runDocumentReferenceTests() { 'null': null, 'timestamp': Timestamp.now(), 'geopoint': const GeoPoint(1, 2), - 'vectorValue': const VectorValue([1, 2, 3]), + if (defaultTargetPlatform != TargetPlatform.windows) + 'vectorValue': const VectorValue([1, 2, 3]), 'reference': firestore.doc('foo/bar'), 'nan': double.nan, 'infinity': double.infinity, @@ -445,11 +457,13 @@ void runDocumentReferenceTests() { expect(data['geopoint'], isA()); expect((data['geopoint'] as GeoPoint).latitude, equals(1)); expect((data['geopoint'] as GeoPoint).longitude, equals(2)); - expect(data['vectorValue'], isA()); - expect( - (data['vectorValue'] as VectorValue).toArray(), - equals([1, 2, 3]), - ); + if (defaultTargetPlatform != TargetPlatform.windows) { + expect(data['vectorValue'], isA()); + expect( + (data['vectorValue'] as VectorValue).toArray(), + equals([1, 2, 3]), + ); + } expect(data['reference'], isA()); expect((data['reference'] as DocumentReference).id, equals('bar')); expect(data['nan'].isNaN, equals(true)); diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart index 49b7bb0c932a..d97ddc4e2fda 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/query_e2e.dart @@ -8,6 +8,7 @@ import 'dart:math'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void runQueryTests() { @@ -295,8 +296,9 @@ void runQueryTests() { await subscription?.cancel(); }); }, - // Failing on CI but works locally - skip: kIsWeb, + // Failing on CI but works locally. Listening from cache is not + // supported on Windows. + skip: kIsWeb || defaultTargetPlatform == TargetPlatform.windows, ); test('listens to multiple queries', () async { @@ -373,6 +375,88 @@ void runQueryTests() { await subscription.cancel(); }); + testWidgets( + 'large snapshots do not block frame scheduling', + (WidgetTester tester) async { + CollectionReference> collection = + await initializeTest('large-snapshot-listener'); + const int documentCount = 1000; + final String payload = List.filled(1024, 'x').join(); + + for (int start = 0; start < documentCount; start += 400) { + final WriteBatch batch = firestore.batch(); + final int end = min(start + 400, documentCount); + for (int index = start; index < end; index++) { + batch.set(collection.doc('doc-$index'), { + 'index': index, + 'payload': payload, + }); + } + await batch.commit(); + } + + final Completer initialSnapshot = Completer(); + final Completer receivedUpdates = Completer(); + var initialSnapshotReceived = false; + var updateSnapshots = 0; + + final StreamSubscription>> + subscription = collection.snapshots().listen((snapshot) { + if (!initialSnapshotReceived && snapshot.size == documentCount) { + initialSnapshotReceived = true; + initialSnapshot.complete(); + return; + } + + if (initialSnapshotReceived && snapshot.docChanges.isNotEmpty) { + updateSnapshots++; + if (updateSnapshots >= 3 && !receivedUpdates.isCompleted) { + receivedUpdates.complete(); + } + } + }); + addTearDown(subscription.cancel); + + await initialSnapshot.future.timeout(const Duration(seconds: 30)); + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: Center(child: CircularProgressIndicator()), + ), + ); + + var updatesDone = false; + final updateFuture = Future(() async { + for (int index = 0; index < 3; index++) { + await collection.doc('doc-0').update({ + 'counter': index, + 'payload': payload, + }); + } + await receivedUpdates.future.timeout(const Duration(seconds: 30)); + }).whenComplete(() { + updatesDone = true; + }); + + final pumpDurations = []; + while (!updatesDone) { + final Stopwatch stopwatch = Stopwatch()..start(); + await tester.pump(const Duration(milliseconds: 16)); + stopwatch.stop(); + pumpDurations.add(stopwatch.elapsed); + } + await updateFuture; + + expect(pumpDurations, isNotEmpty); + final Duration longestPump = pumpDurations.reduce( + (current, next) => current > next ? current : next, + ); + expect(longestPump, lessThan(const Duration(milliseconds: 750))); + }, + timeout: const Timeout.factor(10), + skip: kIsWeb || defaultTargetPlatform == TargetPlatform.windows, + ); + test( 'listeners throws a [FirebaseException] with Query', () async { @@ -1022,6 +1106,33 @@ void runQueryTests() { expect(snapshot.docs[1].id, equals('doc4')); }); + test( + 'startAfterDocument() preserves Timestamp cursor precision', + () async { + CollectionReference> collection = + await initializeTest('startAfter-document-timestamp-precision'); + await collection.doc('doc1').set({ + 'createdAt': Timestamp(1, 123456789), + }); + + Query> baseQuery = + collection.orderBy('createdAt'); + QuerySnapshot> firstPage = + await baseQuery.limit(50).get(); + + expect(firstPage.docs.length, equals(1)); + expect(firstPage.docs.first.id, equals('doc1')); + + QuerySnapshot> nextPage = await baseQuery + .startAfterDocument(firstPage.docs.last) + .limit(50) + .get(); + + expect(nextPage.docs, isEmpty); + }, + skip: !kIsWeb, + ); + testWidgets( 'throws exception without orderBy() on field used for inequality query', (_) async { @@ -1052,14 +1163,17 @@ void runQueryTests() { isA().having( (e) => e.message, 'message', - contains( - 'Client specified an invalid argument', + anyOf( + contains('Client specified an invalid argument'), + contains('order by clause cannot contain more fields ' + 'after the key'), ), ), ), ); }, - // firebase-js-sdk does not require an orderBy() field to be set for this to work + // firebase-js-sdk does not require an orderBy() field to be set for + // this to work skip: kIsWeb, ); @@ -1106,8 +1220,10 @@ void runQueryTests() { isA().having( (e) => e.message, 'message', - contains( - 'Client specified an invalid argument', + anyOf( + contains('Client specified an invalid argument'), + contains('order by clause cannot contain more fields ' + 'after the key'), ), ), ), @@ -3798,6 +3914,7 @@ void runQueryTests() { 3, ); }, + skip: defaultTargetPlatform == TargetPlatform.windows, ); test( @@ -3820,6 +3937,7 @@ void runQueryTests() { 1, ); }, + skip: defaultTargetPlatform == TargetPlatform.windows, ); test( @@ -3841,6 +3959,7 @@ void runQueryTests() { 1.5, ); }, + skip: defaultTargetPlatform == TargetPlatform.windows, ); test( @@ -3863,6 +3982,7 @@ void runQueryTests() { 1, ); }, + skip: defaultTargetPlatform == TargetPlatform.windows, ); test( @@ -3894,37 +4014,42 @@ void runQueryTests() { 1.5, ); }, + skip: defaultTargetPlatform == TargetPlatform.windows, ); - test('chaining multiples aggregate queries', () async { - final collection = await initializeTest('chaining'); + test( + 'chaining multiples aggregate queries', + () async { + final collection = await initializeTest('chaining'); - await Future.wait([ - collection.add({'foo': 1}), - collection.add({'foo': 2}), - ]); + await Future.wait([ + collection.add({'foo': 1}), + collection.add({'foo': 2}), + ]); - AggregateQuery query = collection - .where('foo', isEqualTo: 1) - .aggregate(count(), sum('foo'), average('foo')); + AggregateQuery query = collection + .where('foo', isEqualTo: 1) + .aggregate(count(), sum('foo'), average('foo')); - AggregateQuerySnapshot snapshot = await query.get(); + AggregateQuerySnapshot snapshot = await query.get(); - expect( - snapshot.count, - 1, - ); + expect( + snapshot.count, + 1, + ); - expect( - snapshot.getSum('foo'), - 1, - ); + expect( + snapshot.getSum('foo'), + 1, + ); - expect( - snapshot.getAverage('foo'), - 1, - ); - }); + expect( + snapshot.getAverage('foo'), + 1, + ); + }, + skip: defaultTargetPlatform == TargetPlatform.windows, + ); test( 'count() with collectionGroup', @@ -3966,16 +4091,20 @@ void runQueryTests() { }, ); - test('count(), average() & sum() on empty collection', () async { - final collection = await initializeTest('empty-collection'); + test( + 'count(), average() & sum() on empty collection', + () async { + final collection = await initializeTest('empty-collection'); - final snapshot = await collection - .aggregate(count(), sum('foo'), average('foo')) - .get(); - expect(snapshot.count, 0); - expect(snapshot.getSum('foo'), 0); - expect(snapshot.getAverage('foo'), null); - }); + final snapshot = await collection + .aggregate(count(), sum('foo'), average('foo')) + .get(); + expect(snapshot.count, 0); + expect(snapshot.getSum('foo'), 0); + expect(snapshot.getAverage('foo'), null); + }, + skip: defaultTargetPlatform == TargetPlatform.windows, + ); }); group('startAfterDocument', () { diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/transaction_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/transaction_e2e.dart index 3b5017077dc7..8a55da126ad6 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/transaction_e2e.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/transaction_e2e.dart @@ -126,39 +126,43 @@ void runTransactionTests() { retry: 2, ); - test('should collide if number of maxAttempts is too low', () async { - DocumentReference> doc1 = - await initializeTest('transaction-maxAttempts-2'); + test( + 'should collide if number of maxAttempts is too low', + () async { + DocumentReference> doc1 = + await initializeTest('transaction-maxAttempts-2'); - await doc1.set({'test': 0}); + await doc1.set({'test': 0}); - await expectLater( - Future.wait([ - firestore.runTransaction( - (Transaction transaction) async { - final value = await transaction.get(doc1); - transaction.set(doc1, { - 'test': value['test'] + 1, - }); - }, - maxAttempts: 1, - ), - firestore.runTransaction( - (Transaction transaction) async { - final value = await transaction.get(doc1); - transaction.set(doc1, { - 'test': value['test'] + 1, - }); - }, - maxAttempts: 1, + await expectLater( + Future.wait([ + firestore.runTransaction( + (Transaction transaction) async { + final value = await transaction.get(doc1); + transaction.set(doc1, { + 'test': value['test'] + 1, + }); + }, + maxAttempts: 1, + ), + firestore.runTransaction( + (Transaction transaction) async { + final value = await transaction.get(doc1); + transaction.set(doc1, { + 'test': value['test'] + 1, + }); + }, + maxAttempts: 1, + ), + ]), + throwsA( + isA() + .having((e) => e.code, 'code', 'failed-precondition'), ), - ]), - throwsA( - isA() - .having((e) => e.code, 'code', 'failed-precondition'), - ), - ); - }); + ); + }, + skip: kIsWeb || defaultTargetPlatform == TargetPlatform.windows, + ); test('runs multiple transactions in parallel', () async { DocumentReference> doc1 = @@ -188,19 +192,23 @@ void runTransactionTests() { expect(snapshot2.data()!['test'], equals('value4')); }); - test('should abort if timeout is exceeded', () async { - await expectLater( - firestore.runTransaction( - (Transaction transaction) => - Future.delayed(const Duration(seconds: 2)), - timeout: const Duration(seconds: 1), - ), - throwsA( - isA() - .having((e) => e.code, 'code', 'deadline-exceeded'), - ), - ); - }); + test( + 'should abort if timeout is exceeded', + () async { + await expectLater( + firestore.runTransaction( + (Transaction transaction) => + Future.delayed(const Duration(seconds: 2)), + timeout: const Duration(seconds: 1), + ), + throwsA( + isA() + .having((e) => e.code, 'code', 'deadline-exceeded'), + ), + ); + }, + skip: kIsWeb || defaultTargetPlatform == TargetPlatform.windows, + ); test('should throw with exception', () async { try { @@ -217,23 +225,26 @@ void runTransactionTests() { } }); - test('should throw a native error, and convert to a [FirebaseException]', - () async { - DocumentReference> documentReference = - firestore.doc('not-allowed/document'); + test( + 'should throw a native error, and convert to a [FirebaseException]', + () async { + DocumentReference> documentReference = + firestore.doc('not-allowed/document'); - try { - await firestore.runTransaction((Transaction transaction) async { - transaction.set(documentReference, {'foo': 'bar'}); - }); - fail('Transaction should not have resolved'); - } on FirebaseException catch (e) { - expect(e.code, equals('permission-denied')); - return; - } catch (e) { - fail('Transaction threw invalid exception'); - } - }); + try { + await firestore.runTransaction((Transaction transaction) async { + transaction.set(documentReference, {'foo': 'bar'}); + }); + fail('Transaction should not have resolved'); + } on FirebaseException catch (e) { + expect(e.code, equals('permission-denied')); + return; + } catch (e) { + fail('Transaction threw invalid exception'); + } + }, + skip: kIsWeb || defaultTargetPlatform == TargetPlatform.windows, + ); group('Transaction.get()', () { test('should throw if get is called after a command', () async { @@ -251,23 +262,25 @@ void runTransactionTests() { }); test( - 'should throw a native error, and convert to a [FirebaseException]', - () async { - DocumentReference> documentReference = - firestore.doc('not-allowed/document'); + 'should throw a native error, and convert to a [FirebaseException]', + () async { + DocumentReference> documentReference = + firestore.doc('not-allowed/document'); - try { - await firestore.runTransaction((Transaction transaction) async { - await transaction.get(documentReference); - }); - fail('Transaction should not have resolved'); - } on FirebaseException catch (e) { - expect(e.code, equals('permission-denied')); - return; - } catch (e) { - fail('Transaction threw invalid exception'); - } - }); + try { + await firestore.runTransaction((Transaction transaction) async { + await transaction.get(documentReference); + }); + fail('Transaction should not have resolved'); + } on FirebaseException catch (e) { + expect(e.code, equals('permission-denied')); + return; + } catch (e) { + fail('Transaction threw invalid exception'); + } + }, + skip: kIsWeb || defaultTargetPlatform == TargetPlatform.windows, + ); // ignore: todo // TODO(Salakar): Test seems to fail sometimes. Will look at in a future PR. diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/vector_value_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/vector_value_e2e.dart index 3163a65b36a4..e10b4689e642 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/vector_value_e2e.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/vector_value_e2e.dart @@ -3,9 +3,21 @@ // BSD-style license that can be found in the LICENSE file. import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; void runVectorValueTests() { + if (defaultTargetPlatform == TargetPlatform.windows) { + group('$VectorValue', () { + test( + 'is not supported on Windows', + () {}, + skip: 'The Firebase C++ SDK does not expose Firestore vector values.', + ); + }); + return; + } + group('$VectorValue', () { late FirebaseFirestore firestore; diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/write_batch_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/write_batch_e2e.dart index 7eb49c04016b..5853c56da4bb 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/write_batch_e2e.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/write_batch_e2e.dart @@ -70,6 +70,94 @@ void runWriteBatchTests() { expect(snapshot.exists, false); }); + test('updates with typed data through withConverter', () async { + CollectionReference> collection = + await initializeTest('with-converter-batch-update'); + WriteBatch batch = firestore.batch(); + + DocumentReference doc = collection.doc('doc1').withConverter( + fromFirestore: (snapshot, options) { + return snapshot.data()!['value'] as int; + }, + toFirestore: (value, options) => {'value': value}, + ); + + await doc.set(42); + + batch.update(doc, 21); + + await batch.commit(); + + DocumentSnapshot snapshot = await doc.get(); + expect(snapshot.exists, isTrue); + expect(snapshot.data(), 21); + }); + + test('updates complex typed data through withConverter', () async { + CollectionReference> collection = + await initializeTest('with-converter-complex-batch-update'); + DocumentReference> rawDoc = collection.doc('doc1'); + DocumentReference<_WriteBatchProfile> doc = rawDoc.withConverter( + fromFirestore: (snapshot, options) { + return _WriteBatchProfile.fromFirestore(snapshot.data()!); + }, + toFirestore: (value, options) => value.toFirestore(), + ); + + await rawDoc.set({ + 'existing': 'preserved', + 'name': 'before', + }); + + WriteBatch batch = firestore.batch(); + batch.update<_WriteBatchProfile>( + doc, + _WriteBatchProfile( + name: 'Ada', + score: 42, + address: _WriteBatchAddress(city: 'London', postcode: 'NW1'), + tags: ['admin', 'tester'], + preferences: { + 'email': true, + 'theme': 'dark', + }, + nickname: null, + ), + ); + + await batch.commit(); + + DocumentSnapshot> rawSnapshot = await rawDoc.get(); + expect(rawSnapshot.data(), { + 'existing': 'preserved', + 'name': 'Ada', + 'score': 42, + 'address': { + 'city': 'London', + 'postcode': 'NW1', + }, + 'tags': ['admin', 'tester'], + 'preferences': { + 'email': true, + 'theme': 'dark', + }, + 'nickname': null, + }); + + DocumentSnapshot<_WriteBatchProfile> snapshot = await doc.get(); + _WriteBatchProfile profile = snapshot.data()!; + expect(profile.name, 'Ada'); + expect(profile.score, 42); + expect(profile.address.city, 'London'); + expect(profile.address.postcode, 'NW1'); + expect(profile.tags, ['admin', 'tester']); + expect(profile.preferences, { + 'email': true, + 'theme': 'dark', + }); + expect(profile.nickname, isNull); + }); + test('should update a document using FieldPath keys', () async { CollectionReference> collection = await initializeTest('write-batch-field-path'); @@ -153,3 +241,69 @@ void runWriteBatchTests() { }); }); } + +class _WriteBatchProfile { + _WriteBatchProfile({ + required this.name, + required this.score, + required this.address, + required this.tags, + required this.preferences, + required this.nickname, + }); + + factory _WriteBatchProfile.fromFirestore(Map data) { + return _WriteBatchProfile( + name: data['name'] as String, + score: data['score'] as int, + address: _WriteBatchAddress.fromFirestore( + data['address'] as Map, + ), + tags: (data['tags'] as List).cast(), + preferences: Map.from(data['preferences'] as Map), + nickname: data['nickname'] as String?, + ); + } + + final String name; + final int score; + final _WriteBatchAddress address; + final List tags; + final Map preferences; + final String? nickname; + + Map toFirestore() { + return { + 'name': name, + 'score': score, + 'address': address.toFirestore(), + 'tags': tags, + 'preferences': preferences, + 'nickname': nickname, + }; + } +} + +class _WriteBatchAddress { + _WriteBatchAddress({ + required this.city, + required this.postcode, + }); + + factory _WriteBatchAddress.fromFirestore(Map data) { + return _WriteBatchAddress( + city: data['city'] as String, + postcode: data['postcode'] as String, + ); + } + + final String city; + final String postcode; + + Map toFirestore() { + return { + 'city': city, + 'postcode': postcode, + }; + } +} diff --git a/packages/cloud_firestore/cloud_firestore/example/ios/Flutter/AppFrameworkInfo.plist b/packages/cloud_firestore/cloud_firestore/example/ios/Flutter/AppFrameworkInfo.plist index 7c5696400627..391a902b2beb 100644 --- a/packages/cloud_firestore/cloud_firestore/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/cloud_firestore/cloud_firestore/example/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 12.0 diff --git a/packages/cloud_firestore/cloud_firestore/example/ios/Runner.xcodeproj/project.pbxproj b/packages/cloud_firestore/cloud_firestore/example/ios/Runner.xcodeproj/project.pbxproj index cd6121c60ef7..9ded141d1e15 100644 --- a/packages/cloud_firestore/cloud_firestore/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/cloud_firestore/cloud_firestore/example/ios/Runner.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A2D71CA981BE46B8B7F14013 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; E8EC7192C60686BF1D599360 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -68,6 +69,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, diff --git a/packages/cloud_firestore/cloud_firestore/example/ios/Runner/AppDelegate.swift b/packages/cloud_firestore/cloud_firestore/example/ios/Runner/AppDelegate.swift index b6363034812b..626664468b89 100644 --- a/packages/cloud_firestore/cloud_firestore/example/ios/Runner/AppDelegate.swift +++ b/packages/cloud_firestore/cloud_firestore/example/ios/Runner/AppDelegate.swift @@ -1,5 +1,5 @@ -import UIKit import Flutter +import UIKit @main @objc class AppDelegate: FlutterAppDelegate { diff --git a/packages/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/project.pbxproj b/packages/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/project.pbxproj index 38a0d1b5c6cf..16ab9abc74ca 100644 --- a/packages/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 7C6B704452E88DBDC96F005F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1127055D8A6FA8F8B81D64A8 /* Pods_Runner.framework */; }; F086503D2C16E7BB001AC8E8 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F086503C2C16E7BB001AC8E8 /* GoogleService-Info.plist */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -85,6 +86,7 @@ 8075C6E6575C8FD1BB2ACCEE /* 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 = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; F086503C2C16E7BB001AC8E8 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -99,6 +101,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 7C6B704452E88DBDC96F005F /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -160,6 +163,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, @@ -222,6 +226,9 @@ productType = "com.apple.product-type.bundle.unit-test"; }; 33CC10EC2044A3C60003C045 /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -248,6 +255,9 @@ /* Begin PBXProject section */ 33CC10E52044A3C60003C045 /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; @@ -784,6 +794,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/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 15368eccb25a..ee5d00fc0aa9 100644 --- a/packages/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/cloud_firestore/cloud_firestore/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/example/macos/Runner/AppDelegate.swift b/packages/cloud_firestore/cloud_firestore/example/macos/Runner/AppDelegate.swift index 8e02df288835..b3c176141221 100644 --- a/packages/cloud_firestore/cloud_firestore/example/macos/Runner/AppDelegate.swift +++ b/packages/cloud_firestore/cloud_firestore/example/macos/Runner/AppDelegate.swift @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/cloud_firestore/cloud_firestore/example/pubspec.yaml b/packages/cloud_firestore/cloud_firestore/example/pubspec.yaml index f2edd2a204c9..82e0b95a3f31 100755 --- a/packages/cloud_firestore/cloud_firestore/example/pubspec.yaml +++ b/packages/cloud_firestore/cloud_firestore/example/pubspec.yaml @@ -1,12 +1,14 @@ name: cloud_firestore_example description: Demonstrates how to use the firestore plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - cloud_firestore: ^6.2.0 - firebase_core: ^4.6.0 + cloud_firestore: ^6.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter http: ^1.0.0 diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift index e137837a8f4d..46f60ccd5cdf 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "6.1.3" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "6.6.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "cloud_firestore", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "cloud-firestore", targets: ["cloud_firestore"]), + .library(name: "cloud-firestore", targets: ["cloud_firestore"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,14 +30,14 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include/cloud_firestore/Private"), .headerSearchPath("include/cloud_firestore/Public"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-fst\""), ] - ), + ) ] ) diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m index a787f8ad92b9..e8119c70d761 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTDocumentSnapshotStreamHandler.m @@ -55,9 +55,11 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments }); } else { dispatch_async(dispatch_get_main_queue(), ^{ - events( - [[FirestorePigeonParser toPigeonDocumentSnapshot:snapshot - serverTimestampBehavior:self.serverTimestampBehavior] toList]); + // Emit the Pigeon object directly; the Pigeon-aware codec on the + // MessageChannel serializes it end-to-end. Pigeon 26 no longer flattens + // nested types via `toList`. + events([FirestorePigeonParser toPigeonDocumentSnapshot:snapshot + serverTimestampBehavior:self.serverTimestampBehavior]); }); } }; diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m index b7fa740fe68c..66ca1c3a12ac 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestorePlugin.m @@ -15,11 +15,20 @@ #import "include/cloud_firestore/Private/FLTFirebaseFirestoreReader.h" #import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" #import "include/cloud_firestore/Private/FLTLoadBundleStreamHandler.h" +#import "include/cloud_firestore/Private/FLTPipelineParser.h" #import "include/cloud_firestore/Private/FLTQuerySnapshotStreamHandler.h" #import "include/cloud_firestore/Private/FLTSnapshotsInSyncStreamHandler.h" #import "include/cloud_firestore/Private/FLTTransactionStreamHandler.h" #import "include/cloud_firestore/Private/FirestorePigeonParser.h" #import "include/cloud_firestore/Public/FLTFirebaseFirestorePlugin.h" +#import "include/cloud_firestore/Public/FirestoreMessages.g.h" + +// Forward-declare the Pigeon-generated reader/writer defined in +// `FirestoreMessages.g.m`. It bundles `FLTFirebaseFirestoreReader/Writer` with +// Pigeon type serialization, so it's safe to use on the plugin's method/event +// channels. +@interface FirebaseFirestoreHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end NSString *const kFLTFirebaseFirestoreChannelName = @"plugins.flutter.io/firebase_firestore"; NSString *const kFLTFirebaseFirestoreQuerySnapshotEventChannelName = @@ -73,6 +82,20 @@ - (NSString *)registerEventChannelWithPrefix:(NSString *)prefix static NSCache *_serverTimestampMap; +static id _Nullable FLTPipelineNullSafe(id value) { + return (value == nil || [value isKindOfClass:[NSNull class]]) ? nil : value; +} + +static NSNumber *_Nullable FLTPipelineTimestampToMs(id value) { + if (!value) return nil; + if ([value isKindOfClass:[NSNumber class]]) return value; + if ([value isKindOfClass:[FIRTimestamp class]]) { + FIRTimestamp *ts = value; + return @((int64_t)ts.seconds * 1000 + (int64_t)ts.nanoseconds / 1000000); + } + return nil; +} + @implementation FLTFirebaseFirestorePlugin { NSMutableDictionary *_eventChannels; NSMutableDictionary *> *_streamHandlers; @@ -91,8 +114,13 @@ @implementation FLTFirebaseFirestorePlugin { } + (void)initialize { - _codec = - [FlutterStandardMethodCodec codecWithReaderWriter:[FLTFirebaseFirestoreReaderWriter new]]; + // Use the Pigeon-generated reader/writer for MethodChannel/EventChannels so + // Pigeon types emitted by stream handlers (e.g. `InternalDocumentSnapshot`, + // `InternalSnapshotMetadata`) serialize correctly. The reader/writer extend + // `FLTFirebaseFirestoreReader/Writer`, so Firestore-specific types + // (Timestamp, GeoPoint, FieldValue, ...) still round-trip. + _codec = [FlutterStandardMethodCodec + codecWithReaderWriter:[[FirebaseFirestoreHostApiCodecReaderWriter alloc] init]]; } #pragma mark - FlutterPlugin @@ -136,7 +164,7 @@ + (void)registerWithRegistrar:(NSObject *)registrar { #else [registrar publish:instance]; #endif - FirebaseFirestoreHostApiSetup(registrar.messenger, instance); + SetUpFirebaseFirestoreHostApi(registrar.messenger, instance); } - (void)cleanupEventListeners { @@ -338,7 +366,7 @@ - (void)terminate:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResul - (void)documentReferenceGetApp:(nonnull FirestorePigeonFirebaseApp *)app request:(nonnull DocumentReferenceRequest *)request - completion:(nonnull void (^)(PigeonDocumentSnapshot *_Nullable, + completion:(nonnull void (^)(InternalDocumentSnapshot *_Nullable, FlutterError *_Nullable))completion { FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; FIRDocumentReference *document = [firestore documentWithPath:request.path]; @@ -387,7 +415,7 @@ - (void)documentReferenceSetApp:(nonnull FirestorePigeonFirebaseApp *)app - (void)documentReferenceSnapshotApp:(nonnull FirestorePigeonFirebaseApp *)app parameters:(nonnull DocumentReferenceRequest *)parameters - includeMetadataChanges:(nonnull NSNumber *)includeMetadataChanges + includeMetadataChanges:(BOOL)includeMetadataChanges source:(ListenSource)source completion:(nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { @@ -403,7 +431,6 @@ - (void)documentReferenceSnapshotApp:(nonnull FirestorePigeonFirebaseApp *)app initWithFirestore:firestore reference:document includeMetadataChanges:includeMetadataChanges - .boolValue serverTimestampBehavior:serverTimestampBehavior source:listenSource]], nil); @@ -452,8 +479,8 @@ - (void)loadBundleApp:(nonnull FirestorePigeonFirebaseApp *)app - (void)namedQueryGetApp:(nonnull FirestorePigeonFirebaseApp *)app name:(nonnull NSString *)name - options:(nonnull PigeonGetOptions *)options - completion:(nonnull void (^)(PigeonQuerySnapshot *_Nullable, + options:(nonnull InternalGetOptions *)options + completion:(nonnull void (^)(InternalQuerySnapshot *_Nullable, FlutterError *_Nullable))completion { FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; @@ -490,16 +517,16 @@ - (void)namedQueryGetApp:(nonnull FirestorePigeonFirebaseApp *)app - (void)queryGetApp:(nonnull FirestorePigeonFirebaseApp *)app path:(nonnull NSString *)path - isCollectionGroup:(nonnull NSNumber *)isCollectionGroup - parameters:(nonnull PigeonQueryParameters *)parameters - options:(nonnull PigeonGetOptions *)options - completion:(nonnull void (^)(PigeonQuerySnapshot *_Nullable, + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(nonnull InternalQueryParameters *)parameters + options:(nonnull InternalGetOptions *)options + completion:(nonnull void (^)(InternalQuerySnapshot *_Nullable, FlutterError *_Nullable))completion { FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; FIRQuery *query = [FirestorePigeonParser parseQueryWithParameters:parameters firestore:firestore path:path - isCollectionGroup:[isCollectionGroup boolValue]]; + isCollectionGroup:isCollectionGroup]; if (query == nil) { completion(nil, [FlutterError errorWithCode:@"error-parsing" message:@"An error occurred while parsing query arguments, " @@ -527,10 +554,10 @@ - (void)queryGetApp:(nonnull FirestorePigeonFirebaseApp *)app - (void)querySnapshotApp:(nonnull FirestorePigeonFirebaseApp *)app path:(nonnull NSString *)path - isCollectionGroup:(nonnull NSNumber *)isCollectionGroup - parameters:(nonnull PigeonQueryParameters *)parameters - options:(nonnull PigeonGetOptions *)options - includeMetadataChanges:(nonnull NSNumber *)includeMetadataChanges + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(nonnull InternalQueryParameters *)parameters + options:(nonnull InternalGetOptions *)options + includeMetadataChanges:(BOOL)includeMetadataChanges source:(ListenSource)source completion: (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { @@ -538,7 +565,7 @@ - (void)querySnapshotApp:(nonnull FirestorePigeonFirebaseApp *)app FIRQuery *query = [FirestorePigeonParser parseQueryWithParameters:parameters firestore:firestore path:path - isCollectionGroup:[isCollectionGroup boolValue]]; + isCollectionGroup:isCollectionGroup]; if (query == nil) { completion(nil, [FlutterError errorWithCode:@"error-parsing" message:@"An error occurred while parsing query arguments, " @@ -557,7 +584,6 @@ - (void)querySnapshotApp:(nonnull FirestorePigeonFirebaseApp *)app initWithFirestore:firestore query:query includeMetadataChanges:includeMetadataChanges - .boolValue serverTimestampBehavior:serverTimestampBehavior source:listenSource]], nil); @@ -603,9 +629,9 @@ - (void)persistenceCacheIndexManagerRequestApp:(FirestorePigeonFirebaseApp *)app completion(nil); } -- (void)setLoggingEnabledLoggingEnabled:(nonnull NSNumber *)loggingEnabled +- (void)setLoggingEnabledLoggingEnabled:(BOOL)loggingEnabled completion:(nonnull void (^)(FlutterError *_Nullable))completion { - [FIRFirestore enableLogging:[loggingEnabled boolValue]]; + [FIRFirestore enableLogging:loggingEnabled]; completion(nil); } @@ -628,7 +654,7 @@ - (void)terminateApp:(nonnull FirestorePigeonFirebaseApp *)app - (void)transactionGetApp:(nonnull FirestorePigeonFirebaseApp *)app transactionId:(nonnull NSString *)transactionId path:(nonnull NSString *)path - completion:(nonnull void (^)(PigeonDocumentSnapshot *_Nullable, + completion:(nonnull void (^)(InternalDocumentSnapshot *_Nullable, FlutterError *_Nullable))completion { // Dispatching to main thread allow us to ensure that the auth token are fetched in time // for the transaction @@ -665,8 +691,9 @@ - (void)transactionGetApp:(nonnull FirestorePigeonFirebaseApp *)app } - (void)transactionStoreResultTransactionId:(nonnull NSString *)transactionId - resultType:(PigeonTransactionResult)resultType - commands:(nullable NSArray *)commands + resultType:(InternalTransactionResult)resultType + commands: + (nullable NSArray *)commands completion:(nonnull void (^)(FlutterError *_Nullable))completion { [_transactionHandlers[transactionId] receiveTransactionResponse:resultType commands:commands]; @@ -686,26 +713,26 @@ - (void)waitForPendingWritesApp:(nonnull FirestorePigeonFirebaseApp *)app } - (void)writeBatchCommitApp:(nonnull FirestorePigeonFirebaseApp *)app - writes:(nonnull NSArray *)writes + writes:(nonnull NSArray *)writes completion:(nonnull void (^)(FlutterError *_Nullable))completion { FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; FIRWriteBatch *batch = [firestore batch]; - for (PigeonTransactionCommand *write in writes) { - PigeonTransactionType type = write.type; + for (InternalTransactionCommand *write in writes) { + InternalTransactionType type = write.type; NSString *path = write.path; FIRDocumentReference *reference = [firestore documentWithPath:path]; switch (type) { - case PigeonTransactionTypeGet: + case InternalTransactionTypeGet: break; - case PigeonTransactionTypeDeleteType: + case InternalTransactionTypeDeleteType: [batch deleteDocument:reference]; break; - case PigeonTransactionTypeUpdate: + case InternalTransactionTypeUpdate: [batch updateData:write.data forDocument:reference]; break; - case PigeonTransactionTypeSet: + case InternalTransactionTypeSet: if ([write.option.merge isEqual:@YES]) { [batch setData:write.data forDocument:reference merge:YES]; } else if (write.option.mergeFields) { @@ -741,8 +768,8 @@ - (void)snapshotsInSyncSetupApp:(nonnull FirestorePigeonFirebaseApp *)app } - (void)transactionCreateApp:(nonnull FirestorePigeonFirebaseApp *)app - timeout:(nonnull NSNumber *)timeout - maxAttempts:(nonnull NSNumber *)maxAttempts + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts completion: (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; @@ -771,10 +798,10 @@ - (void)transactionCreateApp:(nonnull FirestorePigeonFirebaseApp *)app - (void)aggregateQueryApp:(nonnull FirestorePigeonFirebaseApp *)app path:(nonnull NSString *)path - parameters:(nonnull PigeonQueryParameters *)parameters + parameters:(nonnull InternalQueryParameters *)parameters source:(AggregateSource)source queries:(nonnull NSArray *)queries - isCollectionGroup:(NSNumber *)isCollectionGroup + isCollectionGroup:(BOOL)isCollectionGroup completion:(nonnull void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion { FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; @@ -782,7 +809,7 @@ - (void)aggregateQueryApp:(nonnull FirestorePigeonFirebaseApp *)app FIRQuery *query = [FirestorePigeonParser parseQueryWithParameters:parameters firestore:firestore path:path - isCollectionGroup:[isCollectionGroup boolValue]]; + isCollectionGroup:isCollectionGroup]; if (query == nil) { completion(nil, [FlutterError errorWithCode:@"error-parsing" message:@"An error occurred while parsing query arguments, " @@ -883,4 +910,66 @@ - (void)aggregateQueryApp:(nonnull FirestorePigeonFirebaseApp *)app }]; } +- (void)executePipelineApp:(nonnull FirestorePigeonFirebaseApp *)app + stages:(nonnull NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(nonnull void (^)(InternalPipelineSnapshot *_Nullable, + FlutterError *_Nullable))completion { + FIRFirestore *firestore = [self getFIRFirestoreFromAppNameFromPigeon:app]; + + [FLTPipelineParser + executePipelineWithFirestore:firestore + stages:stages + options:options + completion:^(id _Nullable snapshot, NSError *_Nullable error) { + if (error) { + completion(nil, [self convertToFlutterError:error]); + return; + } + if (snapshot == nil) { + completion( + nil, + [FlutterError errorWithCode:@"error" + message:@"Pipeline execution returned no result" + details:nil]); + return; + } + + NSMutableArray *pigeonResults = + [NSMutableArray array]; + NSArray *results = [snapshot results]; + if ([results isKindOfClass:[NSArray class]]) { + for (id result in results) { + id ref = [result reference]; + NSString *path = (ref && [ref respondsToSelector:@selector(path)]) + ? [ref path] + : FLTPipelineNullSafe([result documentID]); + NSNumber *createTime = + FLTPipelineTimestampToMs([result valueForKey:@"create_time"]); + NSNumber *updateTime = + FLTPipelineTimestampToMs([result valueForKey:@"update_time"]); + NSDictionary *data = FLTPipelineNullSafe([result data]); + InternalPipelineResult *pigeonResult = + [InternalPipelineResult makeWithDocumentPath:path + createTime:createTime + updateTime:updateTime + data:data]; + [pigeonResults addObject:pigeonResult]; + } + } + + NSNumber *executionTime = + FLTPipelineTimestampToMs([snapshot execution_time]); + if (executionTime == nil) { + executionTime = + @((int64_t)([[NSDate date] timeIntervalSince1970] * 1000)); + } + + InternalPipelineSnapshot *pigeonSnapshot = [InternalPipelineSnapshot + makeWithResults:pigeonResults + executionTime:[executionTime longLongValue]]; + completion(pigeonSnapshot, nil); + }]; +} + @end diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m index d49fbc7f4a79..db06c89e52a0 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreUtils.m @@ -21,6 +21,8 @@ - (FlutterStandardReader *_Nonnull)readerWithData:(NSData *)data { NSMutableDictionary *firestoreInstanceCache; +const NSInteger FLTFirebaseFirestoreErrorCodePipelineParse = -1; + @implementation FLTFirebaseFirestoreUtils + (NSString *)generateKeyForAppName:(NSString *)appName andDatabaseURL:(NSString *)databaseURL { @@ -240,6 +242,11 @@ + (NSArray *)ErrorCodeAndMessageFromNSError:(NSError *)error { code = @"unknown"; message = @"Unknown error or an error from a different error domain."; break; + case FLTFirebaseFirestoreErrorCodePipelineParse: + code = @"parse-error"; + message = (error.localizedDescription.length > 0) ? error.localizedDescription + : @"An unknown error occurred."; + break; default: code = @"unknown"; message = @"An unknown error occurred."; diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m new file mode 100644 index 000000000000..c0ca562b0e33 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m @@ -0,0 +1,1541 @@ +/* + * Copyright 2026, the Chromium project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#import "include/cloud_firestore/Private/FLTPipelineParser.h" +#import "include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h" + +#if TARGET_OS_OSX +#import +#import "FirebaseFirestoreInternal/FIRPipelineBridge.h" +#else +@import FirebaseFirestore; +#if __has_include("FirebaseFirestoreInternal/FIRPipelineBridge.h") +#import "FirebaseFirestoreInternal/FIRPipelineBridge.h" +#elif __has_include("FIRPipelineBridge.h") +#import "FIRPipelineBridge.h" +#endif +#endif + +#if __has_include("FirebaseFirestoreInternal/FIRVectorValue.h") +#import "FirebaseFirestoreInternal/FIRVectorValue.h" +#elif __has_include() +#import +#endif + +#import + +static NSString *const kPipelineNotAvailable = + @"Pipeline API is not available. Firestore Pipelines require Firebase iOS SDK with pipeline " + "support."; + +static NSError *pipelineUnavailableError(void) { + return [NSError errorWithDomain:@"FLTFirebaseFirestore" + code:FLTFirebaseFirestoreErrorCodePipelineParse + userInfo:@{NSLocalizedDescriptionKey : kPipelineNotAvailable}]; +} + +#if TARGET_OS_OSX +#if __has_include("FirebaseFirestoreInternal/FIRPipelineBridge.h") +#define FLT_PIPELINE_AVAILABLE 1 +#endif +#else +#if __has_include("FirebaseFirestoreInternal/FIRPipelineBridge.h") || \ + __has_include("FIRPipelineBridge.h") +#define FLT_PIPELINE_AVAILABLE 1 +#endif +#endif + +#if FLT_PIPELINE_AVAILABLE + +// Firebase iOS SDK versions differ: some expose initWithName:Args:Options:, others +// initWithName:Args:. +@interface FIRFunctionExprBridge (FLTSDKCompat) +- (instancetype)initWithName:(NSString *)name + Args:(NSArray *)args + Options:(NSDictionary *)options; +- (instancetype)initWithName:(NSString *)name Args:(NSArray *)args; +@end + +static FIRFunctionExprBridge *FLTNewFunctionExprBridge(NSString *name, + NSArray *args) { + FIRFunctionExprBridge *obj = [FIRFunctionExprBridge alloc]; + if ([obj respondsToSelector:@selector(initWithName:Args:Options:)]) { + return [obj initWithName:name Args:args Options:nil]; + } + return [obj initWithName:name Args:args]; +} + +static NSError *parseError(NSString *message) { + return [NSError errorWithDomain:@"FLTFirebaseFirestore" + code:FLTFirebaseFirestoreErrorCodePipelineParse + userInfo:@{NSLocalizedDescriptionKey : message}]; +} + +@interface FLTPipelineExpressionParser : NSObject +@property(nonatomic, strong) FIRFirestore *firestore; +- (instancetype)initWithFirestore:(FIRFirestore *)firestore; +- (FIRExprBridge *)parseExpression:(NSDictionary *)map error:(NSError **)error; +- (FIRExprBridge *)parseBooleanExpression:(NSDictionary *)map + error:(NSError **)error; +- (FIRExprBridge *)rightExprFromValue:(id)value error:(NSError **)error; +@end + +@implementation FLTPipelineExpressionParser + +- (instancetype)initWithFirestore:(FIRFirestore *)firestore { + self = [super init]; + if (self) { + _firestore = firestore; + } + return self; +} + +- (FIRExprBridge *)parseExpression:(NSDictionary *)map error:(NSError **)error { + NSString *name = map[@"name"]; + if (!name) { + NSDictionary *args = map[@"args"]; + if ([args isKindOfClass:[NSDictionary class]] && args[@"field"]) { + return [[FIRFieldBridge alloc] initWithName:args[@"field"]]; + } + if (error) *error = parseError(@"Expression must have a 'name' field"); + return nil; + } + + NSDictionary *args = map[@"args"]; + if (![args isKindOfClass:[NSDictionary class]]) args = @{}; + + if ([name isEqualToString:@"field"]) { + NSString *field = args[@"field"]; + if (!field) { + if (error) *error = parseError(@"Field expression requires 'field' argument"); + return nil; + } + return [[FIRFieldBridge alloc] initWithName:field]; + } + + if ([name isEqualToString:@"constant"]) { + id value = args[@"value"]; + if (value == nil) { + if (error) *error = parseError(@"Constant requires 'value' argument"); + return nil; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSString *path = ((NSDictionary *)value)[@"path"]; + if ([path isKindOfClass:[NSString class]] && self.firestore) { + FIRDocumentReference *docRef = [self.firestore documentWithPath:path]; + return [[FIRConstantBridge alloc] init:docRef]; + } + } + return [[FIRConstantBridge alloc] init:value]; + } + + if ([name isEqualToString:@"alias"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"Alias requires 'expression'"); + return nil; + } + // No explicit AliasedExpression type in ObjC; aliases are dict keys when building stages. + // Parse and return the inner expression; the caller uses args[@"alias"] as the dict key. + return [self parseExpression:exprMap error:error]; + } + + if ([name isEqualToString:@"null"]) { + return [[FIRConstantBridge alloc] init:[NSNull null]]; + } + + if ([name isEqualToString:@"document_id_from_ref"]) { + NSString *path = args[@"doc_ref"]; + if (![path isKindOfClass:[NSString class]] || path.length == 0) { + if (error) *error = parseError(@"document_id_from_ref requires doc_ref path"); + return nil; + } + if (!self.firestore) { + if (error) *error = parseError(@"document_id_from_ref requires firestore"); + return nil; + } + FIRDocumentReference *docRef = [self.firestore documentWithPath:path]; + FIRExprBridge *refExpr = [[FIRConstantBridge alloc] init:docRef]; + return FLTNewFunctionExprBridge(@"document_id", @[ refExpr ]); + } + + // Swift asBoolean() is a type coercion, not a pipeline function named "as_boolean". + // Dart still sends as_boolean + expression; unwrap to the inner FIRExprBridge. + if ([name isEqualToString:@"as_boolean"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"as_boolean requires expression"); + return nil; + } + return [self parseExpression:(NSDictionary *)exprMap error:error]; + } + + if ([name isEqualToString:@"document_matches"]) { + NSString *query = args[@"query"]; + if (![query isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"document_matches requires query"); + return nil; + } + FIRExprBridge *queryExpr = [[FIRConstantBridge alloc] init:query]; + return FLTNewFunctionExprBridge(@"document_matches", @[ queryExpr ]); + } + + // Map Dart names to iOS SDK names where they differ + NSString *sdkName = name; + if ([name isEqualToString:@"bit_xor"]) sdkName = @"xor"; + if ([name isEqualToString:@"modulo"]) sdkName = @"mod"; + + // ------------------------------------------------------------------------- + // Binary expressions (left + right): comparisons, arithmetic, bitwise + // ------------------------------------------------------------------------- + static NSArray *binaryNames = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + binaryNames = @[ + @"equal", @"not_equal", @"greater_than", @"greater_than_or_equal", @"less_than", + @"less_than_or_equal", @"add", @"subtract", @"multiply", @"divide", @"mod", @"bit_and", + @"bit_or", @"bit_left_shift", @"bit_right_shift" + ]; + }); + if ([binaryNames containsObject:sdkName] || [name isEqualToString:@"bit_xor"]) { + id leftMap = args[@"left"]; + id rightMap = args[@"right"]; + if (![leftMap isKindOfClass:[NSDictionary class]] || + ![rightMap isKindOfClass:[NSDictionary class]]) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires left and right expressions", name]); + return nil; + } + FIRExprBridge *left = [self parseExpression:leftMap error:error]; + FIRExprBridge *right = [self parseExpression:rightMap error:error]; + if (!left || !right) return nil; + return FLTNewFunctionExprBridge(sdkName, @[ left, right ]); + } + + // ------------------------------------------------------------------------- + // Unary expressions (single expression): exists, is_error, is_absent, not + // (as_boolean is handled above — unwrap only, not a pipeline function.) + // ------------------------------------------------------------------------- + NSArray *unaryNames = @[ @"exists", @"is_error", @"is_absent", @"not" ]; + if ([unaryNames containsObject:name]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError([NSString stringWithFormat:@"%@ requires expression", name]); + return nil; + } + FIRExprBridge *expr = [name isEqualToString:@"not"] + ? [self parseBooleanExpression:exprMap error:error] + : [self parseExpression:exprMap error:error]; + if (!expr) return nil; + return FLTNewFunctionExprBridge(name, @[ expr ]); + } + + // ------------------------------------------------------------------------- + // Unary with optional SDK name mapping: length, to_lower, to_upper, trim, + // abs, array_length, array_reverse, bit_not, document_id, collection_id + // ------------------------------------------------------------------------- + NSArray *unaryWithSdkName = @[ + @"length", @"to_lower_case", @"to_upper_case", @"trim", @"abs", @"array_length", + @"array_reverse", @"bit_not", @"document_id", @"collection_id" + ]; + if ([unaryWithSdkName containsObject:name]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError([NSString stringWithFormat:@"%@ requires expression", name]); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + if (!expr) return nil; + NSString *unarySdkName = name; + if ([name isEqualToString:@"to_lower_case"]) unarySdkName = @"to_lower"; + if ([name isEqualToString:@"to_upper_case"]) unarySdkName = @"to_upper"; + return FLTNewFunctionExprBridge(unarySdkName, @[ expr ]); + } + + // ------------------------------------------------------------------------- + // N-ary logical (expressions array): and, or, xor + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"and"] || [name isEqualToString:@"or"] || + [name isEqualToString:@"xor"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires at least one expression", name]); + return nil; + } + NSMutableArray *all = [NSMutableArray array]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseBooleanExpression:em error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires at least one expression", name]); + return nil; + } + return FLTNewFunctionExprBridge(name, all); + } + + // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"equal_any"] || [name isEqualToString:@"not_equal_any"]) { + id valueMap = args[@"value"]; + NSArray *valuesMaps = args[@"values"]; + if (![valueMap isKindOfClass:[NSDictionary class]] || + ![valuesMaps isKindOfClass:[NSArray class]] || valuesMaps.count == 0) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires value and non-empty values", name]); + return nil; + } + FIRExprBridge *valueExpr = [self parseExpression:valueMap error:error]; + if (!valueExpr) return nil; + NSMutableArray *valueExprs = [NSMutableArray array]; + for (id vm in valuesMaps) { + if (![vm isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *ve = [self parseExpression:vm error:error]; + if (!ve) return nil; + [valueExprs addObject:ve]; + } + if (valueExprs.count == 0) { + if (error) + *error = parseError([NSString stringWithFormat:@"%@ requires at least one value", name]); + return nil; + } + FIRExprBridge *valuesArrayExpr = FLTNewFunctionExprBridge(@"array", valueExprs); + return FLTNewFunctionExprBridge(name, @[ valueExpr, valuesArrayExpr ]); + } + + // ------------------------------------------------------------------------- + // array + element: array_contains + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_contains"]) { + id arrayMap = args[@"array"]; + id elementMap = args[@"element"]; + if (![arrayMap isKindOfClass:[NSDictionary class]] || + ![elementMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_contains requires array and element"); + return nil; + } + FIRExprBridge *arrayExpr = [self parseExpression:arrayMap error:error]; + FIRExprBridge *elementExpr = [self parseExpression:elementMap error:error]; + if (!arrayExpr || !elementExpr) return nil; + return FLTNewFunctionExprBridge(name, @[ arrayExpr, elementExpr ]); + } + + // ------------------------------------------------------------------------- + // array + values[]: array_contains_all, array_contains_any + // SDK expects: array_contains_any(field, array(val1, val2, ...)) — two args. + // Reuse the "array" expression parser to build the values array. + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_contains_all"] || + [name isEqualToString:@"array_contains_any"]) { + id arrayMap = args[@"array"]; + if (![arrayMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError([NSString stringWithFormat:@"%@ requires array", name]); + return nil; + } + FIRExprBridge *arrayExpr = [self parseExpression:arrayMap error:error]; + if (!arrayExpr) return nil; + + NSArray *valuesMaps = args[@"values"]; + if (![valuesMaps isKindOfClass:[NSArray class]]) valuesMaps = args[@"elements"]; + BOOL hasValues = [valuesMaps isKindOfClass:[NSArray class]] && valuesMaps.count > 0; + + if (hasValues) { + NSDictionary *arrayExprMap = @{@"name" : @"array", @"args" : @{@"elements" : valuesMaps}}; + FIRExprBridge *valuesArrayExpr = [self parseExpression:arrayExprMap error:error]; + if (!valuesArrayExpr) return nil; + return FLTNewFunctionExprBridge(name, @[ arrayExpr, valuesArrayExpr ]); + } + + if ([name isEqualToString:@"array_contains_all"]) { + id arrayExpressionMap = args[@"array_expression"]; + if ([arrayExpressionMap isKindOfClass:[NSDictionary class]]) { + FIRExprBridge *requiredArrayExpr = [self parseExpression:arrayExpressionMap error:error]; + if (!requiredArrayExpr) return nil; + return FLTNewFunctionExprBridge(name, @[ arrayExpr, requiredArrayExpr ]); + } + } + + if (error) + *error = parseError([NSString + stringWithFormat: + @"%@ requires array and values/elements, or array_contains_all with array_expression", + name]); + return nil; + } + + // ------------------------------------------------------------------------- + // expressions[]: concat (SDK: concat) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"concat"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"concat requires non-empty expressions"); + return nil; + } + NSMutableArray *all = [NSMutableArray array]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseExpression:em error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) { + if (error) *error = parseError(@"concat requires at least one expression"); + return nil; + } + return FLTNewFunctionExprBridge(@"concat", all); + } + + // ------------------------------------------------------------------------- + // expression + start + end: substring (SDK: substring) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"substring"]) { + id exprMap = args[@"expression"]; + id startMap = args[@"start"]; + id endMap = args[@"end"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![startMap isKindOfClass:[NSDictionary class]] || + ![endMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"substring requires expression, start, and end"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *start = [self parseExpression:startMap error:error]; + FIRExprBridge *end = [self parseExpression:endMap error:error]; + if (!expr || !start || !end) return nil; + return FLTNewFunctionExprBridge(@"substring", @[ expr, start, end ]); + } + + // ------------------------------------------------------------------------- + // expression + find + replacement: replace (SDK: string_replace) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"replace"]) { + id exprMap = args[@"expression"]; + id findMap = args[@"find"]; + id replacementMap = args[@"replacement"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![findMap isKindOfClass:[NSDictionary class]] || + ![replacementMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"replace requires expression, find, and replacement"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *find = [self parseExpression:findMap error:error]; + FIRExprBridge *replacement = [self parseExpression:replacementMap error:error]; + if (!expr || !find || !replacement) return nil; + return FLTNewFunctionExprBridge(@"string_replace", @[ expr, find, replacement ]); + } + + // ------------------------------------------------------------------------- + // expression + delimiter: split, join (SDK: split, join) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"split"] || [name isEqualToString:@"join"]) { + id exprMap = args[@"expression"]; + id delimiterMap = args[@"delimiter"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![delimiterMap isKindOfClass:[NSDictionary class]]) { + if (error) + *error = + parseError([NSString stringWithFormat:@"%@ requires expression and delimiter", name]); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *delimiter = [self parseExpression:delimiterMap error:error]; + if (!expr || !delimiter) return nil; + return FLTNewFunctionExprBridge(name, @[ expr, delimiter ]); + } + + // ------------------------------------------------------------------------- + // first + second: array_concat (SDK: array_concat) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_concat"]) { + id firstMap = args[@"first"]; + id secondMap = args[@"second"]; + if (![firstMap isKindOfClass:[NSDictionary class]] || + ![secondMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_concat requires first and second"); + return nil; + } + FIRExprBridge *first = [self parseExpression:firstMap error:error]; + FIRExprBridge *second = [self parseExpression:secondMap error:error]; + if (!first || !second) return nil; + return FLTNewFunctionExprBridge(@"array_concat", @[ first, second ]); + } + + // ------------------------------------------------------------------------- + // arrays[]: array_concat_multiple (SDK: array_concat) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_concat_multiple"]) { + NSArray *arraysMaps = args[@"arrays"]; + if (![arraysMaps isKindOfClass:[NSArray class]] || arraysMaps.count == 0) { + if (error) *error = parseError(@"array_concat_multiple requires non-empty arrays"); + return nil; + } + NSMutableArray *all = [NSMutableArray array]; + for (id am in arraysMaps) { + if (![am isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseExpression:am error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) { + if (error) *error = parseError(@"array_concat_multiple requires at least one array"); + return nil; + } + return FLTNewFunctionExprBridge(@"array_concat", all); + } + + // ------------------------------------------------------------------------- + // expression + offset (+ optional length): array_slice + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_slice"]) { + id exprMap = args[@"expression"]; + id offsetMap = args[@"offset"]; + id lengthMap = args[@"length"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![offsetMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_slice requires expression and offset"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *offset = [self parseExpression:offsetMap error:error]; + if (!expr || !offset) return nil; + NSMutableArray *sliceArgs = + [NSMutableArray arrayWithObjects:expr, offset, nil]; + if ([lengthMap isKindOfClass:[NSDictionary class]]) { + FIRExprBridge *length = [self parseExpression:lengthMap error:error]; + if (!length) return nil; + [sliceArgs addObject:length]; + } + return FLTNewFunctionExprBridge(@"array_slice", sliceArgs); + } + + // ------------------------------------------------------------------------- + // expression + alias + filter: array_filter + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_filter"]) { + id exprMap = args[@"expression"]; + NSString *alias = args[@"alias"]; + id filterMap = args[@"filter"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || ![alias isKindOfClass:[NSString class]] || + ![filterMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"array_filter requires expression, alias, and filter"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *filter = [self parseBooleanExpression:filterMap error:error]; + if (!expr || !filter) return nil; + return FLTNewFunctionExprBridge(@"array_filter", + @[ expr, [[FIRConstantBridge alloc] init:alias], filter ]); + } + + // ------------------------------------------------------------------------- + // expression + aliases + transform: array_transform / array_transform_with_index + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array_transform"] || + [name isEqualToString:@"array_transform_with_index"]) { + id exprMap = args[@"expression"]; + NSString *elementAlias = args[@"element_alias"]; + NSString *indexAlias = args[@"index_alias"]; + id transformMap = args[@"transform"]; + BOOL withIndex = [name isEqualToString:@"array_transform_with_index"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![elementAlias isKindOfClass:[NSString class]] || + (withIndex && ![indexAlias isKindOfClass:[NSString class]]) || + ![transformMap isKindOfClass:[NSDictionary class]]) { + if (error) { + NSString *message = + withIndex + ? @"array_transform_with_index requires expression, element_alias, index_alias, " + @"and transform" + : @"array_transform requires expression, element_alias, and transform"; + *error = parseError(message); + } + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *transform = [self parseExpression:transformMap error:error]; + if (!expr || !transform) return nil; + NSMutableArray *transformArgs = + [NSMutableArray arrayWithObjects:expr, [[FIRConstantBridge alloc] init:elementAlias], nil]; + if (withIndex) { + [transformArgs addObject:[[FIRConstantBridge alloc] init:indexAlias]]; + } + [transformArgs addObject:transform]; + return FLTNewFunctionExprBridge(name, transformArgs); + } + + // ------------------------------------------------------------------------- + // elements[]: array (construct) — Expression.array([...]) from Dart + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"array"]) { + NSArray *elementsMaps = args[@"elements"]; + if (![elementsMaps isKindOfClass:[NSArray class]] || elementsMaps.count == 0) { + if (error) *error = parseError(@"array requires non-empty elements"); + return nil; + } + NSMutableArray *elementExprs = [NSMutableArray array]; + for (id em in elementsMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseExpression:em error:error]; + if (!e) return nil; + [elementExprs addObject:e]; + } + if (elementExprs.count == 0) { + if (error) *error = parseError(@"array requires at least one element"); + return nil; + } + return FLTNewFunctionExprBridge(@"array", elementExprs); + } + + // ------------------------------------------------------------------------- + // data: map (construct) — Expression.map({ "k": expr, ... }) from Dart + // SDK expects Args as alternating key (constant), value (expression). + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"map"]) { + NSDictionary *dataMap = args[@"data"]; + if (![dataMap isKindOfClass:[NSDictionary class]] || dataMap.count == 0) { + if (error) *error = parseError(@"map requires non-empty data"); + return nil; + } + NSMutableArray *mapArgs = [NSMutableArray array]; + for (NSString *key in dataMap) { + id valueMap = dataMap[key]; + if (![valueMap isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *keyExpr = [[FIRConstantBridge alloc] init:key]; + FIRExprBridge *valueExpr = [self parseExpression:valueMap error:error]; + if (!valueExpr) return nil; + [mapArgs addObject:keyExpr]; + [mapArgs addObject:valueExpr]; + } + if (mapArgs.count == 0) { + if (error) *error = parseError(@"map requires at least one key-value pair"); + return nil; + } + return FLTNewFunctionExprBridge(@"map", mapArgs); + } + + // ------------------------------------------------------------------------- + // map + key: map_get (SDK: map_get) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"map_get"]) { + id mapMap = args[@"map"]; + id keyMap = args[@"key"]; + if (![mapMap isKindOfClass:[NSDictionary class]] || + ![keyMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"map_get requires map and key"); + return nil; + } + FIRExprBridge *mapExpr = [self parseExpression:mapMap error:error]; + FIRExprBridge *keyExpr = [self parseExpression:keyMap error:error]; + if (!mapExpr || !keyExpr) return nil; + return FLTNewFunctionExprBridge(@"map_get", @[ mapExpr, keyExpr ]); + } + + // ------------------------------------------------------------------------- + // expression + else: if_absent (SDK: if_absent) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"if_absent"]) { + id exprMap = args[@"expression"]; + id elseMap = args[@"else"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![elseMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"if_absent requires expression and else"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *elseExpr = [self parseExpression:elseMap error:error]; + if (!expr || !elseExpr) return nil; + return FLTNewFunctionExprBridge(@"if_absent", @[ expr, elseExpr ]); + } + + // ------------------------------------------------------------------------- + // expression + catch: if_error (SDK: if_error) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"if_error"]) { + id exprMap = args[@"expression"]; + id catchMap = args[@"catch"]; + if (![exprMap isKindOfClass:[NSDictionary class]] || + ![catchMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"if_error requires expression and catch"); + return nil; + } + FIRExprBridge *expr = [self parseExpression:exprMap error:error]; + FIRExprBridge *catchExpr = [self parseExpression:catchMap error:error]; + if (!expr || !catchExpr) return nil; + return FLTNewFunctionExprBridge(@"if_error", @[ expr, catchExpr ]); + } + + // ------------------------------------------------------------------------- + // condition + then + else: conditional (SDK: conditional) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"conditional"]) { + id conditionMap = args[@"condition"]; + id thenMap = args[@"then"]; + id elseMap = args[@"else"]; + if (![conditionMap isKindOfClass:[NSDictionary class]] || + ![thenMap isKindOfClass:[NSDictionary class]] || + ![elseMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"conditional requires condition, then, and else"); + return nil; + } + FIRExprBridge *condition = [self parseBooleanExpression:conditionMap error:error]; + FIRExprBridge *thenExpr = [self parseExpression:thenMap error:error]; + FIRExprBridge *elseExpr = [self parseExpression:elseMap error:error]; + if (!condition || !thenExpr || !elseExpr) return nil; + return FLTNewFunctionExprBridge(@"conditional", @[ condition, thenExpr, elseExpr ]); + } + + // ------------------------------------------------------------------------- + // timestamp + amount + unit: timestamp_add, timestamp_subtract (SDK: Args ts, amount, unit) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"timestamp_add"] || [name isEqualToString:@"timestamp_subtract"]) { + id timestampMap = args[@"timestamp"]; + id unitVal = args[@"unit"]; + id amountMap = args[@"amount"]; + if (![timestampMap isKindOfClass:[NSDictionary class]] || !unitVal || + ![amountMap isKindOfClass:[NSDictionary class]]) { + if (error) + *error = parseError( + [NSString stringWithFormat:@"%@ requires timestamp, unit, and amount", name]); + return nil; + } + FIRExprBridge *timestampExpr = [self parseExpression:timestampMap error:error]; + FIRExprBridge *amountExpr = [self parseExpression:amountMap error:error]; + if (!timestampExpr || !amountExpr) return nil; + FIRExprBridge *unitExpr = [[FIRConstantBridge alloc] init:unitVal]; + return FLTNewFunctionExprBridge(name, @[ timestampExpr, unitExpr, amountExpr ]); + } + + // ------------------------------------------------------------------------- + // No args: current_timestamp (SDK: current_timestamp with empty Args) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"current_timestamp"]) { + return FLTNewFunctionExprBridge(@"current_timestamp", @[]); + } + + // ------------------------------------------------------------------------- + // timestamp + unit: timestamp_truncate (SDK: timestamp_trunc) + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"timestamp_truncate"]) { + id timestampMap = args[@"timestamp"]; + id unitVal = args[@"unit"]; + if (![timestampMap isKindOfClass:[NSDictionary class]] || !unitVal) { + if (error) *error = parseError(@"timestamp_truncate requires timestamp and unit"); + return nil; + } + FIRExprBridge *timestampExpr = [self parseExpression:timestampMap error:error]; + if (!timestampExpr) return nil; + FIRExprBridge *unitExpr = [[FIRConstantBridge alloc] init:unitVal]; + return FLTNewFunctionExprBridge(@"timestamp_trunc", @[ timestampExpr, unitExpr ]); + } + + // ------------------------------------------------------------------------- + // PipelineFilter (name "filter"): operator-based (and/or) or field-based + // ------------------------------------------------------------------------- + if ([name isEqualToString:@"filter"]) { + return [self parseFilterExpressionWithArgs:args error:error]; + } + + if (error) *error = parseError([NSString stringWithFormat:@"Unsupported expression: %@", name]); + return nil; +} + +- (FIRExprBridge *)rightExprFromValue:(id)value error:(NSError **)error { + if ([value isKindOfClass:[NSDictionary class]]) { + return [self parseExpression:(NSDictionary *)value error:error]; + } + return [[FIRConstantBridge alloc] init:value]; +} + +- (FIRExprBridge *)parseFilterExpressionWithArgs:(NSDictionary *)args error:(NSError **)error { + // Operator-based: and/or with expressions array (from PipelineFilter.and / .or) + NSString *operator= args[@"operator"]; + NSArray *exprMaps = args[@"expressions"]; + if ([operator isKindOfClass:[NSString class]] && [exprMaps isKindOfClass:[NSArray class]]) { + if (exprMaps.count == 0) { + if (error) *error = parseError(@"filter with operator requires at least one expression"); + return nil; + } + if (exprMaps.count == 1) { + id em = exprMaps[0]; + if (![em isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"filter expressions must be maps"); + return nil; + } + return [self parseBooleanExpression:(NSDictionary *)em error:error]; + } + NSMutableArray *all = [NSMutableArray array]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *e = [self parseBooleanExpression:(NSDictionary *)em error:error]; + if (!e) return nil; + [all addObject:e]; + } + if (all.count == 0) return nil; + return FLTNewFunctionExprBridge(operator, all); + } + + // Field-based: field + isEqualTo, isGreaterThan, etc. + NSString *fieldName = args[@"field"]; + if (![fieldName isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"filter requires operator+expressions or field"); + return nil; + } + FIRExprBridge *fieldExpr = [[FIRFieldBridge alloc] initWithName:fieldName]; + + static NSArray *filterComparisonKeys = nil; + static dispatch_once_t filterOnce; + dispatch_once(&filterOnce, ^{ + filterComparisonKeys = @[ + @"isEqualTo", @"isNotEqualTo", @"isGreaterThan", @"isGreaterThanOrEqualTo", @"isLessThan", + @"isLessThanOrEqualTo", @"arrayContains", @"arrayContainsAny", @"whereIn", @"whereNotIn", + @"isNull", @"isNotNull" + ]; + }); + for (NSString *key in filterComparisonKeys) { + id value = args[key]; + if (value == nil) continue; + + if ([key isEqualToString:@"isEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isNotEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"not_equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isGreaterThan"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"greater_than", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isGreaterThanOrEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"greater_than_or_equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isLessThan"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"less_than", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isLessThanOrEqualTo"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"less_than_or_equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"arrayContains"]) { + FIRExprBridge *right = [self rightExprFromValue:value error:error]; + if (!right) return nil; + return FLTNewFunctionExprBridge(@"array_contains", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"arrayContainsAny"] || [key isEqualToString:@"whereIn"]) { + NSArray *valuesList = [value isKindOfClass:[NSArray class]] ? value : @[]; + NSMutableArray *valueExprs = [NSMutableArray array]; + for (id v in valuesList) { + FIRExprBridge *ve = [self rightExprFromValue:v error:error]; + if (!ve) return nil; + [valueExprs addObject:ve]; + } + if (valueExprs.count == 0) { + if (error) *error = parseError(@"arrayContainsAny/whereIn requires non-empty list"); + return nil; + } + // SDK expects (value, array) not (value, v1, v2, ...); wrap in "array" expr. + FIRExprBridge *valuesArrayExpr = FLTNewFunctionExprBridge(@"array", valueExprs); + return FLTNewFunctionExprBridge(@"equal_any", @[ fieldExpr, valuesArrayExpr ]); + } + if ([key isEqualToString:@"whereNotIn"]) { + NSArray *valuesList = [value isKindOfClass:[NSArray class]] ? value : @[]; + NSMutableArray *valueExprs = [NSMutableArray array]; + for (id v in valuesList) { + FIRExprBridge *ve = [self rightExprFromValue:v error:error]; + if (!ve) return nil; + [valueExprs addObject:ve]; + } + if (valueExprs.count == 0) { + if (error) *error = parseError(@"whereNotIn requires non-empty list"); + return nil; + } + // SDK expects (value, array) not (value, v1, v2, ...); wrap in "array" expr. + FIRExprBridge *valuesArrayExpr = FLTNewFunctionExprBridge(@"array", valueExprs); + return FLTNewFunctionExprBridge(@"not_equal_any", @[ fieldExpr, valuesArrayExpr ]); + } + if ([key isEqualToString:@"isNull"]) { + FIRExprBridge *right = [[FIRConstantBridge alloc] init:[NSNull null]]; + return FLTNewFunctionExprBridge(@"equal", @[ fieldExpr, right ]); + } + if ([key isEqualToString:@"isNotNull"]) { + FIRExprBridge *right = [[FIRConstantBridge alloc] init:[NSNull null]]; + return FLTNewFunctionExprBridge(@"not_equal", @[ fieldExpr, right ]); + } + } + + if (error) + *error = + parseError(@"filter requires at least one comparison (isEqualTo, isGreaterThan, etc.)"); + return nil; +} + +- (FIRExprBridge *)parseBooleanExpression:(NSDictionary *)map + error:(NSError **)error { + return [self parseExpression:map error:error]; +} + +@end + +@implementation FLTPipelineParser + +/// Returns the key (alias or field name) for an expression map in select/distinct stages. +/// Uses args.alias if present; otherwise for "field" expressions uses args.field. Returns nil if +/// no key can be determined (caller should error). ++ (NSString *)keyForExpressionMap:(NSDictionary *)em error:(NSError **)error { + NSString *alias = [em valueForKeyPath:@"args.alias"]; + if ([alias isKindOfClass:[NSString class]] && alias.length > 0) { + return alias; + } + if ([em[@"name"] isEqualToString:@"field"]) { + NSString *field = [em valueForKeyPath:@"args.field"]; + if ([field isKindOfClass:[NSString class]]) return field; + if (error) *error = parseError(@"field expression must have args.field"); + return nil; + } + if (error) *error = parseError(@"expression must have alias or be a field reference"); + return nil; +} + ++ (NSDictionary *) + parseSearchFieldsWithExpressionMaps:(NSArray *> *)exprMaps + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + NSError *parseErr = nil; + + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + + NSString *key = [self keyForExpressionMap:em error:error]; + if (![key isKindOfClass:[NSString class]] || key.length == 0) return nil; + fields[key] = expr; + } + + return fields; +} + ++ (FIRStageBridge *)parseSearchStageWithArgs:(NSDictionary *)args + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSString *queryType = args[@"query_type"]; + id query = args[@"query"]; + NSMutableDictionary *options = [NSMutableDictionary dictionary]; + NSError *parseErr = nil; + + if ([queryType isEqualToString:@"string"]) { + if (![query isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"search query_type 'string' requires string query"); + return nil; + } + FIRExprBridge *queryExpr = [[FIRConstantBridge alloc] init:query]; + options[@"query"] = FLTNewFunctionExprBridge(@"document_matches", @[ queryExpr ]); + } else if ([queryType isEqualToString:@"expression"]) { + if (![query isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"search query_type 'expression' requires expression query"); + return nil; + } + FIRExprBridge *queryExpr = [exprParser parseBooleanExpression:query error:&parseErr]; + if (!queryExpr) { + if (error) *error = parseErr; + return nil; + } + options[@"query"] = queryExpr; + } else { + if (error) *error = parseError(@"search requires query_type to be 'string' or 'expression'"); + return nil; + } + + NSNumber *limit = [args[@"limit"] isKindOfClass:[NSNumber class]] ? args[@"limit"] : nil; + if (limit) options[@"limit"] = [[FIRConstantBridge alloc] init:limit]; + + NSNumber *offset = [args[@"offset"] isKindOfClass:[NSNumber class]] ? args[@"offset"] : nil; + if (offset) options[@"offset"] = [[FIRConstantBridge alloc] init:offset]; + + NSNumber *retrievalDepth = + [args[@"retrieval_depth"] isKindOfClass:[NSNumber class]] ? args[@"retrieval_depth"] : nil; + if (retrievalDepth) { + options[@"retrieval_depth"] = [[FIRConstantBridge alloc] init:retrievalDepth]; + } + + NSString *languageCode = + [args[@"language_code"] isKindOfClass:[NSString class]] ? args[@"language_code"] : nil; + if (languageCode) { + options[@"language_code"] = [[FIRConstantBridge alloc] init:languageCode]; + } + + NSMutableArray *sort = [NSMutableArray array]; + NSArray *orderingMaps = args[@"sort"]; + if ([orderingMaps isKindOfClass:[NSArray class]]) { + for (id om in orderingMaps) { + if (![om isKindOfClass:[NSDictionary class]]) continue; + id exprMap = ((NSDictionary *)om)[@"expression"]; + NSString *dir = ((NSDictionary *)om)[@"order_direction"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *direction = [dir isEqualToString:@"asc"] ? @"ascending" : @"descending"; + [sort addObject:[[FIROrderingBridge alloc] initWithExpr:expr Direction:direction]]; + } + } + + NSDictionary *addFields = @{}; + NSArray *addFieldMaps = args[@"add_fields"]; + if ([addFieldMaps isKindOfClass:[NSArray class]] && addFieldMaps.count > 0) { + addFields = [self parseSearchFieldsWithExpressionMaps:addFieldMaps + exprParser:exprParser + error:error]; + if (!addFields) return nil; + } + + return [[FIRSearchStageBridge alloc] initWithOptions:options + addFields:addFields + select:@{} + sort:sort]; +} + ++ (NSArray *) + parseStagesWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + error:(NSError **)error { + FLTPipelineExpressionParser *exprParser = + [[FLTPipelineExpressionParser alloc] initWithFirestore:firestore]; + NSMutableArray *stageBridges = [NSMutableArray array]; + NSError *parseErr = nil; + + for (NSUInteger i = 0; i < stages.count; i++) { + NSDictionary *stageMap = stages[i]; + if (![stageMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"Stage must be a map"); + return nil; + } + NSString *stageName = stageMap[@"stage"]; + if (![stageName isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"Stage must have a 'stage' field"); + return nil; + } + id argsObj = stageMap[@"args"]; + NSDictionary *args = [argsObj isKindOfClass:[NSDictionary class]] ? argsObj : @{}; + NSArray *argsArray = [argsObj isKindOfClass:[NSArray class]] ? argsObj : nil; + + FIRStageBridge *stage = nil; + + if (i == 0) { + if ([stageName isEqualToString:@"collection"]) { + NSString *path = args[@"path"]; + if (!path) { + if (error) *error = parseError(@"collection requires 'path'"); + return nil; + } + FIRCollectionReference *ref = [firestore collectionWithPath:path]; + stage = [[FIRCollectionSourceStageBridge alloc] initWithRef:ref + firestore:firestore + forceIndex:nil]; + } else if ([stageName isEqualToString:@"collection_group"]) { + NSString *path = args[@"path"]; + if (!path) { + if (error) *error = parseError(@"collection_group requires 'path'"); + return nil; + } + stage = [[FIRCollectionGroupSourceStageBridge alloc] initWithCollectionId:path + forceIndex:nil]; + } else if ([stageName isEqualToString:@"database"]) { + stage = [[FIRDatabaseSourceStageBridge alloc] init]; + } else if ([stageName isEqualToString:@"documents"]) { + NSArray *docMaps = argsArray; + if (!docMaps || docMaps.count == 0) { + if (error) *error = parseError(@"documents requires array of document refs"); + return nil; + } + NSMutableArray *refs = [NSMutableArray array]; + for (id docMap in docMaps) { + if (![docMap isKindOfClass:[NSDictionary class]]) continue; + NSString *path = ((NSDictionary *)docMap)[@"path"]; + if (path) [refs addObject:[firestore documentWithPath:path]]; + } + stage = [[FIRDocumentsSourceStageBridge alloc] initWithDocuments:refs firestore:firestore]; + } else { + if (error) + *error = parseError( + [NSString stringWithFormat:@"First stage must be collection, collection_group, " + @"documents, or database. Got: %@", + stageName]); + return nil; + } + } else { + if ([stageName isEqualToString:@"where"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"where requires expression"); + return nil; + } + FIRExprBridge *expr = [exprParser parseBooleanExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + stage = [[FIRWhereStageBridge alloc] initWithExpr:expr]; + } else if ([stageName isEqualToString:@"search"]) { + stage = [self parseSearchStageWithArgs:args exprParser:exprParser error:error]; + if (!stage) return nil; + } else if ([stageName isEqualToString:@"limit"]) { + NSNumber *limit = args[@"limit"]; + if (![limit isKindOfClass:[NSNumber class]]) { + if (error) *error = parseError(@"limit requires numeric limit"); + return nil; + } + stage = [[FIRLimitStageBridge alloc] initWithLimit:limit.intValue]; + } else if ([stageName isEqualToString:@"offset"]) { + NSNumber *offset = args[@"offset"]; + if (![offset isKindOfClass:[NSNumber class]]) { + if (error) *error = parseError(@"offset requires numeric offset"); + return nil; + } + stage = [[FIROffsetStageBridge alloc] initWithOffset:offset.intValue]; + } else if ([stageName isEqualToString:@"sort"]) { + NSArray *orderingMaps = args[@"orderings"]; + if (![orderingMaps isKindOfClass:[NSArray class]] || orderingMaps.count == 0) { + if (error) *error = parseError(@"sort requires at least one ordering"); + return nil; + } + NSMutableArray *orderings = [NSMutableArray array]; + for (id om in orderingMaps) { + if (![om isKindOfClass:[NSDictionary class]]) continue; + id exprMap = ((NSDictionary *)om)[@"expression"]; + NSString *dir = ((NSDictionary *)om)[@"order_direction"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *direction = [dir isEqualToString:@"asc"] ? @"ascending" : @"descending"; + FIROrderingBridge *ordering = [[FIROrderingBridge alloc] initWithExpr:expr + Direction:direction]; + [orderings addObject:ordering]; + } + if (orderings.count == 0) { + if (error) *error = parseError(@"sort requires at least one ordering"); + return nil; + } + stage = [[FIRSorStageBridge alloc] initWithOrderings:orderings]; + } else if ([stageName isEqualToString:@"select"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"select requires at least one expression"); + return nil; + } + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *key = [self keyForExpressionMap:em error:error]; + if (!key) return nil; + fields[key] = expr; + } + stage = [[FIRSelectStageBridge alloc] initWithSelections:fields]; + } else if ([stageName isEqualToString:@"add_fields"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"add_fields requires at least one expression"); + return nil; + } + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *alias = [em valueForKeyPath:@"args.alias"]; + if (!alias) { + if (error) *error = parseError(@"add_fields expressions must have alias"); + return nil; + } + fields[alias] = expr; + } + stage = [[FIRAddFieldsStageBridge alloc] initWithFields:fields]; + } else if ([stageName isEqualToString:@"remove_fields"]) { + NSArray *paths = args[@"field_paths"]; + if (![paths isKindOfClass:[NSArray class]] || paths.count == 0) { + if (error) *error = parseError(@"remove_fields requires field_paths"); + return nil; + } + stage = [[FIRRemoveFieldsStageBridge alloc] initWithFields:paths]; + } else if ([stageName isEqualToString:@"distinct"]) { + NSArray *exprMaps = args[@"expressions"]; + if (![exprMaps isKindOfClass:[NSArray class]] || exprMaps.count == 0) { + if (error) *error = parseError(@"distinct requires at least one expression"); + return nil; + } + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + for (id em in exprMaps) { + if (![em isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:em error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + NSString *key = [self keyForExpressionMap:em error:error]; + if (!key) return nil; + fields[key] = expr; + } + stage = [[FIRDistinctStageBridge alloc] initWithGroups:fields]; + } else if ([stageName isEqualToString:@"replace_with"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"replace_with requires expression"); + return nil; + } + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!expr) { + if (error) *error = parseErr; + return nil; + } + stage = [[FIRReplaceWithStageBridge alloc] initWithExpr:expr]; + } else if ([stageName isEqualToString:@"union"]) { + NSArray *nestedStages = args[@"pipeline"]; + if (![nestedStages isKindOfClass:[NSArray class]] || nestedStages.count == 0) { + if (error) *error = parseError(@"union requires non-empty pipeline"); + return nil; + } + id otherPipeline = [self buildPipelineWithFirestore:firestore + stages:nestedStages + error:&parseErr]; + if (!otherPipeline) { + if (error) *error = parseErr; + return nil; + } + stage = [[FIRUnionStageBridge alloc] initWithOther:otherPipeline]; + } else if ([stageName isEqualToString:@"sample"]) { + NSString *type = args[@"type"]; + id val = args[@"value"]; + if ([type isEqualToString:@"percentage"]) { + double v = [val isKindOfClass:[NSNumber class]] ? [(NSNumber *)val doubleValue] : 0; + stage = [[FIRSampleStageBridge alloc] initWithPercentage:v]; + } else { + int v = [val isKindOfClass:[NSNumber class]] ? [(NSNumber *)val intValue] : 0; + stage = [[FIRSampleStageBridge alloc] initWithCount:v]; + } + } else if ([stageName isEqualToString:@"aggregate"]) { + stage = [self parseAggregateStageWithArgs:args exprParser:exprParser error:error]; + } else if ([stageName isEqualToString:@"aggregate_with_options"]) { + stage = [self parseAggregateStageWithOptionsArgs:args exprParser:exprParser error:error]; + } else if ([stageName isEqualToString:@"unnest"]) { + id exprMap = args[@"expression"]; + if (![exprMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"unnest requires expression"); + return nil; + } + FIRExprBridge *fieldExpr = nil; + FIRExprBridge *aliasExpr = nil; + NSDictionary *exprDict = (NSDictionary *)exprMap; + NSString *aliasStr = nil; + if ([exprDict[@"name"] isEqualToString:@"alias"]) { + NSDictionary *aliasArgs = exprDict[@"args"]; + if ([aliasArgs isKindOfClass:[NSDictionary class]] && aliasArgs[@"expression"]) { + fieldExpr = [exprParser parseExpression:aliasArgs[@"expression"] error:&parseErr]; + if (!fieldExpr) { + if (error) *error = parseErr; + return nil; + } + aliasStr = + [aliasArgs[@"alias"] isKindOfClass:[NSString class]] ? aliasArgs[@"alias"] : nil; + } + } + if (!fieldExpr) { + fieldExpr = [exprParser parseExpression:exprMap error:&parseErr]; + if (!fieldExpr) { + if (error) *error = parseErr; + return nil; + } + if (!aliasStr && [exprDict[@"name"] isEqualToString:@"field"]) { + NSDictionary *fieldArgs = exprDict[@"args"]; + aliasStr = + [fieldArgs[@"field"] isKindOfClass:[NSString class]] ? fieldArgs[@"field"] : @"_"; + } + } + if (!aliasStr) aliasStr = @"_"; + aliasExpr = [[FIRFieldBridge alloc] initWithName:aliasStr]; + NSString *indexFieldStr = + [args[@"index_field"] isKindOfClass:[NSString class]] ? args[@"index_field"] : nil; + FIRExprBridge *indexFieldExpr = + (indexFieldStr.length > 0) ? [[FIRFieldBridge alloc] initWithName:indexFieldStr] : nil; + stage = [[FIRUnnestStageBridge alloc] initWithField:fieldExpr + alias:aliasExpr + indexField:indexFieldExpr]; + } else if ([stageName isEqualToString:@"find_nearest"]) { + NSString *vectorFieldName = args[@"vector_field"]; + NSArray *vectorValueArray = args[@"vector_value"]; + NSString *distanceMeasure = args[@"distance_measure"]; + NSNumber *limit = [args[@"limit"] isKindOfClass:[NSNumber class]] ? args[@"limit"] : nil; + NSString *distanceField = [args[@"distance_field"] isKindOfClass:[NSString class]] + ? args[@"distance_field"] + : nil; + if (![vectorFieldName isKindOfClass:[NSString class]] || vectorFieldName.length == 0) { + if (error) *error = parseError(@"find_nearest requires 'vector_field'"); + return nil; + } + if (![vectorValueArray isKindOfClass:[NSArray class]] || vectorValueArray.count == 0) { + if (error) *error = parseError(@"find_nearest requires non-empty 'vector_value'"); + return nil; + } + if (![distanceMeasure isKindOfClass:[NSString class]] || distanceMeasure.length == 0) { + if (error) *error = parseError(@"find_nearest requires 'distance_measure'"); + return nil; + } + FIRFieldBridge *embeddingField = [[FIRFieldBridge alloc] initWithName:vectorFieldName]; + NSMutableArray *numbers = + [NSMutableArray arrayWithCapacity:vectorValueArray.count]; + for (id v in vectorValueArray) { + if ([v isKindOfClass:[NSNumber class]]) { + [numbers addObject:(NSNumber *)v]; + } + } + if (numbers.count != (NSUInteger)vectorValueArray.count) { + if (error) *error = parseError(@"find_nearest vector_value must be an array of numbers"); + return nil; + } + FIRVectorValue *queryVector = [[FIRVectorValue alloc] initWithArray:numbers]; + stage = [[FIRFindNearestStageBridge alloc] initWithField:embeddingField + vectorValue:queryVector + distanceMeasure:distanceMeasure + limit:limit + distanceField:distanceField]; + } else { + if (error) + *error = parseError([NSString stringWithFormat:@"Unknown pipeline stage: %@", stageName]); + return nil; + } + } + + if (stage) [stageBridges addObject:stage]; + } + + if (stageBridges.count == 0) { + if (error && !*error) *error = parseError(@"No valid stages"); + return nil; + } + + return stageBridges; +} + ++ (FIRAggregateFunctionBridge *)aggregateFunctionFromMap:(NSDictionary *)funcMap + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSString *name = funcMap[@"name"]; + if (![name isKindOfClass:[NSString class]]) { + if (error) *error = parseError(@"Aggregate function must have a 'name'"); + return nil; + } + // Map Dart aggregate function names to iOS SDK names (count_all -> count with no args; minimum -> + // min; maximum -> max) + NSString *iosName = name; + if ([name isEqualToString:@"count_all"]) { + iosName = @"count"; + } else if ([name isEqualToString:@"minimum"]) { + iosName = @"min"; + } else if ([name isEqualToString:@"maximum"]) { + iosName = @"max"; + } + NSDictionary *argsDict = funcMap[@"args"]; + NSMutableArray *argsArray = [NSMutableArray array]; + if ([argsDict isKindOfClass:[NSDictionary class]]) { + id exprMap = argsDict[@"expression"]; + if ([exprMap isKindOfClass:[NSDictionary class]]) { + FIRExprBridge *expr = [exprParser parseExpression:exprMap error:error]; + if (!expr) return nil; + [argsArray addObject:expr]; + } + } + return [[FIRAggregateFunctionBridge alloc] initWithName:iosName Args:argsArray]; +} + ++ (FIRStageBridge *)parseAggregateStageWithArgs:(NSDictionary *)args + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSArray *accumulatorMaps = args[@"aggregate_functions"]; + if (![accumulatorMaps isKindOfClass:[NSArray class]] || accumulatorMaps.count == 0) { + if (error) *error = parseError(@"aggregate requires aggregate_functions"); + return nil; + } + return [self parseAggregateStageWithAccumulatorMaps:accumulatorMaps + groupMaps:nil + exprParser:exprParser + error:error]; +} + ++ (FIRStageBridge *)parseAggregateStageWithOptionsArgs:(NSDictionary *)args + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSDictionary *stageMap = args[@"aggregate_stage"]; + if (![stageMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"aggregate_with_options requires aggregate_stage"); + return nil; + } + NSArray *accumulatorMaps = stageMap[@"accumulators"]; + if (![accumulatorMaps isKindOfClass:[NSArray class]] || accumulatorMaps.count == 0) { + accumulatorMaps = stageMap[@"aggregate_functions"]; + } + if (![accumulatorMaps isKindOfClass:[NSArray class]] || accumulatorMaps.count == 0) { + if (error) *error = parseError(@"aggregate_stage requires accumulators or aggregate_functions"); + return nil; + } + NSArray *groupMaps = stageMap[@"groups"]; + return [self parseAggregateStageWithAccumulatorMaps:accumulatorMaps + groupMaps:groupMaps + exprParser:exprParser + error:error]; +} + ++ (FIRStageBridge *)parseAggregateStageWithAccumulatorMaps:(NSArray *)accumulatorMaps + groupMaps:(nullable NSArray *)groupMaps + exprParser:(FLTPipelineExpressionParser *)exprParser + error:(NSError **)error { + NSError *parseErr = nil; + NSMutableDictionary *accumulators = + [NSMutableDictionary dictionary]; + for (id accMap in accumulatorMaps) { + if (![accMap isKindOfClass:[NSDictionary class]]) continue; + NSString *alias = nil; + NSDictionary *funcMap = nil; + if ([accMap[@"name"] isEqualToString:@"alias"]) { + NSDictionary *accArgs = accMap[@"args"]; + if (![accArgs isKindOfClass:[NSDictionary class]]) continue; + alias = accArgs[@"alias"]; + funcMap = accArgs[@"aggregate_function"]; + } + if (![alias isKindOfClass:[NSString class]] || ![funcMap isKindOfClass:[NSDictionary class]]) { + if (error) *error = parseError(@"Each accumulator must have alias and aggregate_function"); + return nil; + } + FIRAggregateFunctionBridge *func = [self aggregateFunctionFromMap:funcMap + exprParser:exprParser + error:&parseErr]; + if (!func) { + if (error) *error = parseErr; + return nil; + } + accumulators[alias] = func; + } + if (accumulators.count == 0) { + if (error) *error = parseError(@"aggregate requires at least one valid accumulator"); + return nil; + } + + NSMutableDictionary *groups = [NSMutableDictionary dictionary]; + if ([groupMaps isKindOfClass:[NSArray class]] && groupMaps.count > 0) { + for (NSUInteger g = 0; g < groupMaps.count; g++) { + id gm = groupMaps[g]; + if (![gm isKindOfClass:[NSDictionary class]]) continue; + FIRExprBridge *expr = [exprParser parseExpression:gm error:&parseErr]; + if (!expr) continue; + NSError *groupKeyError = nil; + NSString *groupKey = [self keyForExpressionMap:gm error:&groupKeyError]; + if (![groupKey isKindOfClass:[NSString class]] || groupKey.length == 0) { + if (error) + *error = + groupKeyError + ?: parseError( + @"aggregate group expression must be a field reference or have an alias"); + return nil; + } + groups[groupKey] = expr; + } + } + + return [[FIRAggregateStageBridge alloc] initWithAccumulators:accumulators groups:groups]; +} + ++ (void)executePipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(void (^)(id _Nullable snapshot, + NSError *_Nullable error))completion { + if (!stages || stages.count == 0) { + completion(nil, parseError(@"Pipeline requires at least one stage")); + return; + } + + NSError *parseErr = nil; + NSArray *stageBridges = [self parseStagesWithFirestore:firestore + stages:stages + error:&parseErr]; + if (!stageBridges) { + completion(nil, parseErr); + return; + } + + FIRPipelineBridge *pipeline = [[FIRPipelineBridge alloc] initWithStages:stageBridges + db:firestore]; + [pipeline executeWithCompletion:^(id snapshot, NSError *execError) { + if (execError) { + completion(nil, execError); + return; + } + completion(snapshot, nil); + }]; +} + ++ (id)buildPipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + error:(NSError **)error { + NSArray *stageBridges = [self parseStagesWithFirestore:firestore + stages:stages + error:error]; + if (!stageBridges) return nil; + return [[FIRPipelineBridge alloc] initWithStages:stageBridges db:firestore]; +} + +@end + +#else + +@implementation FLTPipelineParser + ++ (void)executePipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(void (^)(id _Nullable snapshot, + NSError *_Nullable error))completion { + completion(nil, pipelineUnavailableError()); +} + +@end + +#endif diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m index bb80a2d6e69e..a821cc35e94f 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTQuerySnapshotStreamHandler.m @@ -16,6 +16,7 @@ @interface FLTQuerySnapshotStreamHandler () @property(readwrite, strong) id listenerRegistration; +@property(nonatomic) dispatch_queue_t snapshotQueue; @end @implementation FLTQuerySnapshotStreamHandler @@ -32,6 +33,8 @@ - (instancetype)initWithFirestore:(FIRFirestore *)firestore _includeMetadataChanges = includeMetadataChanges; _serverTimestampBehavior = serverTimestampBehavior; _source = source; + _snapshotQueue = dispatch_queue_create("io.flutter.plugins.firebase.firestore.query_snapshot", + DISPATCH_QUEUE_SERIAL); } return self; } @@ -64,33 +67,17 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments andOptionalNSError:error]); }); } else { - dispatch_async(dispatch_get_main_queue(), ^{ - NSMutableArray *toListResult = [[NSMutableArray alloc] initWithCapacity:3]; - - NSMutableArray *documents = - [[NSMutableArray alloc] initWithCapacity:snapshot.documents.count]; - NSMutableArray *documentChanges = - [[NSMutableArray alloc] initWithCapacity:snapshot.documentChanges.count]; - - for (FIRDocumentSnapshot *documentSnapshot in snapshot.documents) { - [documents addObject:[[FirestorePigeonParser - toPigeonDocumentSnapshot:documentSnapshot - serverTimestampBehavior:self.serverTimestampBehavior] toList]]; - } - - for (FIRDocumentChange *documentChange in snapshot.documentChanges) { - [documentChanges - addObject:[[FirestorePigeonParser toPigeonDocumentChange:documentChange - serverTimestampBehavior:self.serverTimestampBehavior] - toList]]; - } - - [toListResult addObject:documents]; - [toListResult addObject:documentChanges]; - [toListResult - addObject:[[FirestorePigeonParser toPigeonSnapshotMetadata:snapshot.metadata] toList]]; - - events(toListResult); + dispatch_async(self.snapshotQueue, ^{ + // Emit the Pigeon object directly; the Pigeon-aware codec serializes nested + // `InternalDocumentSnapshot` / `InternalDocumentChange` / `InternalSnapshotMetadata` + // with their proper type codes. Pigeon 26 no longer flattens nested types + // via `toList`. + InternalQuerySnapshot *pigeonSnapshot = + [FirestorePigeonParser toPigeonQuerySnapshot:snapshot + serverTimestampBehavior:self.serverTimestampBehavior]; + dispatch_async(dispatch_get_main_queue(), ^{ + events(pigeonSnapshot); + }); }); } }; diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m index 29252ca4821c..8cd347b0b269 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTTransactionStreamHandler.m @@ -17,8 +17,8 @@ @interface FLTTransactionStreamHandler () @property(nonatomic, copy, nonnull) void (^started)(FIRTransaction *); @property(nonatomic, copy, nonnull) void (^ended)(void); @property(strong) dispatch_semaphore_t semaphore; -@property PigeonTransactionResult resultType; -@property NSArray *commands; +@property InternalTransactionResult resultType; +@property NSArray *commands; @end @@ -28,8 +28,8 @@ @implementation FLTTransactionStreamHandler { - (instancetype)initWithId:(NSString *)transactionId firestore:(FIRFirestore *)firestore - timeout:(nonnull NSNumber *)timeout - maxAttempts:(nonnull NSNumber *)maxAttempts + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts started:(void (^)(FIRTransaction *))startedListener ended:(void (^)(void))endedListener { self = [super init]; @@ -60,8 +60,7 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments }); long timedOut = dispatch_semaphore_wait( - strongSelf.semaphore, - dispatch_time(DISPATCH_TIME_NOW, [self.timeout integerValue] * NSEC_PER_MSEC)); + strongSelf.semaphore, dispatch_time(DISPATCH_TIME_NOW, self.timeout * NSEC_PER_MSEC)); if (timedOut) { NSArray *codeAndMessage = [FLTFirebaseFirestoreUtils @@ -80,24 +79,24 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments }); } - if (self.resultType == PigeonTransactionResultFailure) { + if (self.resultType == InternalTransactionResultFailure) { // Do nothing - already handled in Dart land. return nil; } - for (PigeonTransactionCommand *command in self.commands) { - PigeonTransactionType commandType = command.type; + for (InternalTransactionCommand *command in self.commands) { + InternalTransactionType commandType = command.type; NSString *documentPath = command.path; FIRDocumentReference *reference = [self.firestore documentWithPath:documentPath]; switch (commandType) { - case PigeonTransactionTypeDeleteType: + case InternalTransactionTypeDeleteType: [transaction deleteDocument:reference]; break; - case PigeonTransactionTypeUpdate: + case InternalTransactionTypeUpdate: [transaction updateData:command.data forDocument:reference]; break; - case PigeonTransactionTypeSet: + case InternalTransactionTypeSet: if ([command.option.merge isEqual:@YES]) { [transaction setData:command.data forDocument:reference merge:YES]; } else if (command.option.mergeFields) { @@ -142,7 +141,7 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments strongSelf.ended(); }; FIRTransactionOptions *options = [[FIRTransactionOptions alloc] init]; - options.maxAttempts = _maxAttempts.integerValue; + options.maxAttempts = _maxAttempts; [_firestore runTransactionWithOptions:options block:transactionRunBlock @@ -157,8 +156,8 @@ - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { return nil; } -- (void)receiveTransactionResponse:(PigeonTransactionResult)resultType - commands:(NSArray *)commands { +- (void)receiveTransactionResponse:(InternalTransactionResult)resultType + commands:(NSArray *)commands { self.resultType = resultType; self.commands = commands; diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m index 55ad066b2f16..f9594c6e943f 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "FirestoreMessages.g.h" @@ -9,14 +9,115 @@ #import "FLTFirebaseFirestoreWriter.h" #if TARGET_OS_OSX -#import +@import FlutterMacOS; #else -#import +@import Flutter; #endif -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. -#endif +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return + [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} + +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} + +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} /// An enumeration of document change types. @implementation DocumentChangeTypeBox @@ -89,8 +190,8 @@ - (instancetype)initWithValue:(PersistenceCacheIndexManagerRequest)value { } @end -@implementation PigeonTransactionResultBox -- (instancetype)initWithValue:(PigeonTransactionResult)value { +@implementation InternalTransactionResultBox +- (instancetype)initWithValue:(InternalTransactionResult)value { self = [super init]; if (self) { _value = value; @@ -99,8 +200,8 @@ - (instancetype)initWithValue:(PigeonTransactionResult)value { } @end -@implementation PigeonTransactionTypeBox -- (instancetype)initWithValue:(PigeonTransactionType)value { +@implementation InternalTransactionTypeBox +- (instancetype)initWithValue:(InternalTransactionType)value { self = [super init]; if (self) { _value = value; @@ -119,104 +220,103 @@ - (instancetype)initWithValue:(AggregateType)value { } @end -static NSArray *wrapResult(id result, FlutterError *error) { - if (error) { - return @[ - error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] - ]; - } - return @[ result ?: [NSNull null] ]; -} -static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { - id result = array[key]; - return (result == [NSNull null]) ? nil : result; -} - -@interface PigeonFirebaseSettings () -+ (PigeonFirebaseSettings *)fromList:(NSArray *)list; -+ (nullable PigeonFirebaseSettings *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalFirebaseSettings () ++ (InternalFirebaseSettings *)fromList:(NSArray *)list; ++ (nullable InternalFirebaseSettings *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end @interface FirestorePigeonFirebaseApp () -+ (FirestorePigeonFirebaseApp *)fromList:(NSArray *)list; -+ (nullable FirestorePigeonFirebaseApp *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; ++ (FirestorePigeonFirebaseApp *)fromList:(NSArray *)list; ++ (nullable FirestorePigeonFirebaseApp *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonSnapshotMetadata () -+ (PigeonSnapshotMetadata *)fromList:(NSArray *)list; -+ (nullable PigeonSnapshotMetadata *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalSnapshotMetadata () ++ (InternalSnapshotMetadata *)fromList:(NSArray *)list; ++ (nullable InternalSnapshotMetadata *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonDocumentSnapshot () -+ (PigeonDocumentSnapshot *)fromList:(NSArray *)list; -+ (nullable PigeonDocumentSnapshot *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalDocumentSnapshot () ++ (InternalDocumentSnapshot *)fromList:(NSArray *)list; ++ (nullable InternalDocumentSnapshot *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonDocumentChange () -+ (PigeonDocumentChange *)fromList:(NSArray *)list; -+ (nullable PigeonDocumentChange *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalDocumentChange () ++ (InternalDocumentChange *)fromList:(NSArray *)list; ++ (nullable InternalDocumentChange *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonQuerySnapshot () -+ (PigeonQuerySnapshot *)fromList:(NSArray *)list; -+ (nullable PigeonQuerySnapshot *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalQuerySnapshot () ++ (InternalQuerySnapshot *)fromList:(NSArray *)list; ++ (nullable InternalQuerySnapshot *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonGetOptions () -+ (PigeonGetOptions *)fromList:(NSArray *)list; -+ (nullable PigeonGetOptions *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalPipelineResult () ++ (InternalPipelineResult *)fromList:(NSArray *)list; ++ (nullable InternalPipelineResult *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonDocumentOption () -+ (PigeonDocumentOption *)fromList:(NSArray *)list; -+ (nullable PigeonDocumentOption *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalPipelineSnapshot () ++ (InternalPipelineSnapshot *)fromList:(NSArray *)list; ++ (nullable InternalPipelineSnapshot *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonTransactionCommand () -+ (PigeonTransactionCommand *)fromList:(NSArray *)list; -+ (nullable PigeonTransactionCommand *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalGetOptions () ++ (InternalGetOptions *)fromList:(NSArray *)list; ++ (nullable InternalGetOptions *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalDocumentOption () ++ (InternalDocumentOption *)fromList:(NSArray *)list; ++ (nullable InternalDocumentOption *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalTransactionCommand () ++ (InternalTransactionCommand *)fromList:(NSArray *)list; ++ (nullable InternalTransactionCommand *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end @interface DocumentReferenceRequest () -+ (DocumentReferenceRequest *)fromList:(NSArray *)list; -+ (nullable DocumentReferenceRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; ++ (DocumentReferenceRequest *)fromList:(NSArray *)list; ++ (nullable DocumentReferenceRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonQueryParameters () -+ (PigeonQueryParameters *)fromList:(NSArray *)list; -+ (nullable PigeonQueryParameters *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalQueryParameters () ++ (InternalQueryParameters *)fromList:(NSArray *)list; ++ (nullable InternalQueryParameters *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end @interface AggregateQuery () -+ (AggregateQuery *)fromList:(NSArray *)list; -+ (nullable AggregateQuery *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; ++ (AggregateQuery *)fromList:(NSArray *)list; ++ (nullable AggregateQuery *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end @interface AggregateQueryResponse () -+ (AggregateQueryResponse *)fromList:(NSArray *)list; -+ (nullable AggregateQueryResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; ++ (AggregateQueryResponse *)fromList:(NSArray *)list; ++ (nullable AggregateQueryResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@implementation PigeonFirebaseSettings +@implementation InternalFirebaseSettings + (instancetype)makeWithPersistenceEnabled:(nullable NSNumber *)persistenceEnabled host:(nullable NSString *)host sslEnabled:(nullable NSNumber *)sslEnabled cacheSizeBytes:(nullable NSNumber *)cacheSizeBytes - ignoreUndefinedProperties:(NSNumber *)ignoreUndefinedProperties { - PigeonFirebaseSettings *pigeonResult = [[PigeonFirebaseSettings alloc] init]; + ignoreUndefinedProperties:(BOOL)ignoreUndefinedProperties { + InternalFirebaseSettings *pigeonResult = [[InternalFirebaseSettings alloc] init]; pigeonResult.persistenceEnabled = persistenceEnabled; pigeonResult.host = host; pigeonResult.sslEnabled = sslEnabled; @@ -224,33 +324,56 @@ + (instancetype)makeWithPersistenceEnabled:(nullable NSNumber *)persistenceEnabl pigeonResult.ignoreUndefinedProperties = ignoreUndefinedProperties; return pigeonResult; } -+ (PigeonFirebaseSettings *)fromList:(NSArray *)list { - PigeonFirebaseSettings *pigeonResult = [[PigeonFirebaseSettings alloc] init]; ++ (InternalFirebaseSettings *)fromList:(NSArray *)list { + InternalFirebaseSettings *pigeonResult = [[InternalFirebaseSettings alloc] init]; pigeonResult.persistenceEnabled = GetNullableObjectAtIndex(list, 0); pigeonResult.host = GetNullableObjectAtIndex(list, 1); pigeonResult.sslEnabled = GetNullableObjectAtIndex(list, 2); pigeonResult.cacheSizeBytes = GetNullableObjectAtIndex(list, 3); - pigeonResult.ignoreUndefinedProperties = GetNullableObjectAtIndex(list, 4); - NSAssert(pigeonResult.ignoreUndefinedProperties != nil, @""); + pigeonResult.ignoreUndefinedProperties = [GetNullableObjectAtIndex(list, 4) boolValue]; return pigeonResult; } -+ (nullable PigeonFirebaseSettings *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonFirebaseSettings fromList:list] : nil; ++ (nullable InternalFirebaseSettings *)nullableFromList:(NSArray *)list { + return (list) ? [InternalFirebaseSettings fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.persistenceEnabled ?: [NSNull null]), - (self.host ?: [NSNull null]), - (self.sslEnabled ?: [NSNull null]), - (self.cacheSizeBytes ?: [NSNull null]), - (self.ignoreUndefinedProperties ?: [NSNull null]), + self.persistenceEnabled ?: [NSNull null], + self.host ?: [NSNull null], + self.sslEnabled ?: [NSNull null], + self.cacheSizeBytes ?: [NSNull null], + @(self.ignoreUndefinedProperties), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalFirebaseSettings *other = (InternalFirebaseSettings *)object; + return FLTPigeonDeepEquals(self.persistenceEnabled, other.persistenceEnabled) && + FLTPigeonDeepEquals(self.host, other.host) && + FLTPigeonDeepEquals(self.sslEnabled, other.sslEnabled) && + FLTPigeonDeepEquals(self.cacheSizeBytes, other.cacheSizeBytes) && + self.ignoreUndefinedProperties == other.ignoreUndefinedProperties; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.persistenceEnabled); + result = result * 31 + FLTPigeonDeepHash(self.host); + result = result * 31 + FLTPigeonDeepHash(self.sslEnabled); + result = result * 31 + FLTPigeonDeepHash(self.cacheSizeBytes); + result = result * 31 + @(self.ignoreUndefinedProperties).hash; + return result; +} @end @implementation FirestorePigeonFirebaseApp + (instancetype)makeWithAppName:(NSString *)appName - settings:(PigeonFirebaseSettings *)settings + settings:(InternalFirebaseSettings *)settings databaseURL:(NSString *)databaseURL { FirestorePigeonFirebaseApp *pigeonResult = [[FirestorePigeonFirebaseApp alloc] init]; pigeonResult.appName = appName; @@ -258,246 +381,482 @@ + (instancetype)makeWithAppName:(NSString *)appName pigeonResult.databaseURL = databaseURL; return pigeonResult; } -+ (FirestorePigeonFirebaseApp *)fromList:(NSArray *)list { ++ (FirestorePigeonFirebaseApp *)fromList:(NSArray *)list { FirestorePigeonFirebaseApp *pigeonResult = [[FirestorePigeonFirebaseApp alloc] init]; pigeonResult.appName = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.appName != nil, @""); - pigeonResult.settings = - [PigeonFirebaseSettings nullableFromList:(GetNullableObjectAtIndex(list, 1))]; - NSAssert(pigeonResult.settings != nil, @""); + pigeonResult.settings = GetNullableObjectAtIndex(list, 1); pigeonResult.databaseURL = GetNullableObjectAtIndex(list, 2); - NSAssert(pigeonResult.databaseURL != nil, @""); return pigeonResult; } -+ (nullable FirestorePigeonFirebaseApp *)nullableFromList:(NSArray *)list { ++ (nullable FirestorePigeonFirebaseApp *)nullableFromList:(NSArray *)list { return (list) ? [FirestorePigeonFirebaseApp fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.appName ?: [NSNull null]), - (self.settings ? [self.settings toList] : [NSNull null]), - (self.databaseURL ?: [NSNull null]), + self.appName ?: [NSNull null], + self.settings ?: [NSNull null], + self.databaseURL ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + FirestorePigeonFirebaseApp *other = (FirestorePigeonFirebaseApp *)object; + return FLTPigeonDeepEquals(self.appName, other.appName) && + FLTPigeonDeepEquals(self.settings, other.settings) && + FLTPigeonDeepEquals(self.databaseURL, other.databaseURL); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.appName); + result = result * 31 + FLTPigeonDeepHash(self.settings); + result = result * 31 + FLTPigeonDeepHash(self.databaseURL); + return result; +} @end -@implementation PigeonSnapshotMetadata -+ (instancetype)makeWithHasPendingWrites:(NSNumber *)hasPendingWrites - isFromCache:(NSNumber *)isFromCache { - PigeonSnapshotMetadata *pigeonResult = [[PigeonSnapshotMetadata alloc] init]; +@implementation InternalSnapshotMetadata ++ (instancetype)makeWithHasPendingWrites:(BOOL)hasPendingWrites isFromCache:(BOOL)isFromCache { + InternalSnapshotMetadata *pigeonResult = [[InternalSnapshotMetadata alloc] init]; pigeonResult.hasPendingWrites = hasPendingWrites; pigeonResult.isFromCache = isFromCache; return pigeonResult; } -+ (PigeonSnapshotMetadata *)fromList:(NSArray *)list { - PigeonSnapshotMetadata *pigeonResult = [[PigeonSnapshotMetadata alloc] init]; - pigeonResult.hasPendingWrites = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.hasPendingWrites != nil, @""); - pigeonResult.isFromCache = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.isFromCache != nil, @""); ++ (InternalSnapshotMetadata *)fromList:(NSArray *)list { + InternalSnapshotMetadata *pigeonResult = [[InternalSnapshotMetadata alloc] init]; + pigeonResult.hasPendingWrites = [GetNullableObjectAtIndex(list, 0) boolValue]; + pigeonResult.isFromCache = [GetNullableObjectAtIndex(list, 1) boolValue]; return pigeonResult; } -+ (nullable PigeonSnapshotMetadata *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonSnapshotMetadata fromList:list] : nil; ++ (nullable InternalSnapshotMetadata *)nullableFromList:(NSArray *)list { + return (list) ? [InternalSnapshotMetadata fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.hasPendingWrites ?: [NSNull null]), - (self.isFromCache ?: [NSNull null]), + @(self.hasPendingWrites), + @(self.isFromCache), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalSnapshotMetadata *other = (InternalSnapshotMetadata *)object; + return self.hasPendingWrites == other.hasPendingWrites && self.isFromCache == other.isFromCache; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.hasPendingWrites).hash; + result = result * 31 + @(self.isFromCache).hash; + return result; +} @end -@implementation PigeonDocumentSnapshot +@implementation InternalDocumentSnapshot + (instancetype)makeWithPath:(NSString *)path data:(nullable NSDictionary *)data - metadata:(PigeonSnapshotMetadata *)metadata { - PigeonDocumentSnapshot *pigeonResult = [[PigeonDocumentSnapshot alloc] init]; + metadata:(InternalSnapshotMetadata *)metadata { + InternalDocumentSnapshot *pigeonResult = [[InternalDocumentSnapshot alloc] init]; pigeonResult.path = path; pigeonResult.data = data; pigeonResult.metadata = metadata; return pigeonResult; } -+ (PigeonDocumentSnapshot *)fromList:(NSArray *)list { - PigeonDocumentSnapshot *pigeonResult = [[PigeonDocumentSnapshot alloc] init]; ++ (InternalDocumentSnapshot *)fromList:(NSArray *)list { + InternalDocumentSnapshot *pigeonResult = [[InternalDocumentSnapshot alloc] init]; pigeonResult.path = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.path != nil, @""); pigeonResult.data = GetNullableObjectAtIndex(list, 1); - pigeonResult.metadata = - [PigeonSnapshotMetadata nullableFromList:(GetNullableObjectAtIndex(list, 2))]; - NSAssert(pigeonResult.metadata != nil, @""); + pigeonResult.metadata = GetNullableObjectAtIndex(list, 2); return pigeonResult; } -+ (nullable PigeonDocumentSnapshot *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonDocumentSnapshot fromList:list] : nil; ++ (nullable InternalDocumentSnapshot *)nullableFromList:(NSArray *)list { + return (list) ? [InternalDocumentSnapshot fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.path ?: [NSNull null]), - (self.data ?: [NSNull null]), - (self.metadata ? [self.metadata toList] : [NSNull null]), + self.path ?: [NSNull null], + self.data ?: [NSNull null], + self.metadata ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalDocumentSnapshot *other = (InternalDocumentSnapshot *)object; + return FLTPigeonDeepEquals(self.path, other.path) && FLTPigeonDeepEquals(self.data, other.data) && + FLTPigeonDeepEquals(self.metadata, other.metadata); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.path); + result = result * 31 + FLTPigeonDeepHash(self.data); + result = result * 31 + FLTPigeonDeepHash(self.metadata); + return result; +} @end -@implementation PigeonDocumentChange +@implementation InternalDocumentChange + (instancetype)makeWithType:(DocumentChangeType)type - document:(PigeonDocumentSnapshot *)document - oldIndex:(NSNumber *)oldIndex - newIndex:(NSNumber *)newIndex { - PigeonDocumentChange *pigeonResult = [[PigeonDocumentChange alloc] init]; + document:(InternalDocumentSnapshot *)document + oldIndex:(NSInteger)oldIndex + newIndex:(NSInteger)newIndex { + InternalDocumentChange *pigeonResult = [[InternalDocumentChange alloc] init]; pigeonResult.type = type; pigeonResult.document = document; pigeonResult.oldIndex = oldIndex; - pigeonResult.index = newIndex; + pigeonResult.newIndex = newIndex; return pigeonResult; } -+ (PigeonDocumentChange *)fromList:(NSArray *)list { - PigeonDocumentChange *pigeonResult = [[PigeonDocumentChange alloc] init]; - pigeonResult.type = [GetNullableObjectAtIndex(list, 0) integerValue]; - pigeonResult.document = - [PigeonDocumentSnapshot nullableFromList:(GetNullableObjectAtIndex(list, 1))]; - NSAssert(pigeonResult.document != nil, @""); - pigeonResult.oldIndex = GetNullableObjectAtIndex(list, 2); - NSAssert(pigeonResult.oldIndex != nil, @""); - pigeonResult.index = GetNullableObjectAtIndex(list, 3); - NSAssert(pigeonResult.index != nil, @""); ++ (InternalDocumentChange *)fromList:(NSArray *)list { + InternalDocumentChange *pigeonResult = [[InternalDocumentChange alloc] init]; + DocumentChangeTypeBox *boxedDocumentChangeType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedDocumentChangeType.value; + pigeonResult.document = GetNullableObjectAtIndex(list, 1); + pigeonResult.oldIndex = [GetNullableObjectAtIndex(list, 2) integerValue]; + pigeonResult.newIndex = [GetNullableObjectAtIndex(list, 3) integerValue]; return pigeonResult; } -+ (nullable PigeonDocumentChange *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonDocumentChange fromList:list] : nil; ++ (nullable InternalDocumentChange *)nullableFromList:(NSArray *)list { + return (list) ? [InternalDocumentChange fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - @(self.type), - (self.document ? [self.document toList] : [NSNull null]), - (self.oldIndex ?: [NSNull null]), - (self.index ?: [NSNull null]), + [[DocumentChangeTypeBox alloc] initWithValue:self.type], + self.document ?: [NSNull null], + @(self.oldIndex), + @(self.newIndex), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalDocumentChange *other = (InternalDocumentChange *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.document, other.document) && + self.oldIndex == other.oldIndex && self.newIndex == other.newIndex; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.document); + result = result * 31 + @(self.oldIndex).hash; + result = result * 31 + @(self.newIndex).hash; + return result; +} @end -@implementation PigeonQuerySnapshot -+ (instancetype)makeWithDocuments:(NSArray *)documents - documentChanges:(NSArray *)documentChanges - metadata:(PigeonSnapshotMetadata *)metadata { - PigeonQuerySnapshot *pigeonResult = [[PigeonQuerySnapshot alloc] init]; +@implementation InternalQuerySnapshot ++ (instancetype)makeWithDocuments:(NSArray *)documents + documentChanges:(NSArray *)documentChanges + metadata:(InternalSnapshotMetadata *)metadata { + InternalQuerySnapshot *pigeonResult = [[InternalQuerySnapshot alloc] init]; pigeonResult.documents = documents; pigeonResult.documentChanges = documentChanges; pigeonResult.metadata = metadata; return pigeonResult; } -+ (PigeonQuerySnapshot *)fromList:(NSArray *)list { - PigeonQuerySnapshot *pigeonResult = [[PigeonQuerySnapshot alloc] init]; ++ (InternalQuerySnapshot *)fromList:(NSArray *)list { + InternalQuerySnapshot *pigeonResult = [[InternalQuerySnapshot alloc] init]; pigeonResult.documents = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.documents != nil, @""); pigeonResult.documentChanges = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.documentChanges != nil, @""); - pigeonResult.metadata = - [PigeonSnapshotMetadata nullableFromList:(GetNullableObjectAtIndex(list, 2))]; - NSAssert(pigeonResult.metadata != nil, @""); + pigeonResult.metadata = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable InternalQuerySnapshot *)nullableFromList:(NSArray *)list { + return (list) ? [InternalQuerySnapshot fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.documents ?: [NSNull null], + self.documentChanges ?: [NSNull null], + self.metadata ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalQuerySnapshot *other = (InternalQuerySnapshot *)object; + return FLTPigeonDeepEquals(self.documents, other.documents) && + FLTPigeonDeepEquals(self.documentChanges, other.documentChanges) && + FLTPigeonDeepEquals(self.metadata, other.metadata); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.documents); + result = result * 31 + FLTPigeonDeepHash(self.documentChanges); + result = result * 31 + FLTPigeonDeepHash(self.metadata); + return result; +} +@end + +@implementation InternalPipelineResult ++ (instancetype)makeWithDocumentPath:(nullable NSString *)documentPath + createTime:(nullable NSNumber *)createTime + updateTime:(nullable NSNumber *)updateTime + data:(nullable NSDictionary *)data { + InternalPipelineResult *pigeonResult = [[InternalPipelineResult alloc] init]; + pigeonResult.documentPath = documentPath; + pigeonResult.createTime = createTime; + pigeonResult.updateTime = updateTime; + pigeonResult.data = data; + return pigeonResult; +} ++ (InternalPipelineResult *)fromList:(NSArray *)list { + InternalPipelineResult *pigeonResult = [[InternalPipelineResult alloc] init]; + pigeonResult.documentPath = GetNullableObjectAtIndex(list, 0); + pigeonResult.createTime = GetNullableObjectAtIndex(list, 1); + pigeonResult.updateTime = GetNullableObjectAtIndex(list, 2); + pigeonResult.data = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable InternalPipelineResult *)nullableFromList:(NSArray *)list { + return (list) ? [InternalPipelineResult fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.documentPath ?: [NSNull null], + self.createTime ?: [NSNull null], + self.updateTime ?: [NSNull null], + self.data ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalPipelineResult *other = (InternalPipelineResult *)object; + return FLTPigeonDeepEquals(self.documentPath, other.documentPath) && + FLTPigeonDeepEquals(self.createTime, other.createTime) && + FLTPigeonDeepEquals(self.updateTime, other.updateTime) && + FLTPigeonDeepEquals(self.data, other.data); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.documentPath); + result = result * 31 + FLTPigeonDeepHash(self.createTime); + result = result * 31 + FLTPigeonDeepHash(self.updateTime); + result = result * 31 + FLTPigeonDeepHash(self.data); + return result; +} +@end + +@implementation InternalPipelineSnapshot ++ (instancetype)makeWithResults:(NSArray *)results + executionTime:(NSInteger)executionTime { + InternalPipelineSnapshot *pigeonResult = [[InternalPipelineSnapshot alloc] init]; + pigeonResult.results = results; + pigeonResult.executionTime = executionTime; + return pigeonResult; +} ++ (InternalPipelineSnapshot *)fromList:(NSArray *)list { + InternalPipelineSnapshot *pigeonResult = [[InternalPipelineSnapshot alloc] init]; + pigeonResult.results = GetNullableObjectAtIndex(list, 0); + pigeonResult.executionTime = [GetNullableObjectAtIndex(list, 1) integerValue]; return pigeonResult; } -+ (nullable PigeonQuerySnapshot *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonQuerySnapshot fromList:list] : nil; ++ (nullable InternalPipelineSnapshot *)nullableFromList:(NSArray *)list { + return (list) ? [InternalPipelineSnapshot fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.documents ?: [NSNull null]), - (self.documentChanges ?: [NSNull null]), - (self.metadata ? [self.metadata toList] : [NSNull null]), + self.results ?: [NSNull null], + @(self.executionTime), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalPipelineSnapshot *other = (InternalPipelineSnapshot *)object; + return FLTPigeonDeepEquals(self.results, other.results) && + self.executionTime == other.executionTime; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.results); + result = result * 31 + @(self.executionTime).hash; + return result; +} @end -@implementation PigeonGetOptions +@implementation InternalGetOptions + (instancetype)makeWithSource:(Source)source serverTimestampBehavior:(ServerTimestampBehavior)serverTimestampBehavior { - PigeonGetOptions *pigeonResult = [[PigeonGetOptions alloc] init]; + InternalGetOptions *pigeonResult = [[InternalGetOptions alloc] init]; pigeonResult.source = source; pigeonResult.serverTimestampBehavior = serverTimestampBehavior; return pigeonResult; } -+ (PigeonGetOptions *)fromList:(NSArray *)list { - PigeonGetOptions *pigeonResult = [[PigeonGetOptions alloc] init]; - pigeonResult.source = [GetNullableObjectAtIndex(list, 0) integerValue]; - pigeonResult.serverTimestampBehavior = [GetNullableObjectAtIndex(list, 1) integerValue]; ++ (InternalGetOptions *)fromList:(NSArray *)list { + InternalGetOptions *pigeonResult = [[InternalGetOptions alloc] init]; + SourceBox *boxedSource = GetNullableObjectAtIndex(list, 0); + pigeonResult.source = boxedSource.value; + ServerTimestampBehaviorBox *boxedServerTimestampBehavior = GetNullableObjectAtIndex(list, 1); + pigeonResult.serverTimestampBehavior = boxedServerTimestampBehavior.value; return pigeonResult; } -+ (nullable PigeonGetOptions *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonGetOptions fromList:list] : nil; ++ (nullable InternalGetOptions *)nullableFromList:(NSArray *)list { + return (list) ? [InternalGetOptions fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - @(self.source), - @(self.serverTimestampBehavior), + [[SourceBox alloc] initWithValue:self.source], + [[ServerTimestampBehaviorBox alloc] initWithValue:self.serverTimestampBehavior], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalGetOptions *other = (InternalGetOptions *)object; + return self.source == other.source && + self.serverTimestampBehavior == other.serverTimestampBehavior; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.source).hash; + result = result * 31 + @(self.serverTimestampBehavior).hash; + return result; +} @end -@implementation PigeonDocumentOption +@implementation InternalDocumentOption + (instancetype)makeWithMerge:(nullable NSNumber *)merge mergeFields:(nullable NSArray *> *)mergeFields { - PigeonDocumentOption *pigeonResult = [[PigeonDocumentOption alloc] init]; + InternalDocumentOption *pigeonResult = [[InternalDocumentOption alloc] init]; pigeonResult.merge = merge; pigeonResult.mergeFields = mergeFields; return pigeonResult; } -+ (PigeonDocumentOption *)fromList:(NSArray *)list { - PigeonDocumentOption *pigeonResult = [[PigeonDocumentOption alloc] init]; ++ (InternalDocumentOption *)fromList:(NSArray *)list { + InternalDocumentOption *pigeonResult = [[InternalDocumentOption alloc] init]; pigeonResult.merge = GetNullableObjectAtIndex(list, 0); pigeonResult.mergeFields = GetNullableObjectAtIndex(list, 1); return pigeonResult; } -+ (nullable PigeonDocumentOption *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonDocumentOption fromList:list] : nil; ++ (nullable InternalDocumentOption *)nullableFromList:(NSArray *)list { + return (list) ? [InternalDocumentOption fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.merge ?: [NSNull null]), - (self.mergeFields ?: [NSNull null]), + self.merge ?: [NSNull null], + self.mergeFields ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalDocumentOption *other = (InternalDocumentOption *)object; + return FLTPigeonDeepEquals(self.merge, other.merge) && + FLTPigeonDeepEquals(self.mergeFields, other.mergeFields); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.merge); + result = result * 31 + FLTPigeonDeepHash(self.mergeFields); + return result; +} @end -@implementation PigeonTransactionCommand -+ (instancetype)makeWithType:(PigeonTransactionType)type +@implementation InternalTransactionCommand ++ (instancetype)makeWithType:(InternalTransactionType)type path:(NSString *)path data:(nullable NSDictionary *)data - option:(nullable PigeonDocumentOption *)option { - PigeonTransactionCommand *pigeonResult = [[PigeonTransactionCommand alloc] init]; + option:(nullable InternalDocumentOption *)option { + InternalTransactionCommand *pigeonResult = [[InternalTransactionCommand alloc] init]; pigeonResult.type = type; pigeonResult.path = path; pigeonResult.data = data; pigeonResult.option = option; return pigeonResult; } -+ (PigeonTransactionCommand *)fromList:(NSArray *)list { - PigeonTransactionCommand *pigeonResult = [[PigeonTransactionCommand alloc] init]; - pigeonResult.type = [GetNullableObjectAtIndex(list, 0) integerValue]; ++ (InternalTransactionCommand *)fromList:(NSArray *)list { + InternalTransactionCommand *pigeonResult = [[InternalTransactionCommand alloc] init]; + InternalTransactionTypeBox *boxedInternalTransactionType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedInternalTransactionType.value; pigeonResult.path = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.path != nil, @""); pigeonResult.data = GetNullableObjectAtIndex(list, 2); - pigeonResult.option = [PigeonDocumentOption nullableFromList:(GetNullableObjectAtIndex(list, 3))]; + pigeonResult.option = GetNullableObjectAtIndex(list, 3); return pigeonResult; } -+ (nullable PigeonTransactionCommand *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonTransactionCommand fromList:list] : nil; ++ (nullable InternalTransactionCommand *)nullableFromList:(NSArray *)list { + return (list) ? [InternalTransactionCommand fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - @(self.type), - (self.path ?: [NSNull null]), - (self.data ?: [NSNull null]), - (self.option ? [self.option toList] : [NSNull null]), + [[InternalTransactionTypeBox alloc] initWithValue:self.type], + self.path ?: [NSNull null], + self.data ?: [NSNull null], + self.option ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalTransactionCommand *other = (InternalTransactionCommand *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.path, other.path) && + FLTPigeonDeepEquals(self.data, other.data) && + FLTPigeonDeepEquals(self.option, other.option); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.path); + result = result * 31 + FLTPigeonDeepHash(self.data); + result = result * 31 + FLTPigeonDeepHash(self.option); + return result; +} @end @implementation DocumentReferenceRequest + (instancetype)makeWithPath:(NSString *)path data:(nullable NSDictionary *)data - option:(nullable PigeonDocumentOption *)option + option:(nullable InternalDocumentOption *)option source:(nullable SourceBox *)source serverTimestampBehavior:(nullable ServerTimestampBehaviorBox *)serverTimestampBehavior { DocumentReferenceRequest *pigeonResult = [[DocumentReferenceRequest alloc] init]; @@ -508,42 +867,53 @@ + (instancetype)makeWithPath:(NSString *)path pigeonResult.serverTimestampBehavior = serverTimestampBehavior; return pigeonResult; } -+ (DocumentReferenceRequest *)fromList:(NSArray *)list { ++ (DocumentReferenceRequest *)fromList:(NSArray *)list { DocumentReferenceRequest *pigeonResult = [[DocumentReferenceRequest alloc] init]; pigeonResult.path = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.path != nil, @""); pigeonResult.data = GetNullableObjectAtIndex(list, 1); - pigeonResult.option = [PigeonDocumentOption nullableFromList:(GetNullableObjectAtIndex(list, 2))]; - NSNumber *sourceAsNumber = GetNullableObjectAtIndex(list, 3); - SourceBox *source = - sourceAsNumber == nil ? nil : [[SourceBox alloc] initWithValue:[sourceAsNumber integerValue]]; - pigeonResult.source = source; - NSNumber *serverTimestampBehaviorAsNumber = GetNullableObjectAtIndex(list, 4); - ServerTimestampBehaviorBox *serverTimestampBehavior = - serverTimestampBehaviorAsNumber == nil - ? nil - : [[ServerTimestampBehaviorBox alloc] - initWithValue:[serverTimestampBehaviorAsNumber integerValue]]; - pigeonResult.serverTimestampBehavior = serverTimestampBehavior; + pigeonResult.option = GetNullableObjectAtIndex(list, 2); + pigeonResult.source = GetNullableObjectAtIndex(list, 3); + pigeonResult.serverTimestampBehavior = GetNullableObjectAtIndex(list, 4); return pigeonResult; } -+ (nullable DocumentReferenceRequest *)nullableFromList:(NSArray *)list { ++ (nullable DocumentReferenceRequest *)nullableFromList:(NSArray *)list { return (list) ? [DocumentReferenceRequest fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.path ?: [NSNull null]), - (self.data ?: [NSNull null]), - (self.option ? [self.option toList] : [NSNull null]), - (self.source == nil ? [NSNull null] : [NSNumber numberWithInteger:self.source.value]), - (self.serverTimestampBehavior == nil - ? [NSNull null] - : [NSNumber numberWithInteger:self.serverTimestampBehavior.value]), + self.path ?: [NSNull null], + self.data ?: [NSNull null], + self.option ?: [NSNull null], + self.source ?: [NSNull null], + self.serverTimestampBehavior ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + DocumentReferenceRequest *other = (DocumentReferenceRequest *)object; + return FLTPigeonDeepEquals(self.path, other.path) && FLTPigeonDeepEquals(self.data, other.data) && + FLTPigeonDeepEquals(self.option, other.option) && + FLTPigeonDeepEquals(self.source, other.source) && + FLTPigeonDeepEquals(self.serverTimestampBehavior, other.serverTimestampBehavior); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.path); + result = result * 31 + FLTPigeonDeepHash(self.data); + result = result * 31 + FLTPigeonDeepHash(self.option); + result = result * 31 + FLTPigeonDeepHash(self.source); + result = result * 31 + FLTPigeonDeepHash(self.serverTimestampBehavior); + return result; +} @end -@implementation PigeonQueryParameters +@implementation InternalQueryParameters + (instancetype)makeWithWhere:(nullable NSArray *> *)where orderBy:(nullable NSArray *> *)orderBy limit:(nullable NSNumber *)limit @@ -553,7 +923,7 @@ + (instancetype)makeWithWhere:(nullable NSArray *> *)where endAt:(nullable NSArray *)endAt endBefore:(nullable NSArray *)endBefore filters:(nullable NSDictionary *)filters { - PigeonQueryParameters *pigeonResult = [[PigeonQueryParameters alloc] init]; + InternalQueryParameters *pigeonResult = [[InternalQueryParameters alloc] init]; pigeonResult.where = where; pigeonResult.orderBy = orderBy; pigeonResult.limit = limit; @@ -565,8 +935,8 @@ + (instancetype)makeWithWhere:(nullable NSArray *> *)where pigeonResult.filters = filters; return pigeonResult; } -+ (PigeonQueryParameters *)fromList:(NSArray *)list { - PigeonQueryParameters *pigeonResult = [[PigeonQueryParameters alloc] init]; ++ (InternalQueryParameters *)fromList:(NSArray *)list { + InternalQueryParameters *pigeonResult = [[InternalQueryParameters alloc] init]; pigeonResult.where = GetNullableObjectAtIndex(list, 0); pigeonResult.orderBy = GetNullableObjectAtIndex(list, 1); pigeonResult.limit = GetNullableObjectAtIndex(list, 2); @@ -578,22 +948,54 @@ + (PigeonQueryParameters *)fromList:(NSArray *)list { pigeonResult.filters = GetNullableObjectAtIndex(list, 8); return pigeonResult; } -+ (nullable PigeonQueryParameters *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonQueryParameters fromList:list] : nil; ++ (nullable InternalQueryParameters *)nullableFromList:(NSArray *)list { + return (list) ? [InternalQueryParameters fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - (self.where ?: [NSNull null]), - (self.orderBy ?: [NSNull null]), - (self.limit ?: [NSNull null]), - (self.limitToLast ?: [NSNull null]), - (self.startAt ?: [NSNull null]), - (self.startAfter ?: [NSNull null]), - (self.endAt ?: [NSNull null]), - (self.endBefore ?: [NSNull null]), - (self.filters ?: [NSNull null]), + self.where ?: [NSNull null], + self.orderBy ?: [NSNull null], + self.limit ?: [NSNull null], + self.limitToLast ?: [NSNull null], + self.startAt ?: [NSNull null], + self.startAfter ?: [NSNull null], + self.endAt ?: [NSNull null], + self.endBefore ?: [NSNull null], + self.filters ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalQueryParameters *other = (InternalQueryParameters *)object; + return FLTPigeonDeepEquals(self.where, other.where) && + FLTPigeonDeepEquals(self.orderBy, other.orderBy) && + FLTPigeonDeepEquals(self.limit, other.limit) && + FLTPigeonDeepEquals(self.limitToLast, other.limitToLast) && + FLTPigeonDeepEquals(self.startAt, other.startAt) && + FLTPigeonDeepEquals(self.startAfter, other.startAfter) && + FLTPigeonDeepEquals(self.endAt, other.endAt) && + FLTPigeonDeepEquals(self.endBefore, other.endBefore) && + FLTPigeonDeepEquals(self.filters, other.filters); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.where); + result = result * 31 + FLTPigeonDeepHash(self.orderBy); + result = result * 31 + FLTPigeonDeepHash(self.limit); + result = result * 31 + FLTPigeonDeepHash(self.limitToLast); + result = result * 31 + FLTPigeonDeepHash(self.startAt); + result = result * 31 + FLTPigeonDeepHash(self.startAfter); + result = result * 31 + FLTPigeonDeepHash(self.endAt); + result = result * 31 + FLTPigeonDeepHash(self.endBefore); + result = result * 31 + FLTPigeonDeepHash(self.filters); + return result; +} @end @implementation AggregateQuery @@ -603,21 +1005,39 @@ + (instancetype)makeWithType:(AggregateType)type field:(nullable NSString *)fiel pigeonResult.field = field; return pigeonResult; } -+ (AggregateQuery *)fromList:(NSArray *)list { ++ (AggregateQuery *)fromList:(NSArray *)list { AggregateQuery *pigeonResult = [[AggregateQuery alloc] init]; - pigeonResult.type = [GetNullableObjectAtIndex(list, 0) integerValue]; + AggregateTypeBox *boxedAggregateType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedAggregateType.value; pigeonResult.field = GetNullableObjectAtIndex(list, 1); return pigeonResult; } -+ (nullable AggregateQuery *)nullableFromList:(NSArray *)list { ++ (nullable AggregateQuery *)nullableFromList:(NSArray *)list { return (list) ? [AggregateQuery fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - @(self.type), - (self.field ?: [NSNull null]), + [[AggregateTypeBox alloc] initWithValue:self.type], + self.field ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + AggregateQuery *other = (AggregateQuery *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.field, other.field); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.field); + return result; +} @end @implementation AggregateQueryResponse @@ -630,23 +1050,43 @@ + (instancetype)makeWithType:(AggregateType)type pigeonResult.value = value; return pigeonResult; } -+ (AggregateQueryResponse *)fromList:(NSArray *)list { ++ (AggregateQueryResponse *)fromList:(NSArray *)list { AggregateQueryResponse *pigeonResult = [[AggregateQueryResponse alloc] init]; - pigeonResult.type = [GetNullableObjectAtIndex(list, 0) integerValue]; + AggregateTypeBox *boxedAggregateType = GetNullableObjectAtIndex(list, 0); + pigeonResult.type = boxedAggregateType.value; pigeonResult.field = GetNullableObjectAtIndex(list, 1); pigeonResult.value = GetNullableObjectAtIndex(list, 2); return pigeonResult; } -+ (nullable AggregateQueryResponse *)nullableFromList:(NSArray *)list { ++ (nullable AggregateQueryResponse *)nullableFromList:(NSArray *)list { return (list) ? [AggregateQueryResponse fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - @(self.type), - (self.field ?: [NSNull null]), - (self.value ?: [NSNull null]), + [[AggregateTypeBox alloc] initWithValue:self.type], + self.field ?: [NSNull null], + self.value ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + AggregateQueryResponse *other = (AggregateQueryResponse *)object; + return self.type == other.type && FLTPigeonDeepEquals(self.field, other.field) && + FLTPigeonDeepEquals(self.value, other.value); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.type).hash; + result = result * 31 + FLTPigeonDeepHash(self.field); + result = result * 31 + FLTPigeonDeepHash(self.value); + return result; +} @end @interface FirebaseFirestoreHostApiCodecReader : FLTFirebaseFirestoreReader @@ -654,32 +1094,89 @@ @interface FirebaseFirestoreHostApiCodecReader : FLTFirebaseFirestoreReader @implementation FirebaseFirestoreHostApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: - return [AggregateQuery fromList:[self readValue]]; - case 129: - return [AggregateQueryResponse fromList:[self readValue]]; - case 130: - return [DocumentReferenceRequest fromList:[self readValue]]; - case 131: - return [FirestorePigeonFirebaseApp fromList:[self readValue]]; - case 132: - return [PigeonDocumentChange fromList:[self readValue]]; - case 133: - return [PigeonDocumentOption fromList:[self readValue]]; - case 134: - return [PigeonDocumentSnapshot fromList:[self readValue]]; - case 135: - return [PigeonFirebaseSettings fromList:[self readValue]]; - case 136: - return [PigeonGetOptions fromList:[self readValue]]; - case 137: - return [PigeonQueryParameters fromList:[self readValue]]; + case 129: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[DocumentChangeTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 130: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil + : [[SourceBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 131: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[ListenSourceBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 132: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[ServerTimestampBehaviorBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 133: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[AggregateSourceBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 134: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil + : [[PersistenceCacheIndexManagerRequestBox alloc] + initWithValue:[enumAsNumber integerValue]]; + } + case 135: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[InternalTransactionResultBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 136: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[InternalTransactionTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 137: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[AggregateTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } case 138: - return [PigeonQuerySnapshot fromList:[self readValue]]; + return [InternalFirebaseSettings fromList:[self readValue]]; case 139: - return [PigeonSnapshotMetadata fromList:[self readValue]]; + return [FirestorePigeonFirebaseApp fromList:[self readValue]]; case 140: - return [PigeonTransactionCommand fromList:[self readValue]]; + return [InternalSnapshotMetadata fromList:[self readValue]]; + case 141: + return [InternalDocumentSnapshot fromList:[self readValue]]; + case 142: + return [InternalDocumentChange fromList:[self readValue]]; + case 143: + return [InternalQuerySnapshot fromList:[self readValue]]; + case 144: + return [InternalPipelineResult fromList:[self readValue]]; + case 145: + return [InternalPipelineSnapshot fromList:[self readValue]]; + case 146: + return [InternalGetOptions fromList:[self readValue]]; + case 147: + return [InternalDocumentOption fromList:[self readValue]]; + case 148: + return [InternalTransactionCommand fromList:[self readValue]]; + case 149: + return [DocumentReferenceRequest fromList:[self readValue]]; + case 150: + return [InternalQueryParameters fromList:[self readValue]]; + case 151: + return [AggregateQuery fromList:[self readValue]]; + case 152: + return [AggregateQueryResponse fromList:[self readValue]]; default: return [super readValueOfType:type]; } @@ -690,45 +1187,87 @@ @interface FirebaseFirestoreHostApiCodecWriter : FLTFirebaseFirestoreWriter @end @implementation FirebaseFirestoreHostApiCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[AggregateQuery class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[AggregateQueryResponse class]]) { + if ([value isKindOfClass:[DocumentChangeTypeBox class]]) { + DocumentChangeTypeBox *box = (DocumentChangeTypeBox *)value; [self writeByte:129]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[DocumentReferenceRequest class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[SourceBox class]]) { + SourceBox *box = (SourceBox *)value; [self writeByte:130]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FirestorePigeonFirebaseApp class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[ListenSourceBox class]]) { + ListenSourceBox *box = (ListenSourceBox *)value; [self writeByte:131]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonDocumentChange class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[ServerTimestampBehaviorBox class]]) { + ServerTimestampBehaviorBox *box = (ServerTimestampBehaviorBox *)value; [self writeByte:132]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonDocumentOption class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[AggregateSourceBox class]]) { + AggregateSourceBox *box = (AggregateSourceBox *)value; [self writeByte:133]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonDocumentSnapshot class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[PersistenceCacheIndexManagerRequestBox class]]) { + PersistenceCacheIndexManagerRequestBox *box = (PersistenceCacheIndexManagerRequestBox *)value; [self writeByte:134]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonFirebaseSettings class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalTransactionResultBox class]]) { + InternalTransactionResultBox *box = (InternalTransactionResultBox *)value; [self writeByte:135]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonGetOptions class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalTransactionTypeBox class]]) { + InternalTransactionTypeBox *box = (InternalTransactionTypeBox *)value; [self writeByte:136]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonQueryParameters class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[AggregateTypeBox class]]) { + AggregateTypeBox *box = (AggregateTypeBox *)value; [self writeByte:137]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonQuerySnapshot class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalFirebaseSettings class]]) { [self writeByte:138]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonSnapshotMetadata class]]) { + } else if ([value isKindOfClass:[FirestorePigeonFirebaseApp class]]) { [self writeByte:139]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonTransactionCommand class]]) { + } else if ([value isKindOfClass:[InternalSnapshotMetadata class]]) { [self writeByte:140]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalDocumentSnapshot class]]) { + [self writeByte:141]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalDocumentChange class]]) { + [self writeByte:142]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalQuerySnapshot class]]) { + [self writeByte:143]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalPipelineResult class]]) { + [self writeByte:144]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalPipelineSnapshot class]]) { + [self writeByte:145]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalGetOptions class]]) { + [self writeByte:146]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalDocumentOption class]]) { + [self writeByte:147]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalTransactionCommand class]]) { + [self writeByte:148]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[DocumentReferenceRequest class]]) { + [self writeByte:149]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalQueryParameters class]]) { + [self writeByte:150]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[AggregateQuery class]]) { + [self writeByte:151]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[AggregateQueryResponse class]]) { + [self writeByte:152]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -746,7 +1285,7 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { } @end -NSObject *FirebaseFirestoreHostApiGetCodec(void) { +NSObject *GetFirebaseFirestoreHostApiCodec(void) { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ @@ -756,22 +1295,32 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { }); return sSharedObject; } - -void FirebaseFirestoreHostApiSetup(id binaryMessenger, +void SetUpFirebaseFirestoreHostApi(id binaryMessenger, NSObject *api) { + SetUpFirebaseFirestoreHostApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpFirebaseFirestoreHostApiWithSuffix(id binaryMessenger, + NSObject *api, + NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.loadBundle" + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.loadBundle", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(loadBundleApp:bundle:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(loadBundleApp:bundle:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); FlutterStandardTypedData *arg_bundle = GetNullableObjectAtIndex(args, 1); [api loadBundleApp:arg_app @@ -786,24 +1335,27 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.namedQueryGet" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.namedQueryGet", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(namedQueryGetApp:name:options:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(namedQueryGetApp:name:options:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_name = GetNullableObjectAtIndex(args, 1); - PigeonGetOptions *arg_options = GetNullableObjectAtIndex(args, 2); + InternalGetOptions *arg_options = GetNullableObjectAtIndex(args, 2); [api namedQueryGetApp:arg_app name:arg_name options:arg_options - completion:^(PigeonQuerySnapshot *_Nullable output, + completion:^(InternalQuerySnapshot *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -814,17 +1366,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.clearPersistence" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.clearPersistence", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(clearPersistenceApp:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(clearPersistenceApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api clearPersistenceApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -837,17 +1392,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.disableNetwork" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.disableNetwork", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(disableNetworkApp:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(disableNetworkApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api disableNetworkApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -860,17 +1418,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.enableNetwork" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.enableNetwork", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(enableNetworkApp:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(enableNetworkApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api enableNetworkApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -883,17 +1444,19 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.terminate" + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.terminate", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(terminateApp:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(terminateApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api terminateApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -906,17 +1469,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.waitForPendingWrites" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.waitForPendingWrites", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(waitForPendingWritesApp:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(waitForPendingWritesApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api waitForPendingWritesApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -929,10 +1495,13 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.setIndexConfiguration" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.setIndexConfiguration", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector( @@ -941,7 +1510,7 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"@selector(setIndexConfigurationApp:indexConfiguration:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_indexConfiguration = GetNullableObjectAtIndex(args, 1); [api setIndexConfigurationApp:arg_app @@ -956,18 +1525,21 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.setLoggingEnabled" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.setLoggingEnabled", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(setLoggingEnabledLoggingEnabled:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(setLoggingEnabledLoggingEnabled:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - NSNumber *arg_loggingEnabled = GetNullableObjectAtIndex(args, 0); + NSArray *args = message; + BOOL arg_loggingEnabled = [GetNullableObjectAtIndex(args, 0) boolValue]; [api setLoggingEnabledLoggingEnabled:arg_loggingEnabled completion:^(FlutterError *_Nullable error) { callback(wrapResult(nil, error)); @@ -979,17 +1551,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.snapshotsInSyncSetup" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.snapshotsInSyncSetup", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(snapshotsInSyncSetupApp:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(snapshotsInSyncSetupApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api snapshotsInSyncSetupApp:arg_app completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { @@ -1002,10 +1577,13 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.transactionCreate" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.transactionCreate", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(transactionCreateApp:timeout:maxAttempts:completion:)], @@ -1013,10 +1591,10 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"@selector(transactionCreateApp:timeout:maxAttempts:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_timeout = GetNullableObjectAtIndex(args, 1); - NSNumber *arg_maxAttempts = GetNullableObjectAtIndex(args, 2); + NSInteger arg_timeout = [GetNullableObjectAtIndex(args, 1) integerValue]; + NSInteger arg_maxAttempts = [GetNullableObjectAtIndex(args, 2) integerValue]; [api transactionCreateApp:arg_app timeout:arg_timeout maxAttempts:arg_maxAttempts @@ -1030,10 +1608,13 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.transactionStoreResult" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.transactionStoreResult", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(transactionStoreResultTransactionId:resultType: commands:completion:)], @@ -1041,10 +1622,12 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"@selector(transactionStoreResultTransactionId:resultType:commands:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; NSString *arg_transactionId = GetNullableObjectAtIndex(args, 0); - PigeonTransactionResult arg_resultType = [GetNullableObjectAtIndex(args, 1) integerValue]; - NSArray *arg_commands = GetNullableObjectAtIndex(args, 2); + InternalTransactionResultBox *boxedInternalTransactionResult = + GetNullableObjectAtIndex(args, 1); + InternalTransactionResult arg_resultType = boxedInternalTransactionResult.value; + NSArray *arg_commands = GetNullableObjectAtIndex(args, 2); [api transactionStoreResultTransactionId:arg_transactionId resultType:arg_resultType commands:arg_commands @@ -1058,10 +1641,13 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.transactionGet" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.transactionGet", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(transactionGetApp:transactionId:path:completion:)], @@ -1069,14 +1655,14 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"@selector(transactionGetApp:transactionId:path:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_transactionId = GetNullableObjectAtIndex(args, 1); NSString *arg_path = GetNullableObjectAtIndex(args, 2); [api transactionGetApp:arg_app transactionId:arg_transactionId path:arg_path - completion:^(PigeonDocumentSnapshot *_Nullable output, + completion:^(InternalDocumentSnapshot *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1087,17 +1673,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.documentReferenceSet" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceSet", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(documentReferenceSetApp:request:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(documentReferenceSetApp:request:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); [api documentReferenceSetApp:arg_app @@ -1112,17 +1701,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.documentReferenceUpdate" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceUpdate", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(documentReferenceUpdateApp:request:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(documentReferenceUpdateApp:request:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); [api documentReferenceUpdateApp:arg_app @@ -1137,22 +1729,25 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.documentReferenceGet" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceGet", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(documentReferenceGetApp:request:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(documentReferenceGetApp:request:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); [api documentReferenceGetApp:arg_app request:arg_request - completion:^(PigeonDocumentSnapshot *_Nullable output, + completion:^(InternalDocumentSnapshot *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1163,17 +1758,20 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.documentReferenceDelete" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceDelete", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(documentReferenceDeleteApp:request:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(documentReferenceDeleteApp:request:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); DocumentReferenceRequest *arg_request = GetNullableObjectAtIndex(args, 1); [api documentReferenceDeleteApp:arg_app @@ -1188,10 +1786,12 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.queryGet" + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.queryGet", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(queryGetApp:path:isCollectionGroup:parameters: options:completion:)], @@ -1199,18 +1799,18 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"@selector(queryGetApp:path:isCollectionGroup:parameters:options:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_path = GetNullableObjectAtIndex(args, 1); - NSNumber *arg_isCollectionGroup = GetNullableObjectAtIndex(args, 2); - PigeonQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 3); - PigeonGetOptions *arg_options = GetNullableObjectAtIndex(args, 4); + BOOL arg_isCollectionGroup = [GetNullableObjectAtIndex(args, 2) boolValue]; + InternalQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 3); + InternalGetOptions *arg_options = GetNullableObjectAtIndex(args, 4); [api queryGetApp:arg_app path:arg_path isCollectionGroup:arg_isCollectionGroup parameters:arg_parameters options:arg_options - completion:^(PigeonQuerySnapshot *_Nullable output, + completion:^(InternalQuerySnapshot *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1221,10 +1821,13 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.aggregateQuery" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.aggregateQuery", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(aggregateQueryApp:path:parameters:source:queries: isCollectionGroup:completion:)], @@ -1233,13 +1836,14 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_path = GetNullableObjectAtIndex(args, 1); - PigeonQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 2); - AggregateSource arg_source = [GetNullableObjectAtIndex(args, 3) integerValue]; + InternalQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 2); + AggregateSourceBox *boxedAggregateSource = GetNullableObjectAtIndex(args, 3); + AggregateSource arg_source = boxedAggregateSource.value; NSArray *arg_queries = GetNullableObjectAtIndex(args, 4); - NSNumber *arg_isCollectionGroup = GetNullableObjectAtIndex(args, 5); + BOOL arg_isCollectionGroup = [GetNullableObjectAtIndex(args, 5) boolValue]; [api aggregateQueryApp:arg_app path:arg_path parameters:arg_parameters @@ -1257,19 +1861,22 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.writeBatchCommit" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.writeBatchCommit", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(writeBatchCommitApp:writes:completion:)], @"FirebaseFirestoreHostApi api (%@) doesn't respond to " @"@selector(writeBatchCommitApp:writes:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - NSArray *arg_writes = GetNullableObjectAtIndex(args, 1); + NSArray *arg_writes = GetNullableObjectAtIndex(args, 1); [api writeBatchCommitApp:arg_app writes:arg_writes completion:^(FlutterError *_Nullable error) { @@ -1282,10 +1889,13 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.querySnapshot" + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.querySnapshot", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(querySnapshotApp:path:isCollectionGroup:parameters: @@ -1295,14 +1905,15 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"includeMetadataChanges:source:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_path = GetNullableObjectAtIndex(args, 1); - NSNumber *arg_isCollectionGroup = GetNullableObjectAtIndex(args, 2); - PigeonQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 3); - PigeonGetOptions *arg_options = GetNullableObjectAtIndex(args, 4); - NSNumber *arg_includeMetadataChanges = GetNullableObjectAtIndex(args, 5); - ListenSource arg_source = [GetNullableObjectAtIndex(args, 6) integerValue]; + BOOL arg_isCollectionGroup = [GetNullableObjectAtIndex(args, 2) boolValue]; + InternalQueryParameters *arg_parameters = GetNullableObjectAtIndex(args, 3); + InternalGetOptions *arg_options = GetNullableObjectAtIndex(args, 4); + BOOL arg_includeMetadataChanges = [GetNullableObjectAtIndex(args, 5) boolValue]; + ListenSourceBox *boxedListenSource = GetNullableObjectAtIndex(args, 6); + ListenSource arg_source = boxedListenSource.value; [api querySnapshotApp:arg_app path:arg_path isCollectionGroup:arg_isCollectionGroup @@ -1320,10 +1931,13 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.documentReferenceSnapshot" + initWithName: + [NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.documentReferenceSnapshot", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(documentReferenceSnapshotApp:parameters: includeMetadataChanges:source:completion:)], @@ -1332,11 +1946,12 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); DocumentReferenceRequest *arg_parameters = GetNullableObjectAtIndex(args, 1); - NSNumber *arg_includeMetadataChanges = GetNullableObjectAtIndex(args, 2); - ListenSource arg_source = [GetNullableObjectAtIndex(args, 3) integerValue]; + BOOL arg_includeMetadataChanges = [GetNullableObjectAtIndex(args, 2) boolValue]; + ListenSourceBox *boxedListenSource = GetNullableObjectAtIndex(args, 3); + ListenSource arg_source = boxedListenSource.value; [api documentReferenceSnapshotApp:arg_app parameters:arg_parameters includeMetadataChanges:arg_includeMetadataChanges @@ -1352,10 +1967,14 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface." - @"FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest" + initWithName: + [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_interface." + @"FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest", + messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseFirestoreHostApiGetCodec()]; + codec:GetFirebaseFirestoreHostApiCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector( @@ -1364,10 +1983,12 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, @"@selector(persistenceCacheIndexManagerRequestApp:request:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + PersistenceCacheIndexManagerRequestBox *boxedPersistenceCacheIndexManagerRequest = + GetNullableObjectAtIndex(args, 1); PersistenceCacheIndexManagerRequest arg_request = - [GetNullableObjectAtIndex(args, 1) integerValue]; + boxedPersistenceCacheIndexManagerRequest.value; [api persistenceCacheIndexManagerRequestApp:arg_app request:arg_request completion:^(FlutterError *_Nullable error) { @@ -1378,4 +1999,35 @@ void FirebaseFirestoreHostApiSetup(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.cloud_firestore_platform_" + @"interface.FirebaseFirestoreHostApi.executePipeline", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:GetFirebaseFirestoreHostApiCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(executePipelineApp:stages:options:completion:)], + @"FirebaseFirestoreHostApi api (%@) doesn't respond to " + @"@selector(executePipelineApp:stages:options:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSArray *> *arg_stages = GetNullableObjectAtIndex(args, 1); + NSDictionary *arg_options = GetNullableObjectAtIndex(args, 2); + [api executePipelineApp:arg_app + stages:arg_stages + options:arg_options + completion:^(InternalPipelineSnapshot *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } } diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m index 1103e92e4e7b..0178847b7426 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestorePigeonParser.m @@ -60,7 +60,7 @@ + (FIRFilter *_Nonnull)filterFromJson:(NSDictionary *_Nullable)m @throw [NSException exceptionWithName:@"InvalidOperator" reason:@"Invalid operator" userInfo:nil]; } -+ (FIRQuery *_Nonnull)parseQueryWithParameters:(nonnull PigeonQueryParameters *)parameters ++ (FIRQuery *_Nonnull)parseQueryWithParameters:(nonnull InternalQueryParameters *)parameters firestore:(nonnull FIRFirestore *)firestore path:(nonnull NSString *)path isCollectionGroup:(Boolean)isCollectionGroup { @@ -218,17 +218,16 @@ + (FIRListenSource)parseListenSource:(ListenSource)source { } } -+ (PigeonSnapshotMetadata *_Nonnull)toPigeonSnapshotMetadata: ++ (InternalSnapshotMetadata *_Nonnull)toPigeonSnapshotMetadata: (FIRSnapshotMetadata *_Nonnull)snapshotMetadata { - return [PigeonSnapshotMetadata - makeWithHasPendingWrites:[NSNumber numberWithBool:snapshotMetadata.hasPendingWrites] - isFromCache:[NSNumber numberWithBool:snapshotMetadata.isFromCache]]; + return [InternalSnapshotMetadata makeWithHasPendingWrites:snapshotMetadata.hasPendingWrites + isFromCache:snapshotMetadata.isFromCache]; } -+ (PigeonDocumentSnapshot *_Nonnull) ++ (InternalDocumentSnapshot *_Nonnull) toPigeonDocumentSnapshot:(FIRDocumentSnapshot *_Nonnull)documentSnapshot serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior { - return [PigeonDocumentSnapshot + return [InternalDocumentSnapshot makeWithPath:documentSnapshot.reference.path data:[documentSnapshot dataWithServerTimestampBehavior:serverTimestampBehavior] metadata:[FirestorePigeonParser toPigeonSnapshotMetadata:documentSnapshot.metadata]]; @@ -249,11 +248,11 @@ + (DocumentChangeType)toPigeonDocumentChangeType:(FIRDocumentChangeType)document } } -+ (PigeonDocumentChange *_Nonnull)toPigeonDocumentChange:(FIRDocumentChange *_Nonnull)documentChange - serverTimestampBehavior: - (FIRServerTimestampBehavior)serverTimestampBehavior { - NSNumber *oldIndex; - NSNumber *newIndex; ++ (InternalDocumentChange *_Nonnull) + toPigeonDocumentChange:(FIRDocumentChange *_Nonnull)documentChange + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior { + NSInteger oldIndex; + NSInteger newIndex; // Note the Firestore C++ SDK here returns a maxed UInt that is != NSUIntegerMax, so we make one // ourselves so we can convert to -1 for Dart. @@ -261,19 +260,19 @@ + (PigeonDocumentChange *_Nonnull)toPigeonDocumentChange:(FIRDocumentChange *_No if (documentChange.newIndex == NSNotFound || documentChange.newIndex == 4294967295 || documentChange.newIndex == MAX_VAL) { - newIndex = @([@(-1) intValue]); + newIndex = -1; } else { - newIndex = @([@(documentChange.newIndex) intValue]); + newIndex = (NSInteger)documentChange.newIndex; } if (documentChange.oldIndex == NSNotFound || documentChange.oldIndex == 4294967295 || documentChange.oldIndex == MAX_VAL) { - oldIndex = @([@(-1) intValue]); + oldIndex = -1; } else { - oldIndex = @([@(documentChange.oldIndex) intValue]); + oldIndex = (NSInteger)documentChange.oldIndex; } - return [PigeonDocumentChange + return [InternalDocumentChange makeWithType:[FirestorePigeonParser toPigeonDocumentChangeType:documentChange.type] document:[FirestorePigeonParser toPigeonDocumentSnapshot:documentChange.document serverTimestampBehavior:serverTimestampBehavior] @@ -281,7 +280,7 @@ + (PigeonDocumentChange *_Nonnull)toPigeonDocumentChange:(FIRDocumentChange *_No newIndex:newIndex]; } -+ (NSArray *_Nonnull) ++ (NSArray *_Nonnull) toPigeonDocumentChanges:(NSArray *_Nonnull)documentChanges serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior { NSMutableArray *pigeonDocumentChanges = [NSMutableArray array]; @@ -293,16 +292,16 @@ + (PigeonDocumentChange *_Nonnull)toPigeonDocumentChange:(FIRDocumentChange *_No return pigeonDocumentChanges; } -+ (PigeonQuerySnapshot *_Nonnull)toPigeonQuerySnapshot:(FIRQuerySnapshot *_Nonnull)querySnaphot - serverTimestampBehavior: - (FIRServerTimestampBehavior)serverTimestampBehavior { ++ (InternalQuerySnapshot *_Nonnull)toPigeonQuerySnapshot:(FIRQuerySnapshot *_Nonnull)querySnaphot + serverTimestampBehavior: + (FIRServerTimestampBehavior)serverTimestampBehavior { NSMutableArray *documentSnapshots = [NSMutableArray array]; for (FIRDocumentSnapshot *documentSnapshot in querySnaphot.documents) { [documentSnapshots addObject:[FirestorePigeonParser toPigeonDocumentSnapshot:documentSnapshot serverTimestampBehavior:serverTimestampBehavior]]; } - return [PigeonQuerySnapshot + return [InternalQuerySnapshot makeWithDocuments:documentSnapshots documentChanges:[FirestorePigeonParser toPigeonDocumentChanges:querySnaphot.documentChanges serverTimestampBehavior:serverTimestampBehavior] diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h index b6fed78e7795..e04b30b5483e 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h @@ -18,6 +18,10 @@ #import #import "FLTFirebaseFirestoreExtension.h" +/// Error code used by the pipeline parser for parse/unsupported expression errors. +/// Handled in ErrorCodeAndMessageFromNSError to return code "parse-error" and the error message. +FOUNDATION_EXPORT const NSInteger FLTFirebaseFirestoreErrorCodePipelineParse; + typedef NS_ENUM(UInt8, FirestoreDataType) { FirestoreDataTypeDateTime = 180, FirestoreDataTypeGeoPoint = 181, diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h new file mode 100644 index 000000000000..97c77f0e2a88 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h @@ -0,0 +1,23 @@ +/* + * Copyright 2026, the Chromium project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#import + +@class FIRFirestore; + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTPipelineParser : NSObject + ++ (void)executePipelineWithFirestore:(FIRFirestore *)firestore + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion: + (void (^)(id _Nullable snapshot, NSError *_Nullable error))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h index 213570d9887b..c40a148efa5d 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTTransactionStreamHandler.h @@ -25,17 +25,17 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTTransactionStreamHandler : NSObject @property(nonatomic, strong) FIRFirestore *firestore; -@property(nonatomic, strong) NSNumber *timeout; -@property(nonatomic, strong) NSNumber *maxAttempts; +@property(nonatomic, assign) NSInteger timeout; +@property(nonatomic, assign) NSInteger maxAttempts; - (instancetype)initWithId:(NSString *)transactionId firestore:(FIRFirestore *)firestore - timeout:(nonnull NSNumber *)timeout - maxAttempts:(nonnull NSNumber *)maxAttempts + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts started:(void (^)(FIRTransaction *))startedListener ended:(void (^)(void))endedListener; -- (void)receiveTransactionResponse:(PigeonTransactionResult)resultType - commands:(NSArray *)commands; +- (void)receiveTransactionResponse:(InternalTransactionResult)resultType + commands:(NSArray *)commands; @end diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h index e197ef7592b0..5fa20d98759f 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FirestorePigeonParser.h @@ -19,7 +19,7 @@ + (FIRFilter *_Nonnull)filterFromJson:(NSDictionary *_Nullable)map; -+ (FIRQuery *_Nonnull)parseQueryWithParameters:(nonnull PigeonQueryParameters *)parameters ++ (FIRQuery *_Nonnull)parseQueryWithParameters:(nonnull InternalQueryParameters *)parameters firestore:(nonnull FIRFirestore *)firestore path:(nonnull NSString *)path isCollectionGroup:(Boolean)isCollectionGroup; @@ -34,25 +34,25 @@ + (FIRListenSource)parseListenSource:(ListenSource)source; -+ (PigeonSnapshotMetadata *_Nonnull)toPigeonSnapshotMetadata: ++ (InternalSnapshotMetadata *_Nonnull)toPigeonSnapshotMetadata: (FIRSnapshotMetadata *_Nonnull)snapshotMetadata; -+ (PigeonDocumentSnapshot *_Nonnull) ++ (InternalDocumentSnapshot *_Nonnull) toPigeonDocumentSnapshot:(FIRDocumentSnapshot *_Nonnull)documentSnapshot serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior; + (DocumentChangeType)toPigeonDocumentChangeType:(FIRDocumentChangeType)documentChangeType; -+ (PigeonDocumentChange *_Nonnull)toPigeonDocumentChange:(FIRDocumentChange *_Nonnull)documentChange - serverTimestampBehavior: - (FIRServerTimestampBehavior)serverTimestampBehavior; ++ (InternalDocumentChange *_Nonnull) + toPigeonDocumentChange:(FIRDocumentChange *_Nonnull)documentChange + serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior; -+ (NSArray *_Nonnull) ++ (NSArray *_Nonnull) toPigeonDocumentChanges:(NSArray *_Nonnull)documentChanges serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior; -+ (PigeonQuerySnapshot *_Nonnull)toPigeonQuerySnapshot:(FIRQuerySnapshot *_Nonnull)querySnaphot - serverTimestampBehavior: - (FIRServerTimestampBehavior)serverTimestampBehavior; ++ (InternalQuerySnapshot *_Nonnull)toPigeonQuerySnapshot:(FIRQuerySnapshot *_Nonnull)querySnaphot + serverTimestampBehavior: + (FIRServerTimestampBehavior)serverTimestampBehavior; @end diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h index 3cfb88fa5cf9..7127b0061f58 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/CustomPigeonHeaderFirestore.h @@ -3,14 +3,14 @@ // found in the LICENSE file. #import "FirestoreMessages.g.h" -@interface PigeonDocumentSnapshot (Map) +@interface InternalDocumentSnapshot (Map) - (NSDictionary *)toList; @end -@interface PigeonDocumentChange (Map) +@interface InternalDocumentChange (Map) - (NSDictionary *)toList; @end -@interface PigeonSnapshotMetadata (Map) +@interface InternalSnapshotMetadata (Map) - (NSDictionary *)toList; @end diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h index 79c560b14e79..7fd40daf7fcf 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h @@ -1,10 +1,10 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import +@import Foundation; @protocol FlutterBinaryMessenger; @protocol FlutterMessageCodec; @@ -123,28 +123,28 @@ typedef NS_ENUM(NSUInteger, PersistenceCacheIndexManagerRequest) { - (instancetype)initWithValue:(PersistenceCacheIndexManagerRequest)value; @end -typedef NS_ENUM(NSUInteger, PigeonTransactionResult) { - PigeonTransactionResultSuccess = 0, - PigeonTransactionResultFailure = 1, +typedef NS_ENUM(NSUInteger, InternalTransactionResult) { + InternalTransactionResultSuccess = 0, + InternalTransactionResultFailure = 1, }; -/// Wrapper for PigeonTransactionResult to allow for nullability. -@interface PigeonTransactionResultBox : NSObject -@property(nonatomic, assign) PigeonTransactionResult value; -- (instancetype)initWithValue:(PigeonTransactionResult)value; +/// Wrapper for InternalTransactionResult to allow for nullability. +@interface InternalTransactionResultBox : NSObject +@property(nonatomic, assign) InternalTransactionResult value; +- (instancetype)initWithValue:(InternalTransactionResult)value; @end -typedef NS_ENUM(NSUInteger, PigeonTransactionType) { - PigeonTransactionTypeGet = 0, - PigeonTransactionTypeUpdate = 1, - PigeonTransactionTypeSet = 2, - PigeonTransactionTypeDeleteType = 3, +typedef NS_ENUM(NSUInteger, InternalTransactionType) { + InternalTransactionTypeGet = 0, + InternalTransactionTypeUpdate = 1, + InternalTransactionTypeSet = 2, + InternalTransactionTypeDeleteType = 3, }; -/// Wrapper for PigeonTransactionType to allow for nullability. -@interface PigeonTransactionTypeBox : NSObject -@property(nonatomic, assign) PigeonTransactionType value; -- (instancetype)initWithValue:(PigeonTransactionType)value; +/// Wrapper for InternalTransactionType to allow for nullability. +@interface InternalTransactionTypeBox : NSObject +@property(nonatomic, assign) InternalTransactionType value; +- (instancetype)initWithValue:(InternalTransactionType)value; @end typedef NS_ENUM(NSUInteger, AggregateType) { @@ -159,91 +159,113 @@ typedef NS_ENUM(NSUInteger, AggregateType) { - (instancetype)initWithValue:(AggregateType)value; @end -@class PigeonFirebaseSettings; +@class InternalFirebaseSettings; @class FirestorePigeonFirebaseApp; -@class PigeonSnapshotMetadata; -@class PigeonDocumentSnapshot; -@class PigeonDocumentChange; -@class PigeonQuerySnapshot; -@class PigeonGetOptions; -@class PigeonDocumentOption; -@class PigeonTransactionCommand; +@class InternalSnapshotMetadata; +@class InternalDocumentSnapshot; +@class InternalDocumentChange; +@class InternalQuerySnapshot; +@class InternalPipelineResult; +@class InternalPipelineSnapshot; +@class InternalGetOptions; +@class InternalDocumentOption; +@class InternalTransactionCommand; @class DocumentReferenceRequest; -@class PigeonQueryParameters; +@class InternalQueryParameters; @class AggregateQuery; @class AggregateQueryResponse; -@interface PigeonFirebaseSettings : NSObject +@interface InternalFirebaseSettings : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPersistenceEnabled:(nullable NSNumber *)persistenceEnabled host:(nullable NSString *)host sslEnabled:(nullable NSNumber *)sslEnabled cacheSizeBytes:(nullable NSNumber *)cacheSizeBytes - ignoreUndefinedProperties:(NSNumber *)ignoreUndefinedProperties; + ignoreUndefinedProperties:(BOOL)ignoreUndefinedProperties; @property(nonatomic, strong, nullable) NSNumber *persistenceEnabled; @property(nonatomic, copy, nullable) NSString *host; @property(nonatomic, strong, nullable) NSNumber *sslEnabled; @property(nonatomic, strong, nullable) NSNumber *cacheSizeBytes; -@property(nonatomic, strong) NSNumber *ignoreUndefinedProperties; +@property(nonatomic, assign) BOOL ignoreUndefinedProperties; @end @interface FirestorePigeonFirebaseApp : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithAppName:(NSString *)appName - settings:(PigeonFirebaseSettings *)settings + settings:(InternalFirebaseSettings *)settings databaseURL:(NSString *)databaseURL; @property(nonatomic, copy) NSString *appName; -@property(nonatomic, strong) PigeonFirebaseSettings *settings; +@property(nonatomic, strong) InternalFirebaseSettings *settings; @property(nonatomic, copy) NSString *databaseURL; @end -@interface PigeonSnapshotMetadata : NSObject +@interface InternalSnapshotMetadata : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithHasPendingWrites:(NSNumber *)hasPendingWrites - isFromCache:(NSNumber *)isFromCache; -@property(nonatomic, strong) NSNumber *hasPendingWrites; -@property(nonatomic, strong) NSNumber *isFromCache; ++ (instancetype)makeWithHasPendingWrites:(BOOL)hasPendingWrites isFromCache:(BOOL)isFromCache; +@property(nonatomic, assign) BOOL hasPendingWrites; +@property(nonatomic, assign) BOOL isFromCache; @end -@interface PigeonDocumentSnapshot : NSObject +@interface InternalDocumentSnapshot : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPath:(NSString *)path data:(nullable NSDictionary *)data - metadata:(PigeonSnapshotMetadata *)metadata; + metadata:(InternalSnapshotMetadata *)metadata; @property(nonatomic, copy) NSString *path; -@property(nonatomic, strong, nullable) NSDictionary *data; -@property(nonatomic, strong) PigeonSnapshotMetadata *metadata; +@property(nonatomic, copy, nullable) NSDictionary *data; +@property(nonatomic, strong) InternalSnapshotMetadata *metadata; @end -@interface PigeonDocumentChange : NSObject +@interface InternalDocumentChange : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithType:(DocumentChangeType)type - document:(PigeonDocumentSnapshot *)document - oldIndex:(NSNumber *)oldIndex - newIndex:(NSNumber *)newIndex; + document:(InternalDocumentSnapshot *)document + oldIndex:(NSInteger)oldIndex + newIndex:(NSInteger)newIndex; @property(nonatomic, assign) DocumentChangeType type; -@property(nonatomic, strong) PigeonDocumentSnapshot *document; -@property(nonatomic, strong) NSNumber *oldIndex; -@property(nonatomic, strong) NSNumber *index; +@property(nonatomic, strong) InternalDocumentSnapshot *document; +@property(nonatomic, assign) NSInteger oldIndex; +@property(nonatomic, assign) NSInteger newIndex; @end -@interface PigeonQuerySnapshot : NSObject +@interface InternalQuerySnapshot : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithDocuments:(NSArray *)documents - documentChanges:(NSArray *)documentChanges - metadata:(PigeonSnapshotMetadata *)metadata; -@property(nonatomic, strong) NSArray *documents; -@property(nonatomic, strong) NSArray *documentChanges; -@property(nonatomic, strong) PigeonSnapshotMetadata *metadata; ++ (instancetype)makeWithDocuments:(NSArray *)documents + documentChanges:(NSArray *)documentChanges + metadata:(InternalSnapshotMetadata *)metadata; +@property(nonatomic, copy) NSArray *documents; +@property(nonatomic, copy) NSArray *documentChanges; +@property(nonatomic, strong) InternalSnapshotMetadata *metadata; @end -@interface PigeonGetOptions : NSObject +@interface InternalPipelineResult : NSObject ++ (instancetype)makeWithDocumentPath:(nullable NSString *)documentPath + createTime:(nullable NSNumber *)createTime + updateTime:(nullable NSNumber *)updateTime + data:(nullable NSDictionary *)data; +@property(nonatomic, copy, nullable) NSString *documentPath; +@property(nonatomic, strong, nullable) NSNumber *createTime; +@property(nonatomic, strong, nullable) NSNumber *updateTime; +/// All fields in the result (from PipelineResult.data() on Android). +@property(nonatomic, copy, nullable) NSDictionary *data; +@end + +@interface InternalPipelineSnapshot : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithResults:(NSArray *)results + executionTime:(NSInteger)executionTime; +@property(nonatomic, copy) NSArray *results; +@property(nonatomic, assign) NSInteger executionTime; +@end + +@interface InternalGetOptions : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithSource:(Source)source @@ -252,24 +274,24 @@ typedef NS_ENUM(NSUInteger, AggregateType) { @property(nonatomic, assign) ServerTimestampBehavior serverTimestampBehavior; @end -@interface PigeonDocumentOption : NSObject +@interface InternalDocumentOption : NSObject + (instancetype)makeWithMerge:(nullable NSNumber *)merge mergeFields:(nullable NSArray *> *)mergeFields; @property(nonatomic, strong, nullable) NSNumber *merge; -@property(nonatomic, strong, nullable) NSArray *> *mergeFields; +@property(nonatomic, copy, nullable) NSArray *> *mergeFields; @end -@interface PigeonTransactionCommand : NSObject +@interface InternalTransactionCommand : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithType:(PigeonTransactionType)type ++ (instancetype)makeWithType:(InternalTransactionType)type path:(NSString *)path data:(nullable NSDictionary *)data - option:(nullable PigeonDocumentOption *)option; -@property(nonatomic, assign) PigeonTransactionType type; + option:(nullable InternalDocumentOption *)option; +@property(nonatomic, assign) InternalTransactionType type; @property(nonatomic, copy) NSString *path; -@property(nonatomic, strong, nullable) NSDictionary *data; -@property(nonatomic, strong, nullable) PigeonDocumentOption *option; +@property(nonatomic, copy, nullable) NSDictionary *data; +@property(nonatomic, strong, nullable) InternalDocumentOption *option; @end @interface DocumentReferenceRequest : NSObject @@ -277,17 +299,17 @@ typedef NS_ENUM(NSUInteger, AggregateType) { - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPath:(NSString *)path data:(nullable NSDictionary *)data - option:(nullable PigeonDocumentOption *)option + option:(nullable InternalDocumentOption *)option source:(nullable SourceBox *)source serverTimestampBehavior:(nullable ServerTimestampBehaviorBox *)serverTimestampBehavior; @property(nonatomic, copy) NSString *path; -@property(nonatomic, strong, nullable) NSDictionary *data; -@property(nonatomic, strong, nullable) PigeonDocumentOption *option; +@property(nonatomic, copy, nullable) NSDictionary *data; +@property(nonatomic, strong, nullable) InternalDocumentOption *option; @property(nonatomic, strong, nullable) SourceBox *source; @property(nonatomic, strong, nullable) ServerTimestampBehaviorBox *serverTimestampBehavior; @end -@interface PigeonQueryParameters : NSObject +@interface InternalQueryParameters : NSObject + (instancetype)makeWithWhere:(nullable NSArray *> *)where orderBy:(nullable NSArray *> *)orderBy limit:(nullable NSNumber *)limit @@ -297,15 +319,15 @@ typedef NS_ENUM(NSUInteger, AggregateType) { endAt:(nullable NSArray *)endAt endBefore:(nullable NSArray *)endBefore filters:(nullable NSDictionary *)filters; -@property(nonatomic, strong, nullable) NSArray *> *where; -@property(nonatomic, strong, nullable) NSArray *> *orderBy; +@property(nonatomic, copy, nullable) NSArray *> *where; +@property(nonatomic, copy, nullable) NSArray *> *orderBy; @property(nonatomic, strong, nullable) NSNumber *limit; @property(nonatomic, strong, nullable) NSNumber *limitToLast; -@property(nonatomic, strong, nullable) NSArray *startAt; -@property(nonatomic, strong, nullable) NSArray *startAfter; -@property(nonatomic, strong, nullable) NSArray *endAt; -@property(nonatomic, strong, nullable) NSArray *endBefore; -@property(nonatomic, strong, nullable) NSDictionary *filters; +@property(nonatomic, copy, nullable) NSArray *startAt; +@property(nonatomic, copy, nullable) NSArray *startAfter; +@property(nonatomic, copy, nullable) NSArray *endAt; +@property(nonatomic, copy, nullable) NSArray *endBefore; +@property(nonatomic, copy, nullable) NSDictionary *filters; @end @interface AggregateQuery : NSObject @@ -327,8 +349,8 @@ typedef NS_ENUM(NSUInteger, AggregateType) { @property(nonatomic, strong, nullable) NSNumber *value; @end -/// The codec used by FirebaseFirestoreHostApi. -NSObject *FirebaseFirestoreHostApiGetCodec(void); +/// The codec used by all APIs. +NSObject *GetFirebaseFirestoreHostApiCodec(void); @protocol FirebaseFirestoreHostApi - (void)loadBundleApp:(FirestorePigeonFirebaseApp *)app @@ -336,9 +358,9 @@ NSObject *FirebaseFirestoreHostApiGetCodec(void); completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)namedQueryGetApp:(FirestorePigeonFirebaseApp *)app name:(NSString *)name - options:(PigeonGetOptions *)options + options:(InternalGetOptions *)options completion: - (void (^)(PigeonQuerySnapshot *_Nullable, FlutterError *_Nullable))completion; + (void (^)(InternalQuerySnapshot *_Nullable, FlutterError *_Nullable))completion; - (void)clearPersistenceApp:(FirestorePigeonFirebaseApp *)app completion:(void (^)(FlutterError *_Nullable))completion; - (void)disableNetworkApp:(FirestorePigeonFirebaseApp *)app @@ -352,23 +374,24 @@ NSObject *FirebaseFirestoreHostApiGetCodec(void); - (void)setIndexConfigurationApp:(FirestorePigeonFirebaseApp *)app indexConfiguration:(NSString *)indexConfiguration completion:(void (^)(FlutterError *_Nullable))completion; -- (void)setLoggingEnabledLoggingEnabled:(NSNumber *)loggingEnabled +- (void)setLoggingEnabledLoggingEnabled:(BOOL)loggingEnabled completion:(void (^)(FlutterError *_Nullable))completion; - (void)snapshotsInSyncSetupApp:(FirestorePigeonFirebaseApp *)app completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)transactionCreateApp:(FirestorePigeonFirebaseApp *)app - timeout:(NSNumber *)timeout - maxAttempts:(NSNumber *)maxAttempts + timeout:(NSInteger)timeout + maxAttempts:(NSInteger)maxAttempts completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)transactionStoreResultTransactionId:(NSString *)transactionId - resultType:(PigeonTransactionResult)resultType - commands:(nullable NSArray *)commands + resultType:(InternalTransactionResult)resultType + commands: + (nullable NSArray *)commands completion:(void (^)(FlutterError *_Nullable))completion; - (void)transactionGetApp:(FirestorePigeonFirebaseApp *)app transactionId:(NSString *)transactionId path:(NSString *)path - completion: - (void (^)(PigeonDocumentSnapshot *_Nullable, FlutterError *_Nullable))completion; + completion:(void (^)(InternalDocumentSnapshot *_Nullable, + FlutterError *_Nullable))completion; - (void)documentReferenceSetApp:(FirestorePigeonFirebaseApp *)app request:(DocumentReferenceRequest *)request completion:(void (^)(FlutterError *_Nullable))completion; @@ -377,48 +400,58 @@ NSObject *FirebaseFirestoreHostApiGetCodec(void); completion:(void (^)(FlutterError *_Nullable))completion; - (void)documentReferenceGetApp:(FirestorePigeonFirebaseApp *)app request:(DocumentReferenceRequest *)request - completion:(void (^)(PigeonDocumentSnapshot *_Nullable, + completion:(void (^)(InternalDocumentSnapshot *_Nullable, FlutterError *_Nullable))completion; - (void)documentReferenceDeleteApp:(FirestorePigeonFirebaseApp *)app request:(DocumentReferenceRequest *)request completion:(void (^)(FlutterError *_Nullable))completion; - (void)queryGetApp:(FirestorePigeonFirebaseApp *)app path:(NSString *)path - isCollectionGroup:(NSNumber *)isCollectionGroup - parameters:(PigeonQueryParameters *)parameters - options:(PigeonGetOptions *)options - completion:(void (^)(PigeonQuerySnapshot *_Nullable, FlutterError *_Nullable))completion; + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(InternalQueryParameters *)parameters + options:(InternalGetOptions *)options + completion: + (void (^)(InternalQuerySnapshot *_Nullable, FlutterError *_Nullable))completion; - (void)aggregateQueryApp:(FirestorePigeonFirebaseApp *)app path:(NSString *)path - parameters:(PigeonQueryParameters *)parameters + parameters:(InternalQueryParameters *)parameters source:(AggregateSource)source queries:(NSArray *)queries - isCollectionGroup:(NSNumber *)isCollectionGroup + isCollectionGroup:(BOOL)isCollectionGroup completion:(void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion; - (void)writeBatchCommitApp:(FirestorePigeonFirebaseApp *)app - writes:(NSArray *)writes + writes:(NSArray *)writes completion:(void (^)(FlutterError *_Nullable))completion; - (void)querySnapshotApp:(FirestorePigeonFirebaseApp *)app path:(NSString *)path - isCollectionGroup:(NSNumber *)isCollectionGroup - parameters:(PigeonQueryParameters *)parameters - options:(PigeonGetOptions *)options - includeMetadataChanges:(NSNumber *)includeMetadataChanges + isCollectionGroup:(BOOL)isCollectionGroup + parameters:(InternalQueryParameters *)parameters + options:(InternalGetOptions *)options + includeMetadataChanges:(BOOL)includeMetadataChanges source:(ListenSource)source completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)documentReferenceSnapshotApp:(FirestorePigeonFirebaseApp *)app parameters:(DocumentReferenceRequest *)parameters - includeMetadataChanges:(NSNumber *)includeMetadataChanges + includeMetadataChanges:(BOOL)includeMetadataChanges source:(ListenSource)source completion: (void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)persistenceCacheIndexManagerRequestApp:(FirestorePigeonFirebaseApp *)app request:(PersistenceCacheIndexManagerRequest)request completion:(void (^)(FlutterError *_Nullable))completion; +- (void)executePipelineApp:(FirestorePigeonFirebaseApp *)app + stages:(NSArray *> *)stages + options:(nullable NSDictionary *)options + completion:(void (^)(InternalPipelineSnapshot *_Nullable, + FlutterError *_Nullable))completion; @end -extern void FirebaseFirestoreHostApiSetup(id binaryMessenger, +extern void SetUpFirebaseFirestoreHostApi(id binaryMessenger, NSObject *_Nullable api); +extern void SetUpFirebaseFirestoreHostApiWithSuffix( + id binaryMessenger, NSObject *_Nullable api, + NSString *messageChannelSuffix); + NS_ASSUME_NONNULL_END diff --git a/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart b/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart index 8ec159100581..6578ad8f5c9a 100755 --- a/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart +++ b/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart @@ -5,7 +5,7 @@ import 'package:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:flutter/foundation.dart'; import 'package:meta/meta.dart'; @@ -56,7 +56,18 @@ part 'src/filters.dart'; part 'src/firestore.dart'; part 'src/load_bundle_task.dart'; part 'src/load_bundle_task_snapshot.dart'; +part 'src/pipeline_snapshot.dart'; part 'src/persistent_cache_index_manager.dart'; +part 'src/pipeline.dart'; +part 'src/pipeline_aggregate.dart'; +part 'src/pipeline_distance.dart'; +part 'src/pipeline_execute_options.dart'; +part 'src/pipeline_expression.dart'; +part 'src/pipeline_ordering.dart'; +part 'src/pipeline_sample.dart'; +part 'src/pipeline_search.dart'; +part 'src/pipeline_source.dart'; +part 'src/pipeline_stage.dart'; part 'src/query.dart'; part 'src/query_document_snapshot.dart'; part 'src/query_snapshot.dart'; diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/firestore.dart b/packages/cloud_firestore/cloud_firestore/lib/src/firestore.dart index 3e11563bb332..3c51c65d55c3 100644 --- a/packages/cloud_firestore/cloud_firestore/lib/src/firestore.dart +++ b/packages/cloud_firestore/cloud_firestore/lib/src/firestore.dart @@ -15,7 +15,7 @@ part of '../cloud_firestore.dart'; /// /// FirebaseFirestore firestore = FirebaseFirestore.instanceFor(app: secondaryApp); /// ``` -class FirebaseFirestore extends FirebasePluginPlatform { +class FirebaseFirestore extends FirebasePlugin { FirebaseFirestore._({ required this.app, required this.databaseId, @@ -340,6 +340,26 @@ class FirebaseFirestore extends FirebasePluginPlatform { return null; } + /// Returns a [PipelineSource] for creating and executing pipelines. + /// + /// Pipelines allow you to perform complex queries and transformations on + /// Firestore data using a fluent API. + /// + /// Example: + /// ```dart + /// final snapshot = await FirebaseFirestore.instance + /// .pipeline() + /// .collection('users') + /// .where(Field('age').greaterThan(Constant(18))) + /// .sort(Field('name').ascending(), Field('age').descending()) + /// .limit(10) + /// .execute(); + /// ``` + // ignore: use_to_and_as_if_applicable + PipelineSource pipeline() { + return PipelineSource._(this); + } + /// Configures indexing for local query execution. Any previous index configuration is overridden. /// /// The index entries themselves are created asynchronously. You can continue to use queries that diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline.dart new file mode 100644 index 000000000000..55ca21d79f47 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline.dart @@ -0,0 +1,852 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// A pipeline for querying and transforming Firestore data. +/// +/// A [Pipeline] is composed of a sequence of stages. Each stage processes the +/// output from the previous one, and the final stage's output is the result of +/// the pipeline's execution. +/// +/// Start with [FirebaseFirestore.pipeline] and a source stage (e.g. +/// [PipelineSource.collection]), then add transformation and filtering stages. +/// Call [execute] to run the pipeline. +/// +/// Example: +/// ```dart +/// final snapshot = await FirebaseFirestore.instance +/// .pipeline() +/// .collection('users') +/// .where(Expression.field('active').equal(true)) +/// .limit(10) +/// .execute(); +/// for (final result in snapshot.result) { +/// print(result.data()); +/// } +/// ``` +/// +/// **Note on execution:** The stages are conceptual. The Firestore backend may +/// optimize execution (e.g., reordering or merging stages) as long as the final +/// result remains the same. +/// +/// **Important limitations:** Pipelines operate on a request/response basis only. +/// They do not utilize or update the local SDK cache, and they do not support +/// realtime snapshot listeners. +class Pipeline { + final FirebaseFirestore _firestore; + final PipelinePlatform _delegate; + + Pipeline._(this._firestore, this._delegate) { + PipelinePlatform.verify(_delegate); + } + + /// Exposes the [stages] on the pipeline delegate. + /// + /// This should only be used for testing to ensure that all + /// pipeline stages are correctly set on the underlying delegate + /// when being tested from a different package. + @visibleForTesting + List> get stages { + return _delegate.stages; + } + + /// Executes this pipeline and returns the results as a [PipelineSnapshot]. + /// + /// Example: + /// ```dart + /// final snapshot = await firestore + /// .pipeline() + /// .collection('products') + /// .limit(5) + /// .execute(); + /// print('Got ${snapshot.result.length} documents'); + /// ``` + Future execute({ExecuteOptions? options}) async { + final optionsMap = options != null + ? { + 'indexMode': options.indexMode.name, + } + : null; + final platformSnapshot = await _delegate.execute(options: optionsMap); + return _convertPlatformSnapshot(platformSnapshot); + } + + /// Converts platform snapshot to public snapshot + PipelineSnapshot _convertPlatformSnapshot( + PipelineSnapshotPlatform platformSnapshot, + ) { + final results = platformSnapshot.results.map((platformResult) { + return PipelineResult( + document: platformResult.document != null + ? _JsonDocumentReference(_firestore, platformResult.document!) + : null, + createTime: platformResult.createTime, + updateTime: platformResult.updateTime, + data: platformResult.data, + ); + }).toList(); + + return PipelineSnapshot._(results, platformSnapshot.executionTime); + } + + // Pipeline Actions + + /// Adds new fields to outputs from previous stages. + /// + /// This stage allows you to compute values on-the-fly based on existing data + /// from previous stages or constants. You can create new fields or overwrite + /// existing ones. The added fields are defined using [Selectable]s, which can + /// be a [Field] (from [Expression.field]) or an expression with an alias via + /// [Expression.as]. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').addFields( + /// Expression.field('rating').as('bookRating'), + /// Expression.field('title').stringReplaceAllLiteral('Item', 'Doc').as('title_replaced'), + /// Expression.field('score').abs().as('abs_score'), + /// ); + /// ``` + Pipeline addFields( + Selectable selectable1, [ + Selectable? selectable2, + Selectable? selectable3, + Selectable? selectable4, + Selectable? selectable5, + Selectable? selectable6, + Selectable? selectable7, + Selectable? selectable8, + Selectable? selectable9, + Selectable? selectable10, + Selectable? selectable11, + Selectable? selectable12, + Selectable? selectable13, + Selectable? selectable14, + Selectable? selectable15, + Selectable? selectable16, + Selectable? selectable17, + Selectable? selectable18, + Selectable? selectable19, + Selectable? selectable20, + Selectable? selectable21, + Selectable? selectable22, + Selectable? selectable23, + Selectable? selectable24, + Selectable? selectable25, + Selectable? selectable26, + Selectable? selectable27, + Selectable? selectable28, + Selectable? selectable29, + Selectable? selectable30, + ]) { + final selectables = [selectable1]; + if (selectable2 != null) selectables.add(selectable2); + if (selectable3 != null) selectables.add(selectable3); + if (selectable4 != null) selectables.add(selectable4); + if (selectable5 != null) selectables.add(selectable5); + if (selectable6 != null) selectables.add(selectable6); + if (selectable7 != null) selectables.add(selectable7); + if (selectable8 != null) selectables.add(selectable8); + if (selectable9 != null) selectables.add(selectable9); + if (selectable10 != null) selectables.add(selectable10); + if (selectable11 != null) selectables.add(selectable11); + if (selectable12 != null) selectables.add(selectable12); + if (selectable13 != null) selectables.add(selectable13); + if (selectable14 != null) selectables.add(selectable14); + if (selectable15 != null) selectables.add(selectable15); + if (selectable16 != null) selectables.add(selectable16); + if (selectable17 != null) selectables.add(selectable17); + if (selectable18 != null) selectables.add(selectable18); + if (selectable19 != null) selectables.add(selectable19); + if (selectable20 != null) selectables.add(selectable20); + if (selectable21 != null) selectables.add(selectable21); + if (selectable22 != null) selectables.add(selectable22); + if (selectable23 != null) selectables.add(selectable23); + if (selectable24 != null) selectables.add(selectable24); + if (selectable25 != null) selectables.add(selectable25); + if (selectable26 != null) selectables.add(selectable26); + if (selectable27 != null) selectables.add(selectable27); + if (selectable28 != null) selectables.add(selectable28); + if (selectable29 != null) selectables.add(selectable29); + if (selectable30 != null) selectables.add(selectable30); + final stage = _AddFieldsStage(selectables); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Performs aggregation operations on the documents from previous stages. + /// + /// This stage allows you to calculate aggregate values over a set of documents. + /// Define aggregations using [AliasedAggregateFunction]s, typically by calling + /// [PipelineAggregateFunction.as] on [PipelineAggregateFunction] instances + /// such as [Expression.field] with [Expression.sum], [Expression.average], + /// or [CountAll]. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').aggregate( + /// Expression.field('score').sum().as('total_score'), + /// Expression.field('score').average().as('avg_score'), + /// CountAll().as('count'), + /// ); + /// ``` + Pipeline aggregate( + AliasedAggregateFunction aggregateFunction1, [ + AliasedAggregateFunction? aggregateFunction2, + AliasedAggregateFunction? aggregateFunction3, + AliasedAggregateFunction? aggregateFunction4, + AliasedAggregateFunction? aggregateFunction5, + AliasedAggregateFunction? aggregateFunction6, + AliasedAggregateFunction? aggregateFunction7, + AliasedAggregateFunction? aggregateFunction8, + AliasedAggregateFunction? aggregateFunction9, + AliasedAggregateFunction? aggregateFunction10, + AliasedAggregateFunction? aggregateFunction11, + AliasedAggregateFunction? aggregateFunction12, + AliasedAggregateFunction? aggregateFunction13, + AliasedAggregateFunction? aggregateFunction14, + AliasedAggregateFunction? aggregateFunction15, + AliasedAggregateFunction? aggregateFunction16, + AliasedAggregateFunction? aggregateFunction17, + AliasedAggregateFunction? aggregateFunction18, + AliasedAggregateFunction? aggregateFunction19, + AliasedAggregateFunction? aggregateFunction20, + AliasedAggregateFunction? aggregateFunction21, + AliasedAggregateFunction? aggregateFunction22, + AliasedAggregateFunction? aggregateFunction23, + AliasedAggregateFunction? aggregateFunction24, + AliasedAggregateFunction? aggregateFunction25, + AliasedAggregateFunction? aggregateFunction26, + AliasedAggregateFunction? aggregateFunction27, + AliasedAggregateFunction? aggregateFunction28, + AliasedAggregateFunction? aggregateFunction29, + AliasedAggregateFunction? aggregateFunction30, + ]) { + final functions = [aggregateFunction1]; + if (aggregateFunction2 != null) functions.add(aggregateFunction2); + if (aggregateFunction3 != null) functions.add(aggregateFunction3); + if (aggregateFunction4 != null) functions.add(aggregateFunction4); + if (aggregateFunction5 != null) functions.add(aggregateFunction5); + if (aggregateFunction6 != null) functions.add(aggregateFunction6); + if (aggregateFunction7 != null) functions.add(aggregateFunction7); + if (aggregateFunction8 != null) functions.add(aggregateFunction8); + if (aggregateFunction9 != null) functions.add(aggregateFunction9); + if (aggregateFunction10 != null) functions.add(aggregateFunction10); + if (aggregateFunction11 != null) functions.add(aggregateFunction11); + if (aggregateFunction12 != null) functions.add(aggregateFunction12); + if (aggregateFunction13 != null) functions.add(aggregateFunction13); + if (aggregateFunction14 != null) functions.add(aggregateFunction14); + if (aggregateFunction15 != null) functions.add(aggregateFunction15); + if (aggregateFunction16 != null) functions.add(aggregateFunction16); + if (aggregateFunction17 != null) functions.add(aggregateFunction17); + if (aggregateFunction18 != null) functions.add(aggregateFunction18); + if (aggregateFunction19 != null) functions.add(aggregateFunction19); + if (aggregateFunction20 != null) functions.add(aggregateFunction20); + if (aggregateFunction21 != null) functions.add(aggregateFunction21); + if (aggregateFunction22 != null) functions.add(aggregateFunction22); + if (aggregateFunction23 != null) functions.add(aggregateFunction23); + if (aggregateFunction24 != null) functions.add(aggregateFunction24); + if (aggregateFunction25 != null) functions.add(aggregateFunction25); + if (aggregateFunction26 != null) functions.add(aggregateFunction26); + if (aggregateFunction27 != null) functions.add(aggregateFunction27); + if (aggregateFunction28 != null) functions.add(aggregateFunction28); + if (aggregateFunction29 != null) functions.add(aggregateFunction29); + if (aggregateFunction30 != null) functions.add(aggregateFunction30); + + final stage = _AggregateStage(functions); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Performs optionally grouped aggregation operations on the documents from previous stages. + /// + /// This method allows you to calculate aggregate values over a set of documents, optionally + /// grouped by one or more fields or expressions. You can specify: + /// + /// - **Grouping Fields or Expressions**: One or more fields or functions to group the documents by. + /// For each distinct combination of values in these fields, a separate group is created. + /// If no grouping fields are provided, a single group containing all documents is used. + /// + /// - **Aggregate Functions**: One or more accumulation operations to perform within each group. + /// These are defined using [AliasedAggregateFunction] expressions, which are typically created + /// by calling `.as('alias')` on [PipelineAggregateFunction] instances. Each aggregation calculates + /// a value (e.g., sum, average, count) based on the documents within its group. + /// + /// Example: + /// ```dart + /// pipeline.aggregateWithOptions( + /// AggregateStageOptions( + /// accumulators: [ + /// Expression.field('likes').sum().as('total_likes'), + /// Expression.field('likes').average().as('avg_likes'), + /// ], + /// groups: [Expression.field('category')], + /// ), + /// ); + /// ``` + /// + /// With options: + /// ```dart + /// pipeline.aggregateWithOptions( + /// AggregateStageOptions( + /// accumulators: [ + /// Expression.field('likes').sum().as('total_likes'), + /// ], + /// ), + /// options: AggregateOptions(), + /// ); + /// ``` + Pipeline aggregateWithOptions( + AggregateStageOptions aggregateStage, { + AggregateOptions? options, + }) { + final stage = _AggregateStageWithOptions( + aggregateStage, + options ?? AggregateOptions(), + ); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Returns a set of distinct values from the inputs to this stage. + /// + /// This stage runs through the results from previous stages to include only + /// results with unique combinations of the given [Selectable] expressions + /// (e.g. [Expression.field], or expressions with [Expression.as]). + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').distinct( + /// Expression.field('category').as('category'), + /// ); + /// ``` + Pipeline distinct( + Selectable expression1, [ + Selectable? expression2, + Selectable? expression3, + Selectable? expression4, + Selectable? expression5, + Selectable? expression6, + Selectable? expression7, + Selectable? expression8, + Selectable? expression9, + Selectable? expression10, + Selectable? expression11, + Selectable? expression12, + Selectable? expression13, + Selectable? expression14, + Selectable? expression15, + Selectable? expression16, + Selectable? expression17, + Selectable? expression18, + Selectable? expression19, + Selectable? expression20, + Selectable? expression21, + Selectable? expression22, + Selectable? expression23, + Selectable? expression24, + Selectable? expression25, + Selectable? expression26, + Selectable? expression27, + Selectable? expression28, + Selectable? expression29, + Selectable? expression30, + ]) { + final expressions = [expression1]; + if (expression2 != null) expressions.add(expression2); + if (expression3 != null) expressions.add(expression3); + if (expression4 != null) expressions.add(expression4); + if (expression5 != null) expressions.add(expression5); + if (expression6 != null) expressions.add(expression6); + if (expression7 != null) expressions.add(expression7); + if (expression8 != null) expressions.add(expression8); + if (expression9 != null) expressions.add(expression9); + if (expression10 != null) expressions.add(expression10); + if (expression11 != null) expressions.add(expression11); + if (expression12 != null) expressions.add(expression12); + if (expression13 != null) expressions.add(expression13); + if (expression14 != null) expressions.add(expression14); + if (expression15 != null) expressions.add(expression15); + if (expression16 != null) expressions.add(expression16); + if (expression17 != null) expressions.add(expression17); + if (expression18 != null) expressions.add(expression18); + if (expression19 != null) expressions.add(expression19); + if (expression20 != null) expressions.add(expression20); + if (expression21 != null) expressions.add(expression21); + if (expression22 != null) expressions.add(expression22); + if (expression23 != null) expressions.add(expression23); + if (expression24 != null) expressions.add(expression24); + if (expression25 != null) expressions.add(expression25); + if (expression26 != null) expressions.add(expression26); + if (expression27 != null) expressions.add(expression27); + if (expression28 != null) expressions.add(expression28); + if (expression29 != null) expressions.add(expression29); + if (expression30 != null) expressions.add(expression30); + + final stage = _DistinctStage(expressions); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Performs a vector similarity search. + /// + /// Orders the result set by most similar to least similar and returns + /// documents in that order. Requires a vector index on [vectorField]. + /// [vectorValue] is the query embedding; [distanceMeasure] specifies how to + /// compare vectors (e.g. [DistanceMeasure.cosine]). Use [limit] to return + /// only the first N documents. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').findNearest( + /// Field('embedding'), + /// [0.1, 0.2, 0.3], + /// DistanceMeasure.cosine, + /// limit: 10, + /// ); + /// ``` + Pipeline findNearest( + Field vectorField, + List vectorValue, + DistanceMeasure distanceMeasure, { + int? limit, + }) { + final stage = _FindNearestStage( + vectorField, + vectorValue, + distanceMeasure, + limit: limit, + ); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Adds a search stage to this pipeline. + /// + /// Search stages execute full-text search or geo search operations. A search + /// stage must be the first stage after the pipeline source. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('restaurants').search( + /// SearchStage.withQuery('breakfast -diner', limit: 10), + /// ); + /// ``` + Pipeline search(SearchStage searchStage) { + if (_delegate.stages.length != 1) { + throw StateError( + 'A search stage must be the first stage after the pipeline source.', + ); + } + + final stage = _SearchStage(searchStage); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Limits the maximum number of documents returned by previous stages to + /// [limit]. + /// + /// Useful for pagination (with [offset]) or to cap data retrieval and + /// improve performance. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books') + /// .sort(Expression.field('rating').descending()) + /// .limit(10); + /// ``` + Pipeline limit(int limit) { + final stage = _LimitStage(limit); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Skips the first [offset] documents from the results of previous stages. + /// + /// Useful for implementing pagination; typically used with [limit] to control + /// the size of each page. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books') + /// .sort(Expression.field('published').descending()) + /// .offset(20) + /// .limit(20); + /// ``` + Pipeline offset(int offset) { + final stage = _OffsetStage(offset); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Removes fields from outputs of previous stages. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books') + /// .removeFields('rating', 'cost'); + /// ``` + Pipeline removeFields( + String fieldPath1, [ + String? fieldPath2, + String? fieldPath3, + String? fieldPath4, + String? fieldPath5, + String? fieldPath6, + String? fieldPath7, + String? fieldPath8, + String? fieldPath9, + String? fieldPath10, + String? fieldPath11, + String? fieldPath12, + String? fieldPath13, + String? fieldPath14, + String? fieldPath15, + String? fieldPath16, + String? fieldPath17, + String? fieldPath18, + String? fieldPath19, + String? fieldPath20, + String? fieldPath21, + String? fieldPath22, + String? fieldPath23, + String? fieldPath24, + String? fieldPath25, + String? fieldPath26, + String? fieldPath27, + String? fieldPath28, + String? fieldPath29, + String? fieldPath30, + ]) { + final fieldPaths = [fieldPath1]; + if (fieldPath2 != null) fieldPaths.add(fieldPath2); + if (fieldPath3 != null) fieldPaths.add(fieldPath3); + if (fieldPath4 != null) fieldPaths.add(fieldPath4); + if (fieldPath5 != null) fieldPaths.add(fieldPath5); + if (fieldPath6 != null) fieldPaths.add(fieldPath6); + if (fieldPath7 != null) fieldPaths.add(fieldPath7); + if (fieldPath8 != null) fieldPaths.add(fieldPath8); + if (fieldPath9 != null) fieldPaths.add(fieldPath9); + if (fieldPath10 != null) fieldPaths.add(fieldPath10); + if (fieldPath11 != null) fieldPaths.add(fieldPath11); + if (fieldPath12 != null) fieldPaths.add(fieldPath12); + if (fieldPath13 != null) fieldPaths.add(fieldPath13); + if (fieldPath14 != null) fieldPaths.add(fieldPath14); + if (fieldPath15 != null) fieldPaths.add(fieldPath15); + if (fieldPath16 != null) fieldPaths.add(fieldPath16); + if (fieldPath17 != null) fieldPaths.add(fieldPath17); + if (fieldPath18 != null) fieldPaths.add(fieldPath18); + if (fieldPath19 != null) fieldPaths.add(fieldPath19); + if (fieldPath20 != null) fieldPaths.add(fieldPath20); + if (fieldPath21 != null) fieldPaths.add(fieldPath21); + if (fieldPath22 != null) fieldPaths.add(fieldPath22); + if (fieldPath23 != null) fieldPaths.add(fieldPath23); + if (fieldPath24 != null) fieldPaths.add(fieldPath24); + if (fieldPath25 != null) fieldPaths.add(fieldPath25); + if (fieldPath26 != null) fieldPaths.add(fieldPath26); + if (fieldPath27 != null) fieldPaths.add(fieldPath27); + if (fieldPath28 != null) fieldPaths.add(fieldPath28); + if (fieldPath29 != null) fieldPaths.add(fieldPath29); + if (fieldPath30 != null) fieldPaths.add(fieldPath30); + + final stage = _RemoveFieldsStage(fieldPaths); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Fully overwrites each document with the value of the given expression. + /// + /// This stage allows you to emit a nested map or array as the document: each + /// document from the previous stage is replaced by the evaluated [expression] + /// (e.g. [Expression.field] referencing a nested map or array). + /// + /// Example: + /// ```dart + /// // Emit the 'parents' map as the document. + /// firestore.pipeline().collection('people') + /// .replaceWith(Expression.field('parents')); + /// ``` + Pipeline replaceWith(Expression expression) { + final stage = _ReplaceWithStage(expression); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Performs a pseudo-random sampling of the input documents. + /// + /// Use [PipelineSample.withSize] for a fixed number of documents, or + /// [PipelineSample.withPercentage] for a fraction of the result set. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books') + /// .sample(PipelineSample.withSize(10)); + /// firestore.pipeline().collection('books') + /// .sample(PipelineSample.withPercentage(0.5)); + /// ``` + Pipeline sample(PipelineSample sample) { + final stage = _SampleStage(sample); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Selects or creates a set of fields from the outputs of previous stages. + /// + /// Only the selected fields (with their aliases) appear in the output. Use + /// [Expression.field] with [Expression.as] for expressions. Use [addFields] + /// if you only want to add or overwrite fields while keeping the rest. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').select( + /// Expression.field('name').as('name'), + /// Expression.field('address').toUpperCase().as('upperAddress'), + /// ); + /// ``` + Pipeline select( + Selectable expression1, [ + Selectable? expression2, + Selectable? expression3, + Selectable? expression4, + Selectable? expression5, + Selectable? expression6, + Selectable? expression7, + Selectable? expression8, + Selectable? expression9, + Selectable? expression10, + Selectable? expression11, + Selectable? expression12, + Selectable? expression13, + Selectable? expression14, + Selectable? expression15, + Selectable? expression16, + Selectable? expression17, + Selectable? expression18, + Selectable? expression19, + Selectable? expression20, + Selectable? expression21, + Selectable? expression22, + Selectable? expression23, + Selectable? expression24, + Selectable? expression25, + Selectable? expression26, + Selectable? expression27, + Selectable? expression28, + Selectable? expression29, + Selectable? expression30, + ]) { + final expressions = [expression1]; + if (expression2 != null) expressions.add(expression2); + if (expression3 != null) expressions.add(expression3); + if (expression4 != null) expressions.add(expression4); + if (expression5 != null) expressions.add(expression5); + if (expression6 != null) expressions.add(expression6); + if (expression7 != null) expressions.add(expression7); + if (expression8 != null) expressions.add(expression8); + if (expression9 != null) expressions.add(expression9); + if (expression10 != null) expressions.add(expression10); + if (expression11 != null) expressions.add(expression11); + if (expression12 != null) expressions.add(expression12); + if (expression13 != null) expressions.add(expression13); + if (expression14 != null) expressions.add(expression14); + if (expression15 != null) expressions.add(expression15); + if (expression16 != null) expressions.add(expression16); + if (expression17 != null) expressions.add(expression17); + if (expression18 != null) expressions.add(expression18); + if (expression19 != null) expressions.add(expression19); + if (expression20 != null) expressions.add(expression20); + if (expression21 != null) expressions.add(expression21); + if (expression22 != null) expressions.add(expression22); + if (expression23 != null) expressions.add(expression23); + if (expression24 != null) expressions.add(expression24); + if (expression25 != null) expressions.add(expression25); + if (expression26 != null) expressions.add(expression26); + if (expression27 != null) expressions.add(expression27); + if (expression28 != null) expressions.add(expression28); + if (expression29 != null) expressions.add(expression29); + if (expression30 != null) expressions.add(expression30); + + final stage = _SelectStage(expressions); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Sorts the documents from previous stages based on one or more orderings. + /// + /// Orderings are applied in sequence (primary sort by the first, then by the + /// second, etc.). If documents have the same value for a field used for + /// sorting, the next ordering is used. Use [Expression.field] with + /// [Expression.descending] and [Expression.ascending]. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').sort( + /// Expression.field('rating').descending(), + /// Expression.field('title').ascending(), + /// ); + /// ``` + Pipeline sort( + Ordering order, [ + Ordering? order2, + Ordering? order3, + Ordering? order4, + Ordering? order5, + Ordering? order6, + Ordering? order7, + Ordering? order8, + Ordering? order9, + Ordering? order10, + Ordering? order11, + Ordering? order12, + Ordering? order13, + Ordering? order14, + Ordering? order15, + Ordering? order16, + Ordering? order17, + Ordering? order18, + Ordering? order19, + Ordering? order20, + Ordering? order21, + Ordering? order22, + Ordering? order23, + Ordering? order24, + Ordering? order25, + Ordering? order26, + Ordering? order27, + Ordering? order28, + Ordering? order29, + Ordering? order30, + ]) { + final orderings = [order]; + if (order2 != null) orderings.add(order2); + if (order3 != null) orderings.add(order3); + if (order4 != null) orderings.add(order4); + if (order5 != null) orderings.add(order5); + if (order6 != null) orderings.add(order6); + if (order7 != null) orderings.add(order7); + if (order8 != null) orderings.add(order8); + if (order9 != null) orderings.add(order9); + if (order10 != null) orderings.add(order10); + if (order11 != null) orderings.add(order11); + if (order12 != null) orderings.add(order12); + if (order13 != null) orderings.add(order13); + if (order14 != null) orderings.add(order14); + if (order15 != null) orderings.add(order15); + if (order16 != null) orderings.add(order16); + if (order17 != null) orderings.add(order17); + if (order18 != null) orderings.add(order18); + if (order19 != null) orderings.add(order19); + if (order20 != null) orderings.add(order20); + if (order21 != null) orderings.add(order21); + if (order22 != null) orderings.add(order22); + if (order23 != null) orderings.add(order23); + if (order24 != null) orderings.add(order24); + if (order25 != null) orderings.add(order25); + if (order26 != null) orderings.add(order26); + if (order27 != null) orderings.add(order27); + if (order28 != null) orderings.add(order28); + if (order29 != null) orderings.add(order29); + if (order30 != null) orderings.add(order30); + + final stage = _SortStage(orderings); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Takes a specified array from the input documents and outputs a document + /// for each element, with the element stored in a field named by the alias. + /// + /// For each document from the previous stage, this stage emits zero or more + /// documents (one per array element). Use [Expression.field].as() to specify + /// the array and the output field name. Optionally use [indexField] to add + /// the array index to each emitted document. + /// + /// Example: + /// ```dart + /// // Input: { "title": "Guide", "tags": ["comedy", "space"] } + /// // Output: one doc per tag with field "tag". + /// firestore.pipeline().collection('books') + /// .unnest(Expression.field('tags').as('tag')); + /// ``` + Pipeline unnest(Selectable expression, [String? indexField]) { + final stage = _UnnestStage(expression, indexField); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Performs a union of all documents from this pipeline and [pipeline], + /// including duplicates. + /// + /// Documents from the previous stage and from [pipeline] are combined. The + /// order of documents emitted from this stage is undefined. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').union( + /// firestore.pipeline().collection('magazines'), + /// ); + /// ``` + Pipeline union(Pipeline pipeline) { + final stage = _UnionStage(pipeline); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } + + /// Filters the documents from previous stages to only include those matching + /// the specified [BooleanExpression]. + /// + /// This stage applies conditions to the data, similar to a WHERE clause. You + /// can use [Expression.field] with [Expression.equal], [Expression.greaterThan], + /// [Expression.lessThan], etc., and combine conditions with [Expression.and] + /// and [Expression.or]. + /// + /// Example: + /// ```dart + /// firestore.pipeline().collection('books').where( + /// Expression.and( + /// Expression.field('rating').greaterThan(4.0), + /// Expression.field('genre').equal('Science Fiction'), + /// ), + /// ); + /// ``` + Pipeline where(BooleanExpression expression) { + final stage = _WhereStage(expression); + return Pipeline._( + _firestore, + _delegate.addStage(stage.toMap()), + ); + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_aggregate.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_aggregate.dart new file mode 100644 index 000000000000..7598f42a947a --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_aggregate.dart @@ -0,0 +1,294 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Base class for aggregate functions used in pipelines +abstract class PipelineAggregateFunction implements PipelineSerializable { + /// Assigns an alias to this aggregate function + AliasedAggregateFunction as(String alias) { + return AliasedAggregateFunction( + alias: alias, + aggregateFunction: this, + ); + } + + String get name; + + @override + Map toMap() { + return { + 'name': name, + }; + } +} + +/// Represents an aggregate function with an alias +class AliasedAggregateFunction implements PipelineSerializable { + final String _alias; + final PipelineAggregateFunction aggregateFunction; + + AliasedAggregateFunction({ + required String alias, + required this.aggregateFunction, + }) : _alias = alias; + + String get alias => _alias; + + @override + Map toMap() { + return { + 'name': 'alias', + 'args': { + 'alias': _alias, + 'aggregate_function': aggregateFunction.toMap(), + }, + }; + } +} + +/// Counts all documents in the pipeline result +class CountAll extends PipelineAggregateFunction { + CountAll(); + + @override + String get name => 'count_all'; +} + +/// Counts non-null values of the specified expression +class Count extends PipelineAggregateFunction { + final Expression expression; + + Count(this.expression); + + @override + String get name => 'count'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Sums numeric values of the specified expression +class Sum extends PipelineAggregateFunction { + final Expression expression; + + Sum(this.expression); + + @override + String get name => 'sum'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Calculates average of numeric values of the specified expression +class Average extends PipelineAggregateFunction { + final Expression expression; + + Average(this.expression); + + @override + String get name => 'average'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Counts distinct values of the specified expression +class CountDistinct extends PipelineAggregateFunction { + final Expression expression; + + CountDistinct(this.expression); + + @override + String get name => 'count_distinct'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Finds minimum value of the specified expression +class Minimum extends PipelineAggregateFunction { + final Expression expression; + + Minimum(this.expression); + + @override + String get name => 'minimum'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Finds maximum value of the specified expression +class Maximum extends PipelineAggregateFunction { + final Expression expression; + + Maximum(this.expression); + + @override + String get name => 'maximum'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Aggregate that picks the first value of [expression] across pipeline inputs. +/// +/// Use with [Pipeline.aggregate] or [AggregateStageOptions]. Corresponds to the +/// Firestore pipeline `first` accumulator. +class First extends PipelineAggregateFunction { + final Expression expression; + + First(this.expression); + + @override + String get name => 'first'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Aggregate that picks the last value of [expression] across pipeline inputs. +/// +/// Use with [Pipeline.aggregate] or [AggregateStageOptions]. Corresponds to the +/// Firestore pipeline `last` accumulator. +class Last extends PipelineAggregateFunction { + final Expression expression; + + Last(this.expression); + + @override + String get name => 'last'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Aggregate that collects all values of [expression] into an array. +/// +/// Order of elements is not guaranteed. Absent values become `null` in the result. +/// Corresponds to the Firestore pipeline `array_agg` accumulator. +class ArrayAgg extends PipelineAggregateFunction { + final Expression expression; + + ArrayAgg(this.expression); + + @override + String get name => 'array_agg'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Aggregate that collects distinct values of [expression] into an array. +/// +/// Order of elements is not guaranteed. Corresponds to the Firestore pipeline +/// `array_agg_distinct` accumulator. +class ArrayAggDistinct extends PipelineAggregateFunction { + final Expression expression; + + ArrayAggDistinct(this.expression); + + @override + String get name => 'array_agg_distinct'; + + @override + Map toMap() { + final map = super.toMap(); + map['args'] = { + 'expression': expression.toMap(), + }; + return map; + } +} + +/// Represents an aggregate stage with functions and optional grouping +class AggregateStageOptions implements PipelineSerializable { + final List accumulators; + final List? groups; + + AggregateStageOptions({ + required this.accumulators, + this.groups, + }); + + @override + Map toMap() { + final map = { + 'accumulators': accumulators.map((acc) => acc.toMap()).toList(), + }; + if (groups != null && groups!.isNotEmpty) { + map['groups'] = groups!.map((group) => group.toMap()).toList(); + } + return map; + } +} + +/// Options for aggregate operations +class AggregateOptions implements PipelineSerializable { + // Add any aggregate-specific options here as needed + // For now, this is a placeholder for future options + + AggregateOptions(); + + @override + Map toMap() { + return {}; + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_distance.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_distance.dart new file mode 100644 index 000000000000..8f01b16de52e --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_distance.dart @@ -0,0 +1,17 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Distance measure algorithms for vector similarity search +enum DistanceMeasure { + /// Cosine similarity + cosine, + + /// Euclidean distance + euclidean, + + /// Dot product + dotProduct, +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_execute_options.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_execute_options.dart new file mode 100644 index 000000000000..ee8b8e2eb42d --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_execute_options.dart @@ -0,0 +1,20 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Index mode for pipeline execution +enum IndexMode { + /// Use recommended index mode + recommended, +} + +/// Options for executing a pipeline +class ExecuteOptions { + final IndexMode indexMode; + + const ExecuteOptions({ + this.indexMode = IndexMode.recommended, + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_expression.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_expression.dart new file mode 100644 index 000000000000..a25c14f55f34 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_expression.dart @@ -0,0 +1,3782 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Base interface for pipeline serialization +mixin PipelineSerializable { + Map toMap(); +} + +/// Helper function to convert values to Expression (wraps in Constant if needed) +Expression _toExpression(Object? value) { + if (value == null) return Constant(null); + if (value is Expression) return value; + if (value is List) return Expression.array(value.cast()); + if (value is Map) { + return Expression.map(value.cast()); + } + return Constant(value); +} + +/// Valid unit strings for timestamp add/subtract/truncate expressions. +const Set _timestampUnits = { + 'microsecond', + 'millisecond', + 'second', + 'minute', + 'hour', + 'day', +}; + +void _validateTimestampUnit(String unit) { + if (!_timestampUnits.contains(unit)) { + throw ArgumentError( + "Timestamp unit must be one of: 'microsecond', 'millisecond', 'second', " + "'minute', 'hour', 'day'. Got: '$unit'", + ); + } +} + +/// Value types for [Expression.isType] and [Expression.isTypeStatic]. +enum Type { + /// `null` + nullValue('null'), + + /// `array` + array('array'), + + /// `boolean` + boolean('boolean'), + + /// `bytes` + bytes('bytes'), + + /// `timestamp` + timestamp('timestamp'), + + /// `geo_point` + geoPoint('geo_point'), + + /// `number` + number('number'), + + /// `int32` + int32('int32'), + + /// `int64` + int64('int64'), + + /// `float64` + float64('float64'), + + /// `decimal128` + decimal128('decimal128'), + + /// `map` + map('map'), + + /// `reference` + reference('reference'), + + /// `string` + string('string'), + + /// `vector` + vector('vector'), + + /// `max_key` + maxKey('max_key'), + + /// `min_key` + minKey('min_key'), + + /// `object_id` + objectId('object_id'), + + /// `regex` + regex('regex'), + + /// `request_timestamp` + requestTimestamp('request_timestamp'); + + const Type(this.typeValue); + + /// String passed to Firestore for `is_type` (same literals as the JS SDK). + final String typeValue; +} + +/// Base class for all pipeline expressions +abstract class Expression implements PipelineSerializable { + /// Creates an aliased expression + AliasedExpression as(String alias) { + return AliasedExpression( + alias: alias, + expression: this, + ); + } + + /// Creates a descending ordering for this expression + Ordering descending() { + return Ordering(this, OrderDirection.desc); + } + + /// Creates an ascending ordering for this expression + Ordering ascending() { + return Ordering(this, OrderDirection.asc); + } + + // ============================================================================ + // CONDITIONAL / LOGIC OPERATIONS + // ============================================================================ + + /// Returns an alternative expression if this expression is absent + Expression ifAbsent(Expression elseExpr) { + return _IfAbsentExpression(this, elseExpr); + } + + /// Returns an alternative value if this expression is absent + Expression ifAbsentValue(Object? elseValue) { + return _IfAbsentExpression(this, _toExpression(elseValue)); + } + + /// Returns an alternative expression if this expression errors + Expression ifError(Expression catchExpr) { + return _IfErrorExpression(this, catchExpr); + } + + /// Returns an alternative value if this expression errors + Expression ifErrorValue(Object? catchValue) { + return _IfErrorExpression(this, _toExpression(catchValue)); + } + + /// Checks if this expression is absent (null/undefined) + // ignore: use_to_and_as_if_applicable + BooleanExpression isAbsent() { + return _IsAbsentExpression(this); + } + + /// Checks if this expression produces an error + // ignore: use_to_and_as_if_applicable + BooleanExpression isError() { + return _IsErrorExpression(this); + } + + /// Checks if this field expression exists in the document + // ignore: use_to_and_as_if_applicable + BooleanExpression exists() { + return _ExistsExpression(this); + } + + // ============================================================================ + // TYPE CONVERSION + // ============================================================================ + + /// Casts this expression to a boolean expression + BooleanExpression asBoolean() { + return _AsBooleanExpression(this); + } + + // ============================================================================ + // BITWISE OPERATIONS + // ============================================================================ + + /// Performs bitwise AND with another expression + Expression bitAnd(Expression bitsOther) { + return _BitAndExpression(this, bitsOther); + } + + /// Performs bitwise AND with byte array + Expression bitAndBytes(List bitsOther) { + return _BitAndExpression(this, Constant(bitsOther)); + } + + /// Performs bitwise OR with another expression + Expression bitOr(Expression bitsOther) { + return _BitOrExpression(this, bitsOther); + } + + /// Performs bitwise OR with byte array + Expression bitOrBytes(List bitsOther) { + return _BitOrExpression(this, Constant(bitsOther)); + } + + /// Performs bitwise XOR with another expression + Expression bitXor(Expression bitsOther) { + return _BitXorExpression(this, bitsOther); + } + + /// Performs bitwise XOR with byte array + Expression bitXorBytes(List bitsOther) { + return _BitXorExpression(this, Constant(bitsOther)); + } + + /// Performs bitwise NOT on this expression + // ignore: use_to_and_as_if_applicable + Expression bitNot() { + return _BitNotExpression(this); + } + + /// Shifts bits left by an expression amount + Expression bitLeftShift(Expression numberExpr) { + return _BitLeftShiftExpression(this, numberExpr); + } + + /// Shifts bits left by a literal amount + Expression bitLeftShiftLiteral(int number) { + return _BitLeftShiftExpression(this, Constant(number)); + } + + /// Shifts bits right by an expression amount + Expression bitRightShift(Expression numberExpr) { + return _BitRightShiftExpression(this, numberExpr); + } + + /// Shifts bits right by a literal amount + Expression bitRightShiftLiteral(int number) { + return _BitRightShiftExpression(this, Constant(number)); + } + + // ============================================================================ + // DOCUMENT / PATH OPERATIONS + // ============================================================================ + + /// Returns the document ID from this path expression + // ignore: use_to_and_as_if_applicable + Expression documentId() { + return _DocumentIdExpression(this); + } + + /// Returns the collection ID from this path expression + // ignore: use_to_and_as_if_applicable + Expression collectionId() { + return _CollectionIdExpression(this); + } + + // ============================================================================ + // MAP OPERATIONS + // ============================================================================ + + /// Gets a value from this map expression by key expression + Expression mapGet(Expression key) { + return _MapGetExpression(this, key); + } + + /// Gets a value from this map expression by literal key + Expression mapGetLiteral(String key) { + return _MapGetExpression(this, Constant(key)); + } + + /// Returns a map expression with keys set to the given values (shallow update). + /// + /// [key] and [value] are the first pair. [moreKeyValues] must be alternating + /// keys and values. Setting a value to `null` keeps the key with a null value; + /// use map APIs that remove keys if you need deletion semantics. + Expression mapSet( + Object? key, + Object? value, [ + List? moreKeyValues, + ]) { + final pairs = [_toExpression(key), _toExpression(value)]; + if (moreKeyValues != null) { + for (final o in moreKeyValues) { + pairs.add(_toExpression(o)); + } + } + return _MapSetExpression(this, pairs); + } + + /// Returns an array of `{k, v}` map entries for this map expression. + // ignore: use_to_and_as_if_applicable + Expression mapEntries() { + return _MapEntriesExpression(this); + } + + // ============================================================================ + // ALIASING + // ============================================================================ + + /// Assigns an alias to this expression for use in output + Selectable alias(String alias) { + return AliasedExpression(alias: alias, expression: this); + } + + // ============================================================================ + // ARITHMETIC OPERATIONS + // ============================================================================ + + /// Adds this expression to another expression + Expression add(Expression other) { + return _AddExpression(this, other); + } + + /// Adds a number to this expression + Expression addNumber(num other) { + return _AddExpression(this, Constant(other)); + } + + /// Subtracts another expression from this expression + Expression subtract(Expression other) { + return _SubtractExpression(this, other); + } + + /// Subtracts a number from this expression + Expression subtractNumber(num other) { + return _SubtractExpression(this, Constant(other)); + } + + /// Multiplies this expression by another expression + Expression multiply(Expression other) { + return _MultiplyExpression(this, other); + } + + /// Multiplies this expression by a number + Expression multiplyNumber(num other) { + return _MultiplyExpression(this, Constant(other)); + } + + /// Divides this expression by another expression + Expression divide(Expression other) { + return _DivideExpression(this, other); + } + + /// Divides this expression by a number + Expression divideNumber(num other) { + return _DivideExpression(this, Constant(other)); + } + + /// Returns the remainder of dividing this expression by another + Expression modulo(Expression other) { + return _ModuloExpression(this, other); + } + + /// Returns the remainder of dividing this expression by a number + Expression moduloNumber(num other) { + return _ModuloExpression(this, Constant(other)); + } + + /// Returns the absolute value of this expression + // ignore: use_to_and_as_if_applicable + Expression abs() { + return _AbsExpression(this); + } + + /// Truncates this numeric expression toward zero. + /// + /// If [decimals] is omitted, truncates to an integer. If provided, truncates to + /// that many fractional digits (Firestore pipeline `trunc`). + Expression trunc([Expression? decimals]) { + return _TruncExpression(this, decimals); + } + + // ============================================================================ + // COMPARISON OPERATIONS (return BooleanExpression) + // ============================================================================ + + /// Checks if this expression equals another expression + BooleanExpression equal(Expression other) { + return _EqualExpression(this, other); + } + + /// Checks if this expression equals a value + BooleanExpression equalValue(Object? value) { + return _EqualExpression(this, _toExpression(value)); + } + + /// Checks if this expression does not equal another expression + BooleanExpression notEqual(Expression other) { + return _NotEqualExpression(this, other); + } + + /// Checks if this expression does not equal a value + BooleanExpression notEqualValue(Object? value) { + return _NotEqualExpression(this, _toExpression(value)); + } + + /// Checks if this expression is greater than another expression + BooleanExpression greaterThan(Expression other) { + return _GreaterThanExpression(this, other); + } + + /// Checks if this expression is greater than a value + BooleanExpression greaterThanValue(Object? value) { + return _GreaterThanExpression(this, _toExpression(value)); + } + + /// Checks if this expression is greater than or equal to another expression + BooleanExpression greaterThanOrEqual(Expression other) { + return _GreaterThanOrEqualExpression(this, other); + } + + /// Checks if this expression is greater than or equal to a value + BooleanExpression greaterThanOrEqualValue(Object? value) { + return _GreaterThanOrEqualExpression(this, _toExpression(value)); + } + + /// Checks if this expression is less than another expression + BooleanExpression lessThan(Expression other) { + return _LessThanExpression(this, other); + } + + /// Checks if this expression is less than a value + BooleanExpression lessThanValue(Object? value) { + return _LessThanExpression(this, _toExpression(value)); + } + + /// Checks if this expression is less than or equal to another expression + BooleanExpression lessThanOrEqual(Expression other) { + return _LessThanOrEqualExpression(this, other); + } + + /// Checks if this expression is less than or equal to a value + BooleanExpression lessThanOrEqualValue(Object? value) { + return _LessThanOrEqualExpression(this, _toExpression(value)); + } + + // ============================================================================ + // STRING OPERATIONS + // ============================================================================ + + /// Returns the length of this string expression + // ignore: use_to_and_as_if_applicable + Expression length() { + return _LengthExpression(this); + } + + /// Concatenates this expression with other expressions/values + Expression concat(List others) { + final expressions = [this]; + for (final other in others) { + expressions.add(_toExpression(other)); + } + return _ConcatExpression(expressions); + } + + /// Converts this string expression to lowercase + Expression toLowerCase() { + return _ToLowerCaseExpression(this); + } + + /// Converts this string expression to uppercase + Expression toUpperCase() { + return _ToUpperCaseExpression(this); + } + + /// Extracts a substring from this string expression + Expression substring(Expression start, Expression end) { + return _SubstringExpression(this, start, end); + } + + /// Extracts a substring using literal indices + Expression substringLiteral(int start, int end) { + return _SubstringExpression(this, Constant(start), Constant(end)); + } + + /// Replaces all occurrences of a pattern in this string (stringReplaceAll) + Expression stringReplaceAll(Expression find, Expression replacement) { + return _StringReplaceAllExpression(this, find, replacement); + } + + /// Replaces all occurrences of a string literal + Expression stringReplaceAllLiteral(String find, String replacement) { + return _StringReplaceAllExpression( + this, + Constant(find), + Constant(replacement), + ); + } + + /// Splits this string expression by a delimiter + Expression split(Expression delimiter) { + return _SplitExpression(this, delimiter); + } + + /// Splits this string by a literal delimiter + Expression splitLiteral(String delimiter) { + return _SplitExpression(this, Constant(delimiter)); + } + + /// Joins array elements with a delimiter + Expression join(Expression delimiter) { + return _JoinExpression(this, delimiter); + } + + /// Joins array elements with a literal delimiter + Expression joinLiteral(String delimiter) { + return _JoinExpression(this, Constant(delimiter)); + } + + /// Trims whitespace from this string expression + // ignore: use_to_and_as_if_applicable + Expression trim() { + return _TrimExpression(this); + } + + /// Returns the first regex match of [pattern] in this string expression. + Expression regexFind(Object? pattern) { + return _RegexFindExpression(this, _toExpression(pattern)); + } + + /// Returns all regex matches of [pattern] in this string expression. + Expression regexFindAll(Object? pattern) { + return _RegexFindAllExpression(this, _toExpression(pattern)); + } + + /// Replaces the first occurrence of [find] with [replacement]. + Expression stringReplaceOne(Expression find, Expression replacement) { + return _StringReplaceOneExpression(this, find, replacement); + } + + /// Replaces the first occurrence of string literals [find] with [replacement]. + Expression stringReplaceOneLiteral(String find, String replacement) { + return _StringReplaceOneExpression( + this, + Constant(find), + Constant(replacement), + ); + } + + /// Returns the index of [search] in this string, or an absent/error value if not found. + Expression stringIndexOf(Object? search) { + return _StringIndexOfExpression(this, _toExpression(search)); + } + + /// Repeats this string [repetitions] times ([repetitions] may be an [Expression] or number). + Expression stringRepeat(Object? repetitions) { + return _StringRepeatExpression(this, _toExpression(repetitions)); + } + + /// Trims leading whitespace, or the characters in [valueToTrim] when given. + Expression ltrim([Object? valueToTrim]) { + return valueToTrim == null + ? _LtrimExpression(this, null) + : _LtrimExpression(this, _toExpression(valueToTrim)); + } + + /// Trims trailing whitespace, or the characters in [valueToTrim] when given. + Expression rtrim([Object? valueToTrim]) { + return valueToTrim == null + ? _RtrimExpression(this, null) + : _RtrimExpression(this, _toExpression(valueToTrim)); + } + + /// Returns the Firestore value type of this expression as a string (e.g. + /// `int64`, `timestamp`). Possible values align with [Type]. + // ignore: use_to_and_as_if_applicable + Expression type() { + return _TypeExpression(this); + } + + /// Whether this expression evaluates to the given backend [Type]. + // ignore: use_to_and_as_if_applicable + BooleanExpression isType(Type valueType) { + return _IsTypeExpression(this, valueType); + } + + // ============================================================================ + // ARRAY OPERATIONS + // ============================================================================ + + /// Concatenates this array with another array expression + Expression arrayConcat(Object? secondArray) { + return _ArrayConcatExpression(this, _toExpression(secondArray)); + } + + /// Concatenates this array with multiple arrays/values + Expression arrayConcatMultiple(List otherArrays) { + final expressions = [this]; + for (final other in otherArrays) { + expressions.add(_toExpression(other)); + } + return _ArrayConcatMultipleExpression(expressions); + } + + /// Checks if this array contains an element expression + BooleanExpression arrayContainsElement(Expression element) { + return _ArrayContainsExpression(this, element); + } + + /// Checks if this array contains a value + BooleanExpression arrayContainsValue(Object? element) { + return _ArrayContainsExpression(this, _toExpression(element)); + } + + /// Checks if this array contains any of the given values or expressions + BooleanExpression arrayContainsAny(List values) { + return _ArrayContainsAnyExpression( + this, + values.map(_toExpression).toList(), + ); + } + + /// Checks if this array contains all of the given values or expressions + BooleanExpression arrayContainsAll(List values) { + return _ArrayContainsAllValuesExpression( + this, + values.map(_toExpression).toList(), + ); + } + + /// Checks if this array contains all elements of [arrayExpression] + /// (e.g. [arrayExpression] can be [Expression.array] of fields/literals). + BooleanExpression arrayContainsAllFrom(Expression arrayExpression) { + return _ArrayContainsAllExpression(this, arrayExpression); + } + + /// Returns the length of this array expression + // ignore: use_to_and_as_if_applicable + Expression arrayLength() { + return _ArrayLengthExpression(this); + } + + /// Reverses this array expression + // ignore: use_to_and_as_if_applicable + Expression arrayReverse() { + return _ArrayReverseExpression(this); + } + + /// Returns the sum of numeric elements in this array + // ignore: use_to_and_as_if_applicable + Expression arraySum() { + return _ArraySumExpression(this); + } + + /// First element of this array expression. + // ignore: use_to_and_as_if_applicable + Expression arrayFirst() { + return _ArrayFirstExpression(this); + } + + /// First [n] elements of this array expression. + Expression arrayFirstN(Object? n) { + return _ArrayFirstNExpression(this, _toExpression(n)); + } + + /// Last element of this array expression. + // ignore: use_to_and_as_if_applicable + Expression arrayLast() { + return _ArrayLastExpression(this); + } + + /// Last [n] elements of this array expression. + Expression arrayLastN(Object? n) { + return _ArrayLastNExpression(this, _toExpression(n)); + } + + /// Maximum element of this array (per-document), not the aggregate [maximum] accumulator. + // ignore: use_to_and_as_if_applicable + Expression arrayMaximum() { + return _ArrayMaximumExpression(this); + } + + /// The [n] largest elements of this array. + Expression arrayMaximumN(Object? n) { + return _ArrayMaximumNExpression(this, _toExpression(n)); + } + + /// Minimum element of this array (per-document), not the aggregate [minimum] accumulator. + // ignore: use_to_and_as_if_applicable + Expression arrayMinimum() { + return _ArrayMinimumExpression(this); + } + + /// The [n] smallest elements of this array. + Expression arrayMinimumN(Object? n) { + return _ArrayMinimumNExpression(this, _toExpression(n)); + } + + /// Index of the first occurrence of [element] in this array. + Expression arrayIndexOf(Object? element) { + return _ArrayIndexOfExpression(this, _toExpression(element), isLast: false); + } + + /// Index of the last occurrence of [element] in this array. + Expression arrayLastIndexOf(Object? element) { + return _ArrayIndexOfExpression(this, _toExpression(element), isLast: true); + } + + /// Array of all indices where [element] occurs in this array. + Expression arrayIndexOfAll(Object? element) { + return _ArrayIndexOfAllExpression(this, _toExpression(element)); + } + + /// Returns a slice of this array starting at [offset]. + /// + /// When [length] is provided, at most [length] elements are returned. + Expression arraySlice(Object? offset, [Object? length]) { + return _ArraySliceExpression( + this, + _toExpression(offset), + length == null ? null : _toExpression(length), + ); + } + + /// Filters this array by evaluating [filter] for each element bound to [alias]. + Expression arrayFilter(String alias, BooleanExpression filter) { + return _ArrayFilterExpression(this, alias, filter); + } + + /// Transforms each element of this array bound to [elementAlias]. + Expression arrayTransform(String elementAlias, Expression transform) { + return _ArrayTransformExpression(this, elementAlias, null, transform); + } + + /// Transforms each element of this array with both element and index aliases. + Expression arrayTransformWithIndex( + String elementAlias, + String indexAlias, + Expression transform, + ) { + return _ArrayTransformExpression( + this, + elementAlias, + indexAlias, + transform, + ); + } + + // ============================================================================ + // AGGREGATE FUNCTIONS + // ============================================================================ + + /// Creates a sum aggregation function from this expression + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction sum() { + return Sum(this); + } + + /// Creates an average aggregation function from this expression + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction average() { + return Average(this); + } + + /// Creates a count aggregation function from this expression + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction count() { + return Count(this); + } + + /// Creates a count distinct aggregation function from this expression + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction countDistinct() { + return CountDistinct(this); + } + + /// Creates a minimum aggregation function from this expression + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction minimum() { + return Minimum(this); + } + + /// Creates a maximum aggregation function from this expression + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction maximum() { + return Maximum(this); + } + + /// Aggregate: first value across inputs. See [First]. + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction first() { + return First(this); + } + + /// Aggregate: last value across inputs. See [Last]. + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction last() { + return Last(this); + } + + /// Aggregate: collect values into an array. See [ArrayAgg]. + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction arrayAgg() { + return ArrayAgg(this); + } + + /// Aggregate: collect distinct values into an array. See [ArrayAggDistinct]. + // ignore: use_to_and_as_if_applicable + PipelineAggregateFunction arrayAggDistinct() { + return ArrayAggDistinct(this); + } + + String get name; + + @override + Map toMap() { + return { + 'name': name, + }; + } + + // ============================================================================ + // STATIC FACTORY METHODS + // ============================================================================ + + /// Creates a constant expression from a string value + static Expression constantString(String value) => Constant(value); + + /// Creates a constant expression from a number value + static Expression constantNumber(num value) => Constant(value); + + /// Creates a constant expression from a boolean value + static Expression constantBoolean(bool value) => Constant(value); + + /// Creates a constant expression from a DateTime value + static Expression constantDateTime(DateTime value) => Constant(value); + + /// Creates a constant expression from a Timestamp value + static Expression constantTimestamp(Timestamp value) => Constant(value); + + /// Creates a constant expression from a GeoPoint value + static Expression constantGeoPoint(GeoPoint value) => Constant(value); + + /// Creates a constant expression from a Blob value + static Expression constantBlob(Blob value) => Constant(value); + + /// Creates a constant expression from a DocumentReference value + static Expression constantDocumentReference(DocumentReference value) => + Constant(value); + + /// Creates a constant expression from a byte array value + static Expression constantBytes(List value) => Constant(value); + + /// Creates a constant expression from a VectorValue value + static Expression constantVector(VectorValue value) => Constant(value); + + /// Creates a constant expression from any value (convenience) + /// + /// Valid types: String, num, bool, DateTime, Timestamp, GeoPoint, List (byte[]), + /// Blob, DocumentReference, VectorValue + static Expression constant(Object? value) { + if (value == null) { + return Constant(null); + } + // Validate that the value is one of the accepted types + if (value is! String && + value is! num && + value is! bool && + value is! DateTime && + value is! Timestamp && + value is! GeoPoint && + value is! List && + value is! Blob && + value is! DocumentReference && + value is! VectorValue) { + throw ArgumentError( + 'Constant value must be one of: String, num, bool, DateTime, Timestamp, ' + 'GeoPoint, List (byte[]), Blob, DocumentReference, or VectorValue. ' + 'Got: ${value.runtimeType}', + ); + } + return Constant(value); + } + + /// Creates a field reference expression from a field path string + static Field field(String fieldPath) => Field(fieldPath); + + /// Creates a field reference expression from a FieldPath object + static Field fieldPath(FieldPath fieldPath) => Field(fieldPath.toString()); + + /// Creates a null value expression + static Expression nullValue() => _NullExpression(); + + /// Creates a search expression that matches the whole document against + /// [query]. + static BooleanExpression documentMatches(String query) { + return _DocumentMatchesExpression(query); + } + + /// Creates a conditional (ternary) expression + static Expression conditional( + BooleanExpression condition, + Expression thenExpr, + Expression elseExpr, + ) { + return _ConditionalExpression(condition, thenExpr, elseExpr); + } + + /// Creates a conditional expression with literal values + static Expression conditionalValues( + BooleanExpression condition, + Object? thenValue, + Object? elseValue, + ) { + return _ConditionalExpression( + condition, + _toExpression(thenValue), + _toExpression(elseValue), + ); + } + + /// Creates an array expression from elements + static Expression array(List elements) { + return _ArrayExpression( + elements.map(_toExpression).toList(), + ); + } + + /// Creates a map expression from key-value pairs + static Expression map(Map data) { + return _MapExpression(data.map((k, v) => MapEntry(k, _toExpression(v)))); + } + + /// Returns the current timestamp + static Expression currentTimestamp() { + return _CurrentTimestampExpression(); + } + + /// Adds time to a timestamp expression. + /// + /// [unit] must be one of: `microsecond`, `millisecond`, `second`, `minute`, + /// `hour`, `day`. + static Expression timestampAdd( + Expression timestamp, + String unit, + Expression amount, + ) { + _validateTimestampUnit(unit); + return _TimestampAddExpression(timestamp, unit, amount); + } + + /// Adds time to a timestamp with a literal amount. + /// + /// [unit] must be one of: `microsecond`, `millisecond`, `second`, `minute`, + /// `hour`, `day`. + static Expression timestampAddLiteral( + Expression timestamp, + String unit, + int amount, + ) { + _validateTimestampUnit(unit); + return _TimestampAddExpression(timestamp, unit, Constant(amount)); + } + + /// Subtracts time from a timestamp expression. + /// + /// [unit] must be one of: `microsecond`, `millisecond`, `second`, `minute`, + /// `hour`, `day`. + static Expression timestampSubtract( + Expression timestamp, + String unit, + Expression amount, + ) { + _validateTimestampUnit(unit); + return _TimestampSubtractExpression(timestamp, unit, amount); + } + + /// Subtracts time from a timestamp with a literal amount. + /// + /// [unit] must be one of: `microsecond`, `millisecond`, `second`, `minute`, + /// `hour`, `day`. + static Expression timestampSubtractLiteral( + Expression timestamp, + String unit, + int amount, + ) { + _validateTimestampUnit(unit); + return _TimestampSubtractExpression(timestamp, unit, Constant(amount)); + } + + /// Truncates a timestamp to a specific unit. + /// + /// [unit] must be one of: `microsecond`, `millisecond`, `second`, `minute`, + /// `hour`, `day`. + static Expression timestampTruncate( + Expression timestamp, + String unit, + ) { + _validateTimestampUnit(unit); + return _TimestampTruncateExpression(timestamp, unit); + } + + /// Creates a document ID expression from a DocumentReference + static Expression documentIdFromRef(DocumentReference docRef) { + return _DocumentIdFromRefExpression(docRef); + } + + /// Checks if a value is in a list (IN operator) + static BooleanExpression equalAny( + Expression value, + List values, + ) { + return _EqualAnyExpression(value, values.map(_toExpression).toList()); + } + + /// Checks if a value is not in a list (NOT IN operator) + static BooleanExpression notEqualAny( + Expression value, + List values, + ) { + return _NotEqualAnyExpression(value, values.map(_toExpression).toList()); + } + + /// Checks if a field exists in the document + static BooleanExpression existsField(String fieldName) { + return _ExistsExpression(Field(fieldName)); + } + + /// Returns an expression if another is absent + static Expression ifAbsentStatic( + Expression ifExpr, + Expression elseExpr, + ) { + return _IfAbsentExpression(ifExpr, elseExpr); + } + + /// Returns a value if an expression is absent + static Expression ifAbsentValueStatic( + Expression ifExpr, + Object? elseValue, + ) { + return _IfAbsentExpression(ifExpr, _toExpression(elseValue)); + } + + /// Checks if an expression is absent + static BooleanExpression isAbsentStatic(Expression value) { + return _IsAbsentExpression(value); + } + + /// Checks if a field is absent + static BooleanExpression isAbsentField(String fieldName) { + return _IsAbsentExpression(Field(fieldName)); + } + + /// Returns an expression if another errors + static Expression ifErrorStatic( + Expression tryExpr, + Expression catchExpr, + ) { + return _IfErrorExpression(tryExpr, catchExpr); + } + + /// Checks if an expression produces an error + static BooleanExpression isErrorStatic(Expression expr) { + return _IsErrorExpression(expr); + } + + /// Negates a boolean expression + static BooleanExpression not(BooleanExpression expression) { + return _NotExpression(expression); + } + + /// Combines boolean expressions with a logical XOR + static BooleanExpression xor( + BooleanExpression expression1, [ + BooleanExpression? expression2, + BooleanExpression? expression3, + BooleanExpression? expression4, + BooleanExpression? expression5, + BooleanExpression? expression6, + BooleanExpression? expression7, + BooleanExpression? expression8, + BooleanExpression? expression9, + BooleanExpression? expression10, + BooleanExpression? expression11, + BooleanExpression? expression12, + BooleanExpression? expression13, + BooleanExpression? expression14, + BooleanExpression? expression15, + BooleanExpression? expression16, + BooleanExpression? expression17, + BooleanExpression? expression18, + BooleanExpression? expression19, + BooleanExpression? expression20, + BooleanExpression? expression21, + BooleanExpression? expression22, + BooleanExpression? expression23, + BooleanExpression? expression24, + BooleanExpression? expression25, + BooleanExpression? expression26, + BooleanExpression? expression27, + BooleanExpression? expression28, + BooleanExpression? expression29, + BooleanExpression? expression30, + ]) { + final expressions = [expression1]; + if (expression2 != null) expressions.add(expression2); + if (expression3 != null) expressions.add(expression3); + if (expression4 != null) expressions.add(expression4); + if (expression5 != null) expressions.add(expression5); + if (expression6 != null) expressions.add(expression6); + if (expression7 != null) expressions.add(expression7); + if (expression8 != null) expressions.add(expression8); + if (expression9 != null) expressions.add(expression9); + if (expression10 != null) expressions.add(expression10); + if (expression11 != null) expressions.add(expression11); + if (expression12 != null) expressions.add(expression12); + if (expression13 != null) expressions.add(expression13); + if (expression14 != null) expressions.add(expression14); + if (expression15 != null) expressions.add(expression15); + if (expression16 != null) expressions.add(expression16); + if (expression17 != null) expressions.add(expression17); + if (expression18 != null) expressions.add(expression18); + if (expression19 != null) expressions.add(expression19); + if (expression20 != null) expressions.add(expression20); + if (expression21 != null) expressions.add(expression21); + if (expression22 != null) expressions.add(expression22); + if (expression23 != null) expressions.add(expression23); + if (expression24 != null) expressions.add(expression24); + if (expression25 != null) expressions.add(expression25); + if (expression26 != null) expressions.add(expression26); + if (expression27 != null) expressions.add(expression27); + if (expression28 != null) expressions.add(expression28); + if (expression29 != null) expressions.add(expression29); + if (expression30 != null) expressions.add(expression30); + return _XorExpression(expressions); + } + + /// Combines boolean expressions with a logical AND + static BooleanExpression and( + BooleanExpression expression1, [ + BooleanExpression? expression2, + BooleanExpression? expression3, + BooleanExpression? expression4, + BooleanExpression? expression5, + BooleanExpression? expression6, + BooleanExpression? expression7, + BooleanExpression? expression8, + BooleanExpression? expression9, + BooleanExpression? expression10, + BooleanExpression? expression11, + BooleanExpression? expression12, + BooleanExpression? expression13, + BooleanExpression? expression14, + BooleanExpression? expression15, + BooleanExpression? expression16, + BooleanExpression? expression17, + BooleanExpression? expression18, + BooleanExpression? expression19, + BooleanExpression? expression20, + BooleanExpression? expression21, + BooleanExpression? expression22, + BooleanExpression? expression23, + BooleanExpression? expression24, + BooleanExpression? expression25, + BooleanExpression? expression26, + BooleanExpression? expression27, + BooleanExpression? expression28, + BooleanExpression? expression29, + BooleanExpression? expression30, + ]) { + final expressions = [expression1]; + if (expression2 != null) expressions.add(expression2); + if (expression3 != null) expressions.add(expression3); + if (expression4 != null) expressions.add(expression4); + if (expression5 != null) expressions.add(expression5); + if (expression6 != null) expressions.add(expression6); + if (expression7 != null) expressions.add(expression7); + if (expression8 != null) expressions.add(expression8); + if (expression9 != null) expressions.add(expression9); + if (expression10 != null) expressions.add(expression10); + if (expression11 != null) expressions.add(expression11); + if (expression12 != null) expressions.add(expression12); + if (expression13 != null) expressions.add(expression13); + if (expression14 != null) expressions.add(expression14); + if (expression15 != null) expressions.add(expression15); + if (expression16 != null) expressions.add(expression16); + if (expression17 != null) expressions.add(expression17); + if (expression18 != null) expressions.add(expression18); + if (expression19 != null) expressions.add(expression19); + if (expression20 != null) expressions.add(expression20); + if (expression21 != null) expressions.add(expression21); + if (expression22 != null) expressions.add(expression22); + if (expression23 != null) expressions.add(expression23); + if (expression24 != null) expressions.add(expression24); + if (expression25 != null) expressions.add(expression25); + if (expression26 != null) expressions.add(expression26); + if (expression27 != null) expressions.add(expression27); + if (expression28 != null) expressions.add(expression28); + if (expression29 != null) expressions.add(expression29); + if (expression30 != null) expressions.add(expression30); + return _AndExpression(expressions); + } + + /// Combines boolean expressions with a logical OR + static BooleanExpression or( + BooleanExpression expression1, [ + BooleanExpression? expression2, + BooleanExpression? expression3, + BooleanExpression? expression4, + BooleanExpression? expression5, + BooleanExpression? expression6, + BooleanExpression? expression7, + BooleanExpression? expression8, + BooleanExpression? expression9, + BooleanExpression? expression10, + BooleanExpression? expression11, + BooleanExpression? expression12, + BooleanExpression? expression13, + BooleanExpression? expression14, + BooleanExpression? expression15, + BooleanExpression? expression16, + BooleanExpression? expression17, + BooleanExpression? expression18, + BooleanExpression? expression19, + BooleanExpression? expression20, + BooleanExpression? expression21, + BooleanExpression? expression22, + BooleanExpression? expression23, + BooleanExpression? expression24, + BooleanExpression? expression25, + BooleanExpression? expression26, + BooleanExpression? expression27, + BooleanExpression? expression28, + BooleanExpression? expression29, + BooleanExpression? expression30, + ]) { + final expressions = [expression1]; + if (expression2 != null) expressions.add(expression2); + if (expression3 != null) expressions.add(expression3); + if (expression4 != null) expressions.add(expression4); + if (expression5 != null) expressions.add(expression5); + if (expression6 != null) expressions.add(expression6); + if (expression7 != null) expressions.add(expression7); + if (expression8 != null) expressions.add(expression8); + if (expression9 != null) expressions.add(expression9); + if (expression10 != null) expressions.add(expression10); + if (expression11 != null) expressions.add(expression11); + if (expression12 != null) expressions.add(expression12); + if (expression13 != null) expressions.add(expression13); + if (expression14 != null) expressions.add(expression14); + if (expression15 != null) expressions.add(expression15); + if (expression16 != null) expressions.add(expression16); + if (expression17 != null) expressions.add(expression17); + if (expression18 != null) expressions.add(expression18); + if (expression19 != null) expressions.add(expression19); + if (expression20 != null) expressions.add(expression20); + if (expression21 != null) expressions.add(expression21); + if (expression22 != null) expressions.add(expression22); + if (expression23 != null) expressions.add(expression23); + if (expression24 != null) expressions.add(expression24); + if (expression25 != null) expressions.add(expression25); + if (expression26 != null) expressions.add(expression26); + if (expression27 != null) expressions.add(expression27); + if (expression28 != null) expressions.add(expression28); + if (expression29 != null) expressions.add(expression29); + if (expression30 != null) expressions.add(expression30); + return _OrExpression(expressions); + } + + /// Joins array elements with a delimiter + static Expression joinStatic( + Expression arrayExpression, + Expression delimiterExpression, + ) { + return _JoinExpression(arrayExpression, delimiterExpression); + } + + /// Joins array elements with a literal delimiter + static Expression joinStaticLiteral( + Expression arrayExpression, + String delimiter, + ) { + return _JoinExpression(arrayExpression, Constant(delimiter)); + } + + /// Joins a field's array with a delimiter + static Expression joinField( + String arrayFieldName, + String delimiter, + ) { + return _JoinExpression(Field(arrayFieldName), Constant(delimiter)); + } + + /// Concatenates arrays + static Expression arrayConcatStatic( + Expression firstArray, + Expression secondArray, + List? otherArrays, + ) { + final expressions = [firstArray, secondArray]; + if (otherArrays != null) { + for (final other in otherArrays) { + expressions.add(_toExpression(other)); + } + } + return _ArrayConcatMultipleExpression(expressions); + } + + /// Returns the length of an expression + static Expression lengthStatic(Expression expr) { + return _LengthExpression(expr); + } + + /// Returns the length of a field + static Expression lengthField(String fieldName) { + return _LengthExpression(Field(fieldName)); + } + + /// Returns the absolute value of an expression + static Expression absStatic(Expression numericExpr) { + return _AbsExpression(numericExpr); + } + + /// Returns the absolute value of a field + static Expression absField(String numericField) { + return _AbsExpression(Field(numericField)); + } + + /// Adds two expressions + static Expression addStatic( + Expression first, + Expression second, + ) { + return _AddExpression(first, second); + } + + /// Adds an expression and a number + static Expression addStaticNumber( + Expression first, + num second, + ) { + return _AddExpression(first, Constant(second)); + } + + /// Adds a field and an expression + static Expression addField( + String numericFieldName, + Expression second, + ) { + return _AddExpression(Field(numericFieldName), second); + } + + /// Adds a field and a number + static Expression addFieldNumber( + String numericFieldName, + num second, + ) { + return _AddExpression(Field(numericFieldName), Constant(second)); + } + + /// Subtracts two expressions + static Expression subtractStatic( + Expression minuend, + Expression subtrahend, + ) { + return _SubtractExpression(minuend, subtrahend); + } + + /// Multiplies two expressions + static Expression multiplyStatic( + Expression multiplicand, + Expression multiplier, + ) { + return _MultiplyExpression(multiplicand, multiplier); + } + + /// Divides two expressions + static Expression divideStatic( + Expression dividend, + Expression divisor, + ) { + return _DivideExpression(dividend, divisor); + } + + /// Returns modulo of two expressions + static Expression moduloStatic( + Expression dividend, + Expression divisor, + ) { + return _ModuloExpression(dividend, divisor); + } + + /// Compares two expressions for equality + static BooleanExpression equalStatic( + Expression left, + Expression right, + ) { + return _EqualExpression(left, right); + } + + /// Compares expression with value for equality + static BooleanExpression equalStaticValue( + Expression left, + Object? right, + ) { + return _EqualExpression(left, _toExpression(right)); + } + + /// Compares field with value for equality + static BooleanExpression equalField( + String fieldName, + Object? value, + ) { + return _EqualExpression(Field(fieldName), _toExpression(value)); + } + + /// Compares two expressions for inequality + static BooleanExpression notEqualStatic( + Expression left, + Expression right, + ) { + return _NotEqualExpression(left, right); + } + + /// Compares expression with value for inequality + static BooleanExpression notEqualStaticValue( + Expression left, + Object? right, + ) { + return _NotEqualExpression(left, _toExpression(right)); + } + + /// Greater than comparison + static BooleanExpression greaterThanStatic( + Expression left, + Expression right, + ) { + return _GreaterThanExpression(left, right); + } + + /// Greater than comparison with value + static BooleanExpression greaterThanStaticValue( + Expression left, + Object? right, + ) { + return _GreaterThanExpression(left, _toExpression(right)); + } + + /// Greater than comparison for field + static BooleanExpression greaterThanField( + String fieldName, + Object? value, + ) { + return _GreaterThanExpression(Field(fieldName), _toExpression(value)); + } + + /// Greater than or equal comparison + static BooleanExpression greaterThanOrEqualStatic( + Expression left, + Expression right, + ) { + return _GreaterThanOrEqualExpression(left, right); + } + + /// Less than comparison + static BooleanExpression lessThanStatic( + Expression left, + Expression right, + ) { + return _LessThanExpression(left, right); + } + + /// Less than comparison with value + static BooleanExpression lessThanStaticValue( + Expression left, + Object? right, + ) { + return _LessThanExpression(left, _toExpression(right)); + } + + /// Less than comparison for field + static BooleanExpression lessThanField( + String fieldName, + Object? value, + ) { + return _LessThanExpression(Field(fieldName), _toExpression(value)); + } + + /// Less than or equal comparison + static BooleanExpression lessThanOrEqualStatic( + Expression left, + Expression right, + ) { + return _LessThanOrEqualExpression(left, right); + } + + /// Concatenates expressions + static Expression concatStatic( + Expression first, + Expression second, + List? others, + ) { + final expressions = [first, second]; + if (others != null) { + for (final other in others) { + expressions.add(_toExpression(other)); + } + } + return _ConcatExpression(expressions); + } + + /// Converts to lowercase + static Expression toLowerCaseStatic(Expression stringExpr) { + return _ToLowerCaseExpression(stringExpr); + } + + /// Converts field to lowercase + static Expression toLowerCaseField(String stringField) { + return _ToLowerCaseExpression(Field(stringField)); + } + + /// Converts to uppercase + static Expression toUpperCaseStatic(Expression stringExpr) { + return _ToUpperCaseExpression(stringExpr); + } + + /// Converts field to uppercase + static Expression toUpperCaseField(String stringField) { + return _ToUpperCaseExpression(Field(stringField)); + } + + /// Trims whitespace + static Expression trimStatic(Expression stringExpr) { + return _TrimExpression(stringExpr); + } + + /// Trims field whitespace + static Expression trimField(String stringField) { + return _TrimExpression(Field(stringField)); + } + + /// Extracts substring + static Expression substringStatic( + Expression stringExpr, + Expression start, + Expression end, + ) { + return _SubstringExpression(stringExpr, start, end); + } + + /// Replaces all occurrences in string (stringReplaceAll) + static Expression stringReplaceAllStatic( + Expression stringExpr, + Expression find, + Expression replacement, + ) { + return _StringReplaceAllExpression(stringExpr, find, replacement); + } + + /// Splits string + static Expression splitStatic( + Expression stringExpr, + Expression delimiter, + ) { + return _SplitExpression(stringExpr, delimiter); + } + + /// Reverses array + static Expression arrayReverseStatic(Expression array) { + return _ArrayReverseExpression(array); + } + + /// Reverses field array + static Expression arrayReverseField(String arrayFieldName) { + return _ArrayReverseExpression(Field(arrayFieldName)); + } + + /// Sums array + static Expression arraySumStatic(Expression array) { + return _ArraySumExpression(array); + } + + /// Sums field array + static Expression arraySumField(String arrayFieldName) { + return _ArraySumExpression(Field(arrayFieldName)); + } + + /// Gets array length + static Expression arrayLengthStatic(Expression array) { + return _ArrayLengthExpression(array); + } + + /// Gets field array length + static Expression arrayLengthField(String arrayFieldName) { + return _ArrayLengthExpression(Field(arrayFieldName)); + } + + /// Checks array contains + static BooleanExpression arrayContainsElementStatic( + Expression array, + Expression element, + ) { + return _ArrayContainsExpression(array, element); + } + + /// Checks field array contains + static BooleanExpression arrayContainsField( + String arrayFieldName, + Object? element, + ) { + return _ArrayContainsExpression( + Field(arrayFieldName), + _toExpression(element), + ); + } + + /// Checks if [array] contains all elements of [arrayExpression] + /// (e.g. [arrayExpression] can be [Expression.array] of fields/literals). + static BooleanExpression arrayContainsAllWithExpression( + Expression array, + Expression arrayExpression, + ) { + return _ArrayContainsAllExpression(array, arrayExpression); + } + + /// Checks if [array] contains all of the specified [values]. + static BooleanExpression arrayContainsAllValues( + Expression array, + List values, + ) { + return _ArrayContainsAllValuesExpression( + array, + values.map(_toExpression).toList(), + ); + } + + /// Checks if the array field [arrayFieldName] contains all elements of + /// [arrayExpression]. + static BooleanExpression arrayContainsAllField( + String arrayFieldName, + Expression arrayExpression, + ) { + return arrayContainsAllWithExpression( + Field(arrayFieldName), + arrayExpression, + ); + } + + /// Returns a slice of [array] starting at [offset]. + static Expression arraySliceStatic( + Expression array, + Object? offset, [ + Object? length, + ]) { + return _ArraySliceExpression( + array, + _toExpression(offset), + length == null ? null : _toExpression(length), + ); + } + + /// Filters [array] by evaluating [filter] for each element bound to [alias]. + static Expression arrayFilterStatic( + Expression array, + String alias, + BooleanExpression filter, + ) { + return _ArrayFilterExpression(array, alias, filter); + } + + /// Transforms each element of [array] bound to [elementAlias]. + static Expression arrayTransformStatic( + Expression array, + String elementAlias, + Expression transform, + ) { + return _ArrayTransformExpression(array, elementAlias, null, transform); + } + + /// Transforms each element of [array] with both element and index aliases. + static Expression arrayTransformWithIndexStatic( + Expression array, + String elementAlias, + String indexAlias, + Expression transform, + ) { + return _ArrayTransformExpression( + array, + elementAlias, + indexAlias, + transform, + ); + } + + /// Creates a raw/custom function expression + static Expression rawFunction( + String name, + List args, + ) { + return _RawFunctionExpression(name, args); + } + + /// A random value between 0 (inclusive) and 1 (exclusive) per evaluation + /// (Firestore pipeline `rand`). + static Expression rand() { + return _RandExpression(); + } + + /// Same as [Expression.isType] but usable as a static helper for any [expression]. + static BooleanExpression isTypeStatic( + Expression expression, + Type valueType, + ) { + return _IsTypeExpression(expression, valueType); + } +} + +/// Base class for function expressions +abstract class FunctionExpression extends Expression {} + +/// Base class for selectable expressions (can be used in select stage) +abstract class Selectable extends Expression { + String get aliasName; + Expression get expression; +} + +/// Represents an aliased expression wrapper +class AliasedExpression extends Selectable { + final String _alias; + + @override + String get aliasName => _alias; + + @override + final Expression expression; + + AliasedExpression({ + required String alias, + required this.expression, + }) : _alias = alias; + + @override + String get name => 'alias'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'alias': _alias, + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a field reference in a pipeline expression +class Field extends Selectable { + final String fieldName; + + Field(this.fieldName); + + @override + String get name => 'field'; + + @override + String get aliasName => fieldName; + + @override + Expression get expression => this; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'field': fieldName, + }, + }; + } +} + +/// Represents a null value expression +class _NullExpression extends Expression { + _NullExpression(); + + @override + String get name => 'null'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'value': null, + }, + }; + } +} + +/// Represents a constant value in a pipeline expression +/// +/// Valid types: String, num, bool, DateTime, Timestamp, GeoPoint, List (byte[]), +/// Blob, DocumentReference, VectorValue, or null +class Constant extends Expression { + final Object? value; + + Constant(this.value) { + if (value != null) { + // Validate that the value is one of the accepted types + if (value is! String && + value is! num && + value is! bool && + value is! DateTime && + value is! Timestamp && + value is! GeoPoint && + value is! List && + value is! Blob && + value is! DocumentReference && + value is! VectorValue) { + throw ArgumentError( + 'Constant value must be one of: String, num, bool, DateTime, Timestamp, ' + 'GeoPoint, List (byte[]), Blob, DocumentReference, or VectorValue. ' + 'Got: ${value.runtimeType}', + ); + } + } + } + + @override + String get name => 'constant'; + + @override + Map toMap() { + Object? serializedValue = value; + if (value is DocumentReference) { + serializedValue = {'path': (value! as DocumentReference).path}; + } + return { + 'name': name, + 'args': {'value': serializedValue}, + }; + } +} + +/// Represents a concatenation function expression +class Concat extends FunctionExpression { + final List expressions; + + Concat(this.expressions); + + @override + String get name => 'concat'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expressions': expressions.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Represents a concat function expression (internal) +class _ConcatExpression extends FunctionExpression { + final List expressions; + + _ConcatExpression(this.expressions); + + @override + String get name => 'concat'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expressions': expressions.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Represents a length function expression +class _LengthExpression extends FunctionExpression { + final Expression expression; + + _LengthExpression(this.expression); + + @override + String get name => 'length'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a toLowerCase function expression +class _ToLowerCaseExpression extends FunctionExpression { + final Expression expression; + + _ToLowerCaseExpression(this.expression); + + @override + String get name => 'to_lower_case'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a toUpperCase function expression +class _ToUpperCaseExpression extends FunctionExpression { + final Expression expression; + + _ToUpperCaseExpression(this.expression); + + @override + String get name => 'to_upper_case'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a substring function expression +class _SubstringExpression extends FunctionExpression { + final Expression expression; + final Expression start; + final Expression end; + + _SubstringExpression(this.expression, this.start, this.end); + + @override + String get name => 'substring'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'start': start.toMap(), + 'end': end.toMap(), + }, + }; + } +} + +/// Represents a string_replace_all function expression +class _StringReplaceAllExpression extends FunctionExpression { + final Expression expression; + final Expression find; + final Expression replacement; + + _StringReplaceAllExpression(this.expression, this.find, this.replacement); + + @override + String get name => 'string_replace_all'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'find': find.toMap(), + 'replacement': replacement.toMap(), + }, + }; + } +} + +/// Represents a split function expression +class _SplitExpression extends FunctionExpression { + final Expression expression; + final Expression delimiter; + + _SplitExpression(this.expression, this.delimiter); + + @override + String get name => 'split'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'delimiter': delimiter.toMap(), + }, + }; + } +} + +/// Represents a join function expression +class _JoinExpression extends FunctionExpression { + final Expression expression; + final Expression delimiter; + + _JoinExpression(this.expression, this.delimiter); + + @override + String get name => 'join'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'delimiter': delimiter.toMap(), + }, + }; + } +} + +/// Represents a trim function expression +class _TrimExpression extends FunctionExpression { + final Expression expression; + + _TrimExpression(this.expression); + + @override + String get name => 'trim'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Base class for boolean expressions used in filtering +abstract class BooleanExpression extends Expression {} + +/// Represents a document_matches search expression. +class _DocumentMatchesExpression extends BooleanExpression { + final String query; + + _DocumentMatchesExpression(this.query); + + @override + String get name => 'document_matches'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'query': query, + }, + }; + } +} + +// ============================================================================ +// PATTERN DEMONSTRATION - Concrete Function Expression Classes +// ============================================================================ + +/// Represents an addition function expression +class _AddExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _AddExpression(this.left, this.right); + + @override + String get name => 'add'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a subtraction function expression +class _SubtractExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _SubtractExpression(this.left, this.right); + + @override + String get name => 'subtract'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents an equality comparison function expression +class _EqualExpression extends BooleanExpression { + final Expression left; + final Expression right; + + _EqualExpression(this.left, this.right); + + @override + String get name => 'equal'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a greater-than comparison function expression +class _GreaterThanExpression extends BooleanExpression { + final Expression left; + final Expression right; + + _GreaterThanExpression(this.left, this.right); + + @override + String get name => 'greater_than'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a multiply function expression +class _MultiplyExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _MultiplyExpression(this.left, this.right); + + @override + String get name => 'multiply'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a divide function expression +class _DivideExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _DivideExpression(this.left, this.right); + + @override + String get name => 'divide'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a modulo function expression +class _ModuloExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _ModuloExpression(this.left, this.right); + + @override + String get name => 'modulo'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents an absolute value function expression +class _AbsExpression extends FunctionExpression { + final Expression expression; + + _AbsExpression(this.expression); + + @override + String get name => 'abs'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a negation function expression +/// Represents a not-equal comparison function expression +class _NotEqualExpression extends BooleanExpression { + final Expression left; + final Expression right; + + _NotEqualExpression(this.left, this.right); + + @override + String get name => 'not_equal'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a greater-than-or-equal comparison function expression +class _GreaterThanOrEqualExpression extends BooleanExpression { + final Expression left; + final Expression right; + + _GreaterThanOrEqualExpression(this.left, this.right); + + @override + String get name => 'greater_than_or_equal'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a less-than comparison function expression +class _LessThanExpression extends BooleanExpression { + final Expression left; + final Expression right; + + _LessThanExpression(this.left, this.right); + + @override + String get name => 'less_than'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a less-than-or-equal comparison function expression +class _LessThanOrEqualExpression extends BooleanExpression { + final Expression left; + final Expression right; + + _LessThanOrEqualExpression(this.left, this.right); + + @override + String get name => 'less_than_or_equal'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +// ============================================================================ +// ARRAY OPERATION EXPRESSION CLASSES +// ============================================================================ + +/// Represents an array concat function expression +class _ArrayConcatExpression extends FunctionExpression { + final Expression firstArray; + final Expression secondArray; + + _ArrayConcatExpression(this.firstArray, this.secondArray); + + @override + String get name => 'array_concat'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'first': firstArray.toMap(), + 'second': secondArray.toMap(), + }, + }; + } +} + +/// Represents an array concat multiple function expression +class _ArrayConcatMultipleExpression extends FunctionExpression { + final List arrays; + + _ArrayConcatMultipleExpression(this.arrays); + + @override + String get name => 'array_concat_multiple'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'arrays': arrays.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Represents an array contains function expression +class _ArrayContainsExpression extends BooleanExpression { + final Expression array; + final Expression element; + + _ArrayContainsExpression(this.array, this.element); + + @override + String get name => 'array_contains'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'array': array.toMap(), + 'element': element.toMap(), + }, + }; + } +} + +/// Represents an arrayContainsAny function expression +class _ArrayContainsAnyExpression extends BooleanExpression { + final Expression array; + final List values; + + _ArrayContainsAnyExpression(this.array, this.values); + + @override + String get name => 'array_contains_any'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'array': array.toMap(), + 'values': values.map((v) => v.toMap()).toList(), + }, + }; + } +} + +/// Represents an arrayContainsAll function expression (array + list of values) +class _ArrayContainsAllValuesExpression extends BooleanExpression { + final Expression array; + final List values; + + _ArrayContainsAllValuesExpression(this.array, this.values); + + @override + String get name => 'array_contains_all'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'array': array.toMap(), + 'values': values.map((v) => v.toMap()).toList(), + }, + }; + } +} + +/// Represents an arrayContainsAll function expression (array + array expression) +class _ArrayContainsAllExpression extends BooleanExpression { + final Expression array; + final Expression arrayExpression; + + _ArrayContainsAllExpression(this.array, this.arrayExpression); + + @override + String get name => 'array_contains_all'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'array': array.toMap(), + 'array_expression': arrayExpression.toMap(), + }, + }; + } +} + +/// Represents an array length function expression +class _ArrayLengthExpression extends FunctionExpression { + final Expression expression; + + _ArrayLengthExpression(this.expression); + + @override + String get name => 'array_length'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents an array reverse function expression +class _ArrayReverseExpression extends FunctionExpression { + final Expression expression; + + _ArrayReverseExpression(this.expression); + + @override + String get name => 'array_reverse'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents an array sum function expression +class _ArraySumExpression extends FunctionExpression { + final Expression expression; + + _ArraySumExpression(this.expression); + + @override + String get name => 'array_sum'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents an array slice expression. +class _ArraySliceExpression extends FunctionExpression { + final Expression expression; + final Expression offset; + final Expression? sliceLength; + + _ArraySliceExpression(this.expression, this.offset, this.sliceLength); + + @override + String get name => 'array_slice'; + + @override + Map toMap() { + final args = { + 'expression': expression.toMap(), + 'offset': offset.toMap(), + }; + if (sliceLength != null) { + args['length'] = sliceLength!.toMap(); + } + return { + 'name': name, + 'args': args, + }; + } +} + +/// Represents an array filter expression. +class _ArrayFilterExpression extends FunctionExpression { + final Expression expression; + final String elementAlias; + final BooleanExpression filter; + + _ArrayFilterExpression(this.expression, this.elementAlias, this.filter); + + @override + String get name => 'array_filter'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'alias': elementAlias, + 'filter': filter.toMap(), + }, + }; + } +} + +/// Represents an array transform expression. +class _ArrayTransformExpression extends FunctionExpression { + final Expression expression; + final String elementAlias; + final String? indexAlias; + final Expression transform; + + _ArrayTransformExpression( + this.expression, + this.elementAlias, + this.indexAlias, + this.transform, + ); + + @override + String get name => + indexAlias == null ? 'array_transform' : 'array_transform_with_index'; + + @override + Map toMap() { + final args = { + 'expression': expression.toMap(), + 'element_alias': elementAlias, + 'transform': transform.toMap(), + }; + if (indexAlias != null) { + args['index_alias'] = indexAlias; + } + return { + 'name': name, + 'args': args, + }; + } +} + +// ============================================================================ +// CONDITIONAL / LOGIC OPERATION EXPRESSION CLASSES +// ============================================================================ + +/// Represents an ifAbsent function expression +class _IfAbsentExpression extends FunctionExpression { + final Expression expression; + final Expression elseExpr; + + _IfAbsentExpression(this.expression, this.elseExpr); + + @override + String get name => 'if_absent'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'else': elseExpr.toMap(), + }, + }; + } +} + +/// Represents an ifError function expression +class _IfErrorExpression extends Expression { + final Expression expression; + final Expression catchExpr; + + _IfErrorExpression(this.expression, this.catchExpr); + + @override + String get name => 'if_error'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'catch': catchExpr.toMap(), + }, + }; + } +} + +/// Represents an isAbsent function expression +class _IsAbsentExpression extends BooleanExpression { + final Expression expression; + + _IsAbsentExpression(this.expression); + + @override + String get name => 'is_absent'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents an isError function expression +class _IsErrorExpression extends BooleanExpression { + final Expression expression; + + _IsErrorExpression(this.expression); + + @override + String get name => 'is_error'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents an exists function expression +class _ExistsExpression extends BooleanExpression { + final Expression expression; + + _ExistsExpression(this.expression); + + @override + String get name => 'exists'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a not (negation) function expression +class _NotExpression extends BooleanExpression { + final BooleanExpression expression; + + _NotExpression(this.expression); + + @override + String get name => 'not'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +class _XorExpression extends BooleanExpression { + final List expressions; + + _XorExpression(this.expressions); + + @override + String get name => 'xor'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expressions': expressions.map((e) => e.toMap()).toList(), + }, + }; + } +} + +class _AndExpression extends BooleanExpression { + final List expressions; + + _AndExpression(this.expressions); + + @override + String get name => 'and'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expressions': expressions.map((e) => e.toMap()).toList(), + }, + }; + } +} + +class _OrExpression extends BooleanExpression { + final List expressions; + + _OrExpression(this.expressions); + + @override + String get name => 'or'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expressions': expressions.map((e) => e.toMap()).toList(), + }, + }; + } +} + +/// Represents a conditional (ternary) function expression +class _ConditionalExpression extends FunctionExpression { + final BooleanExpression condition; + final Expression thenExpr; + final Expression elseExpr; + + _ConditionalExpression(this.condition, this.thenExpr, this.elseExpr); + + @override + String get name => 'conditional'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'condition': condition.toMap(), + 'then': thenExpr.toMap(), + 'else': elseExpr.toMap(), + }, + }; + } +} + +// ============================================================================ +// TYPE CONVERSION EXPRESSION CLASSES +// ============================================================================ + +/// Represents an asBoolean function expression +class _AsBooleanExpression extends BooleanExpression { + final Expression expression; + + _AsBooleanExpression(this.expression); + + @override + String get name => 'as_boolean'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +// ============================================================================ +// BITWISE OPERATION EXPRESSION CLASSES +// ============================================================================ + +/// Represents a bitAnd function expression +class _BitAndExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _BitAndExpression(this.left, this.right); + + @override + String get name => 'bit_and'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a bitOr function expression +class _BitOrExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _BitOrExpression(this.left, this.right); + + @override + String get name => 'bit_or'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a bitXor function expression +class _BitXorExpression extends FunctionExpression { + final Expression left; + final Expression right; + + _BitXorExpression(this.left, this.right); + + @override + String get name => 'bit_xor'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'left': left.toMap(), + 'right': right.toMap(), + }, + }; + } +} + +/// Represents a bitNot function expression +class _BitNotExpression extends FunctionExpression { + final Expression expression; + + _BitNotExpression(this.expression); + + @override + String get name => 'bit_not'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a bitLeftShift function expression +class _BitLeftShiftExpression extends FunctionExpression { + final Expression expression; + final Expression amount; + + _BitLeftShiftExpression(this.expression, this.amount); + + @override + String get name => 'bit_left_shift'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'amount': amount.toMap(), + }, + }; + } +} + +/// Represents a bitRightShift function expression +class _BitRightShiftExpression extends FunctionExpression { + final Expression expression; + final Expression amount; + + _BitRightShiftExpression(this.expression, this.amount); + + @override + String get name => 'bit_right_shift'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'amount': amount.toMap(), + }, + }; + } +} + +// ============================================================================ +// DOCUMENT / PATH OPERATION EXPRESSION CLASSES +// ============================================================================ + +/// Represents a documentId function expression +class _DocumentIdExpression extends FunctionExpression { + final Expression expression; + + _DocumentIdExpression(this.expression); + + @override + String get name => 'document_id'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a collectionId function expression +class _CollectionIdExpression extends FunctionExpression { + final Expression expression; + + _CollectionIdExpression(this.expression); + + @override + String get name => 'collection_id'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Represents a documentIdFromRef function expression +class _DocumentIdFromRefExpression extends FunctionExpression { + final DocumentReference docRef; + + _DocumentIdFromRefExpression(this.docRef); + + @override + String get name => 'document_id_from_ref'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'doc_ref': docRef.path, + }, + }; + } +} + +// ============================================================================ +// MAP OPERATION EXPRESSION CLASSES +// ============================================================================ + +/// Represents a mapGet function expression +class _MapGetExpression extends FunctionExpression { + final Expression map; + final Expression key; + + _MapGetExpression(this.map, this.key); + + @override + String get name => 'map_get'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'map': map.toMap(), + 'key': key.toMap(), + }, + }; + } +} + +// ============================================================================ +// TIMESTAMP OPERATION EXPRESSION CLASSES +// ============================================================================ + +/// Represents a currentTimestamp function expression +class _CurrentTimestampExpression extends FunctionExpression { + _CurrentTimestampExpression(); + + @override + String get name => 'current_timestamp'; + + @override + Map toMap() { + return { + 'name': name, + }; + } +} + +/// Represents a timestamp_add function expression. +/// Unit must be one of: microsecond, millisecond, second, minute, hour, day. +class _TimestampAddExpression extends FunctionExpression { + final Expression timestamp; + final String unit; + final Expression amount; + + _TimestampAddExpression(this.timestamp, this.unit, this.amount); + + @override + String get name => 'timestamp_add'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'timestamp': timestamp.toMap(), + 'unit': unit, + 'amount': amount.toMap(), + }, + }; + } +} + +/// Represents a timestamp_subtract function expression. +/// Unit must be one of: microsecond, millisecond, second, minute, hour, day. +class _TimestampSubtractExpression extends FunctionExpression { + final Expression timestamp; + final String unit; + final Expression amount; + + _TimestampSubtractExpression(this.timestamp, this.unit, this.amount); + + @override + String get name => 'timestamp_subtract'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'timestamp': timestamp.toMap(), + 'unit': unit, + 'amount': amount.toMap(), + }, + }; + } +} + +/// Represents a timestamp_truncate function expression. +/// Unit must be one of: microsecond, millisecond, second, minute, hour, day. +class _TimestampTruncateExpression extends FunctionExpression { + final Expression timestamp; + final String unit; + + _TimestampTruncateExpression(this.timestamp, this.unit); + + @override + String get name => 'timestamp_truncate'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'timestamp': timestamp.toMap(), + 'unit': unit, + }, + }; + } +} + +// ============================================================================ +// SPECIAL OPERATION EXPRESSION CLASSES +// ============================================================================ + +/// Represents an equalAny (IN) function expression +class _EqualAnyExpression extends BooleanExpression { + final Expression value; + final List values; + + _EqualAnyExpression(this.value, this.values); + + @override + String get name => 'equal_any'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'value': value.toMap(), + 'values': values.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Represents a notEqualAny (NOT IN) function expression +class _NotEqualAnyExpression extends BooleanExpression { + final Expression value; + final List values; + + _NotEqualAnyExpression(this.value, this.values); + + @override + String get name => 'not_equal_any'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'value': value.toMap(), + 'values': values.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Represents an array expression +class _ArrayExpression extends FunctionExpression { + final List elements; + + _ArrayExpression(this.elements); + + @override + String get name => 'array'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'elements': elements.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Represents a map expression +class _MapExpression extends FunctionExpression { + final Map data; + + _MapExpression(this.data); + + @override + String get name => 'map'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'data': data.map((k, v) => MapEntry(k, v.toMap())), + }, + }; + } +} + +/// Serialized pipeline function `map_set` (map plus alternating key/value pairs). +class _MapSetExpression extends FunctionExpression { + final Expression map; + final List keyValues; + + _MapSetExpression(this.map, this.keyValues) { + if (keyValues.isEmpty || keyValues.length.isOdd) { + throw ArgumentError('mapSet requires one or more key/value pairs'); + } + } + + @override + String get name => 'map_set'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'map': map.toMap(), + 'key_values': keyValues.map((e) => e.toMap()).toList(), + }, + }; + } +} + +/// Serialized pipeline function `map_entries`. +class _MapEntriesExpression extends FunctionExpression { + final Expression expression; + + _MapEntriesExpression(this.expression); + + @override + String get name => 'map_entries'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `regex_find`. +class _RegexFindExpression extends FunctionExpression { + final Expression expression; + final Expression pattern; + + _RegexFindExpression(this.expression, this.pattern); + + @override + String get name => 'regex_find'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'pattern': pattern.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `regex_find_all`. +class _RegexFindAllExpression extends FunctionExpression { + final Expression expression; + final Expression pattern; + + _RegexFindAllExpression(this.expression, this.pattern); + + @override + String get name => 'regex_find_all'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'pattern': pattern.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `string_replace_one`. +class _StringReplaceOneExpression extends FunctionExpression { + final Expression expression; + final Expression find; + final Expression replacement; + + _StringReplaceOneExpression(this.expression, this.find, this.replacement); + + @override + String get name => 'string_replace_one'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'find': find.toMap(), + 'replacement': replacement.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `string_index_of`. +class _StringIndexOfExpression extends FunctionExpression { + final Expression expression; + final Expression search; + + _StringIndexOfExpression(this.expression, this.search); + + @override + String get name => 'string_index_of'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'search': search.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `string_repeat`. +class _StringRepeatExpression extends FunctionExpression { + final Expression expression; + final Expression repetitions; + + _StringRepeatExpression(this.expression, this.repetitions); + + @override + String get name => 'string_repeat'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'repetitions': repetitions.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `ltrim`. +class _LtrimExpression extends FunctionExpression { + final Expression expression; + final Expression? value; + + _LtrimExpression(this.expression, this.value); + + @override + String get name => 'ltrim'; + + @override + Map toMap() { + final args = { + 'expression': expression.toMap(), + }; + if (value != null) { + args['value'] = value!.toMap(); + } + return { + 'name': name, + 'args': args, + }; + } +} + +/// Serialized pipeline function `rtrim`. +class _RtrimExpression extends FunctionExpression { + final Expression expression; + final Expression? value; + + _RtrimExpression(this.expression, this.value); + + @override + String get name => 'rtrim'; + + @override + Map toMap() { + final args = { + 'expression': expression.toMap(), + }; + if (value != null) { + args['value'] = value!.toMap(); + } + return { + 'name': name, + 'args': args, + }; + } +} + +/// Serialized pipeline function `type` (runtime type name of the value). +class _TypeExpression extends FunctionExpression { + final Expression expression; + + _TypeExpression(this.expression); + + @override + String get name => 'type'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Serialized pipeline boolean function `is_type`. +class _IsTypeExpression extends BooleanExpression { + final Expression expression; + final Type valueType; + + _IsTypeExpression(this.expression, this.valueType); + + @override + String get name => 'is_type'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'type': valueType.typeValue, + }, + }; + } +} + +/// Serialized pipeline function `trunc`. +class _TruncExpression extends FunctionExpression { + final Expression expression; + final Expression? decimals; + + _TruncExpression(this.expression, this.decimals); + + @override + String get name => 'trunc'; + + @override + Map toMap() { + final args = { + 'expression': expression.toMap(), + }; + if (decimals != null) { + args['decimals'] = decimals!.toMap(); + } + return { + 'name': name, + 'args': args, + }; + } +} + +/// Serialized pipeline function `rand`. +class _RandExpression extends FunctionExpression { + _RandExpression(); + + @override + String get name => 'rand'; + + @override + Map toMap() { + return { + 'name': name, + 'args': {}, + }; + } +} + +/// Serialized pipeline function `array_first`. +class _ArrayFirstExpression extends FunctionExpression { + final Expression expression; + + _ArrayFirstExpression(this.expression); + + @override + String get name => 'array_first'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `array_first_n`. +class _ArrayFirstNExpression extends FunctionExpression { + final Expression expression; + final Expression n; + + _ArrayFirstNExpression(this.expression, this.n); + + @override + String get name => 'array_first_n'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'n': n.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `array_last`. +class _ArrayLastExpression extends FunctionExpression { + final Expression expression; + + _ArrayLastExpression(this.expression); + + @override + String get name => 'array_last'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `array_last_n`. +class _ArrayLastNExpression extends FunctionExpression { + final Expression expression; + final Expression n; + + _ArrayLastNExpression(this.expression, this.n); + + @override + String get name => 'array_last_n'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'n': n.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `maximum` on an array value (not the aggregate). +class _ArrayMaximumExpression extends FunctionExpression { + final Expression expression; + + _ArrayMaximumExpression(this.expression); + + @override + String get name => 'maximum'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `maximum_n`. +class _ArrayMaximumNExpression extends FunctionExpression { + final Expression expression; + final Expression n; + + _ArrayMaximumNExpression(this.expression, this.n); + + @override + String get name => 'maximum_n'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'n': n.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `minimum` on an array value (not the aggregate). +class _ArrayMinimumExpression extends FunctionExpression { + final Expression expression; + + _ArrayMinimumExpression(this.expression); + + @override + String get name => 'minimum'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `minimum_n`. +class _ArrayMinimumNExpression extends FunctionExpression { + final Expression expression; + final Expression n; + + _ArrayMinimumNExpression(this.expression, this.n); + + @override + String get name => 'minimum_n'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'n': n.toMap(), + }, + }; + } +} + +/// Serialized pipeline function `array_index_of` (first or last occurrence). +class _ArrayIndexOfExpression extends FunctionExpression { + final Expression expression; + final Expression element; + final bool isLast; + + _ArrayIndexOfExpression( + this.expression, + this.element, { + required this.isLast, + }); + + @override + String get name => 'array_index_of'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'element': element.toMap(), + 'occurrence': Constant(isLast ? 'last' : 'first').toMap(), + }, + }; + } +} + +/// Serialized pipeline function `array_index_of_all`. +class _ArrayIndexOfAllExpression extends FunctionExpression { + final Expression expression; + final Expression element; + + _ArrayIndexOfAllExpression(this.expression, this.element); + + @override + String get name => 'array_index_of_all'; + + @override + Map toMap() { + return { + 'name': name, + 'args': { + 'expression': expression.toMap(), + 'element': element.toMap(), + }, + }; + } +} + +/// Represents a raw function expression +class _RawFunctionExpression extends FunctionExpression { + final String functionName; + final List args; + + _RawFunctionExpression(this.functionName, this.args); + + @override + String get name => functionName; + + @override + Map toMap() { + return { + 'name': name, + 'args': args.map((expr) => expr.toMap()).toList(), + }; + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_ordering.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_ordering.dart new file mode 100644 index 000000000000..b55687fa5d8b --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_ordering.dart @@ -0,0 +1,30 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Direction for ordering results +enum OrderDirection { + /// Ascending order + asc, + + /// Descending order + desc, +} + +/// Represents an ordering specification for pipeline sorting +class Ordering implements PipelineSerializable { + final Expression expression; + final OrderDirection direction; + + Ordering(this.expression, this.direction); + + @override + Map toMap() { + return { + 'expression': expression.toMap(), + 'order_direction': direction == OrderDirection.asc ? 'asc' : 'desc', + }; + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_sample.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_sample.dart new file mode 100644 index 000000000000..4136c9922872 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_sample.dart @@ -0,0 +1,43 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Base class for pipeline sampling strategies +abstract class PipelineSample implements PipelineSerializable { + const PipelineSample(); + + /// Creates a sample with a fixed size + factory PipelineSample.withSize(int size) = _PipelineSampleSize; + + /// Creates a sample with a percentage + factory PipelineSample.withPercentage(double percentage) = + _PipelineSamplePercentage; +} + +/// Sample stage with a fixed size +class _PipelineSampleSize extends PipelineSample { + final int size; + + const _PipelineSampleSize(this.size); + + @override + Map toMap() => { + 'type': 'size', + 'value': size, + }; +} + +/// Sample stage with a percentage +class _PipelineSamplePercentage extends PipelineSample { + final double percentage; + + const _PipelineSamplePercentage(this.percentage); + + @override + Map toMap() => { + 'type': 'percentage', + 'value': percentage, + }; +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_search.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_search.dart new file mode 100644 index 000000000000..fc5994a74d1b --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_search.dart @@ -0,0 +1,111 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +enum _SearchQueryType { + string, + expression, +} + +/// Specifies how a pipeline search stage is performed. +/// +/// Search stages must be the first stage after a pipeline source. +final class SearchStage implements PipelineSerializable { + final _SearchQueryType _queryType; + final Object _query; + final List? _sort; + final List? _addFields; + final String? _languageCode; + final int? _limit; + final int? _offset; + final int? _retrievalDepth; + + SearchStage._({ + required _SearchQueryType queryType, + required Object query, + List? sort, + List? addFields, + String? languageCode, + int? limit, + int? offset, + int? retrievalDepth, + }) : _queryType = queryType, + _query = query, + _sort = sort, + _addFields = addFields, + _languageCode = languageCode, + _limit = limit, + _offset = offset, + _retrievalDepth = retrievalDepth; + + /// Creates a search stage from a raw query string. + SearchStage.withQuery( + String query, { + List? sort, + List? addFields, + String? languageCode, + int? limit, + int? offset, + int? retrievalDepth, + }) : this._( + queryType: _SearchQueryType.string, + query: query, + sort: sort, + addFields: addFields, + languageCode: languageCode, + limit: limit, + offset: offset, + retrievalDepth: retrievalDepth, + ); + + /// Creates a search stage from a search query expression. + SearchStage.withQueryExpression( + BooleanExpression query, { + List? sort, + List? addFields, + String? languageCode, + int? limit, + int? offset, + int? retrievalDepth, + }) : this._( + queryType: _SearchQueryType.expression, + query: query, + sort: sort, + addFields: addFields, + languageCode: languageCode, + limit: limit, + offset: offset, + retrievalDepth: retrievalDepth, + ); + + @override + Map toMap() { + final args = { + 'query_type': _queryType.name, + 'query': _query is Expression ? _query.toMap() : _query, + }; + + if (_sort != null) { + args['sort'] = _sort.map((ordering) => ordering.toMap()).toList(); + } + if (_addFields != null) { + args['add_fields'] = _addFields.map((field) => field.toMap()).toList(); + } + if (_languageCode != null) { + args['language_code'] = _languageCode; + } + if (_limit != null) { + args['limit'] = _limit; + } + if (_offset != null) { + args['offset'] = _offset; + } + if (_retrievalDepth != null) { + args['retrieval_depth'] = _retrievalDepth; + } + + return args; + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_snapshot.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_snapshot.dart new file mode 100644 index 000000000000..1a078d31945e --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_snapshot.dart @@ -0,0 +1,41 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Represents the results of a Pipeline query, including the data and metadata. +class PipelineResult { + /// The document reference, or null if no document was returned. + final DocumentReference>? document; + + /// The time the document was created. + final DateTime? createTime; + + /// The time the document was last updated (at the time the snapshot was generated). + final DateTime? updateTime; + + /// Retrieves all fields in the result as a map. + final Map? _data; + + PipelineResult({ + this.document, + this.createTime, + this.updateTime, + Map? data, + }) : _data = data; + + /// Retrieves all fields in the result as a map. + Map? data() => _data; +} + +/// A [PipelineSnapshot] contains the results of a pipeline execution. It can be iterated to retrieve the individual [PipelineResult] objects. +class PipelineSnapshot { + /// List of all the results + final List result; + + /// The time at which the pipeline producing this result is executed. + final DateTime executionTime; + + PipelineSnapshot._(this.result, this.executionTime); +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_source.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_source.dart new file mode 100644 index 000000000000..69d4be991a5d --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_source.dart @@ -0,0 +1,67 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Provides methods to create pipelines from different sources +class PipelineSource { + final FirebaseFirestore _firestore; + + PipelineSource._(this._firestore); + + /// Creates a pipeline from a collection path + Pipeline collection(String collectionPath) { + if (collectionPath.isEmpty) { + throw ArgumentError('A collection path must be a non-empty string.'); + } else if (collectionPath.contains('//')) { + throw ArgumentError('A collection path must not contain "//".'); + } + + final stage = _CollectionPipelineStage(collectionPath); + final delegate = _firestore._delegate.pipeline([stage.toMap()]); + return Pipeline._(_firestore, delegate); + } + + /// Creates a pipeline from a collection reference + Pipeline collectionReference( + CollectionReference> collectionReference, + ) { + final stage = _CollectionPipelineStage(collectionReference.path); + final delegate = _firestore._delegate.pipeline([stage.toMap()]); + return Pipeline._(_firestore, delegate); + } + + /// Creates a pipeline from a collection group + Pipeline collectionGroup(String collectionId) { + if (collectionId.isEmpty) { + throw ArgumentError('A collection ID must be a non-empty string.'); + } else if (collectionId.contains('/')) { + throw ArgumentError( + 'A collection ID passed to collectionGroup() cannot contain "/".', + ); + } + + final stage = _CollectionGroupPipelineStage(collectionId); + final delegate = _firestore._delegate.pipeline([stage.toMap()]); + return Pipeline._(_firestore, delegate); + } + + /// Creates a pipeline from a list of document references + Pipeline documents(List>> documents) { + if (documents.isEmpty) { + throw ArgumentError('Documents list must not be empty.'); + } + + final stage = _DocumentsPipelineStage(documents); + final delegate = _firestore._delegate.pipeline([stage.toMap()]); + return Pipeline._(_firestore, delegate); + } + + /// Creates a pipeline from the entire database + Pipeline database() { + final stage = _DatabasePipelineStage(); + final delegate = _firestore._delegate.pipeline([stage.toMap()]); + return Pipeline._(_firestore, delegate); + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_stage.dart b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_stage.dart new file mode 100644 index 000000000000..e6eaf8df20f5 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/lib/src/pipeline_stage.dart @@ -0,0 +1,437 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of '../cloud_firestore.dart'; + +/// Base sealed class for all pipeline stages +sealed class PipelineStage implements PipelineSerializable { + String get name; +} + +/// Stage representing a collection source +final class _CollectionPipelineStage extends PipelineStage { + final String collectionPath; + + _CollectionPipelineStage(this.collectionPath); + + @override + String get name => 'collection'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'path': collectionPath, + }, + }; + } +} + +/// Stage representing a documents source +final class _DocumentsPipelineStage extends PipelineStage { + final List>> documents; + + _DocumentsPipelineStage(this.documents); + + @override + String get name => 'documents'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': documents + .map( + (doc) => { + 'path': doc.path, + }, + ) + .toList(), + }; + } +} + +/// Stage representing a database source +final class _DatabasePipelineStage extends PipelineStage { + _DatabasePipelineStage(); + + @override + String get name => 'database'; + + @override + Map toMap() { + return { + 'stage': name, + }; + } +} + +/// Stage representing a collection group source +final class _CollectionGroupPipelineStage extends PipelineStage { + final String collectionPath; + + _CollectionGroupPipelineStage(this.collectionPath); + + @override + String get name => 'collection_group'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'path': collectionPath, + }, + }; + } +} + +/// Stage for adding fields to documents +final class _AddFieldsStage extends PipelineStage { + final List expressions; + + _AddFieldsStage(this.expressions); + + @override + String get name => 'add_fields'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'expressions': expressions.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Stage for aggregating data +final class _AggregateStage extends PipelineStage { + final List aggregateFunctions; + + _AggregateStage(this.aggregateFunctions); + + @override + String get name => 'aggregate'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'aggregate_functions': + aggregateFunctions.map((func) => func.toMap()).toList(), + }, + }; + } +} + +/// Stage for aggregating data with options and grouping +final class _AggregateStageWithOptions extends PipelineStage { + final AggregateStageOptions aggregateStage; + final AggregateOptions? options; + + _AggregateStageWithOptions(this.aggregateStage, this.options); + + @override + String get name => 'aggregate_with_options'; + + @override + Map toMap() { + final map = aggregateStage.toMap(); + final optionsMap = options?.toMap(); + return { + 'stage': name, + 'args': { + 'aggregate_stage': map, + 'options': optionsMap, + }, + }; + } +} + +/// Stage for getting distinct values +final class _DistinctStage extends PipelineStage { + final List expressions; + + _DistinctStage(this.expressions); + + @override + String get name => 'distinct'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'expressions': expressions.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Stage for finding nearest vectors +final class _FindNearestStage extends PipelineStage { + final Field vectorField; + final List vectorValue; + final DistanceMeasure distanceMeasure; + final int? limit; + + _FindNearestStage( + this.vectorField, + this.vectorValue, + this.distanceMeasure, { + this.limit, + }); + + @override + String get name => 'find_nearest'; + + @override + Map toMap() { + final map = { + 'stage': name, + 'args': { + 'vector_field': vectorField.fieldName, + 'vector_value': vectorValue, + 'distance_measure': distanceMeasure.name, + }, + }; + if (limit != null) { + map['args']['limit'] = limit; + } + return map; + } +} + +/// Stage for full-text or geo search. +final class _SearchStage extends PipelineStage { + final SearchStage searchStage; + + _SearchStage(this.searchStage); + + @override + String get name => 'search'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': searchStage.toMap(), + }; + } +} + +/// Stage for limiting results +final class _LimitStage extends PipelineStage { + final int limit; + + _LimitStage(this.limit); + + @override + String get name => 'limit'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'limit': limit, + }, + }; + } +} + +/// Stage for offsetting results +final class _OffsetStage extends PipelineStage { + final int offset; + + _OffsetStage(this.offset); + + @override + String get name => 'offset'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'offset': offset, + }, + }; + } +} + +/// Stage for removing fields +final class _RemoveFieldsStage extends PipelineStage { + final List fieldPaths; + + _RemoveFieldsStage(this.fieldPaths); + + @override + String get name => 'remove_fields'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'field_paths': fieldPaths, + }, + }; + } +} + +/// Stage for replacing documents +final class _ReplaceWithStage extends PipelineStage { + final Expression expression; + + _ReplaceWithStage(this.expression); + + @override + String get name => 'replace_with'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} + +/// Stage for sampling documents +final class _SampleStage extends PipelineStage { + final PipelineSample sample; + + _SampleStage(this.sample); + + @override + String get name => 'sample'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': sample.toMap(), + }; + } +} + +/// Stage for selecting specific fields +final class _SelectStage extends PipelineStage { + final List expressions; + + _SelectStage(this.expressions); + + @override + String get name => 'select'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'expressions': expressions.map((expr) => expr.toMap()).toList(), + }, + }; + } +} + +/// Stage for sorting results +final class _SortStage extends PipelineStage { + final List orderings; + + _SortStage(this.orderings); + + @override + String get name => 'sort'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'orderings': orderings + .map( + (o) => { + 'expression': o.expression.toMap(), + 'order_direction': + o.direction == OrderDirection.asc ? 'asc' : 'desc', + }, + ) + .toList(), + }, + }; + } +} + +/// Stage for unnesting arrays +final class _UnnestStage extends PipelineStage { + final Selectable expression; + final String? indexField; + + _UnnestStage(this.expression, this.indexField); + + @override + String get name => 'unnest'; + + @override + Map toMap() { + final map = { + 'stage': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + if (indexField != null) { + map['args']['index_field'] = indexField; + } + return map; + } +} + +/// Stage for union with another pipeline +final class _UnionStage extends PipelineStage { + final Pipeline pipeline; + + _UnionStage(this.pipeline); + + @override + String get name => 'union'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'pipeline': pipeline.stages, + }, + }; + } +} + +/// Stage for filtering documents +final class _WhereStage extends PipelineStage { + final BooleanExpression expression; + + _WhereStage(this.expression); + + @override + String get name => 'where'; + + @override + Map toMap() { + return { + 'stage': name, + 'args': { + 'expression': expression.toMap(), + }, + }; + } +} diff --git a/packages/cloud_firestore/cloud_firestore/lib/src/write_batch.dart b/packages/cloud_firestore/cloud_firestore/lib/src/write_batch.dart index b07a4734b4a5..c8a60f3ee5fd 100644 --- a/packages/cloud_firestore/cloud_firestore/lib/src/write_batch.dart +++ b/packages/cloud_firestore/cloud_firestore/lib/src/write_batch.dart @@ -68,14 +68,23 @@ class WriteBatch { /// If the document does not yet exist, an exception will be thrown. /// /// Objects key can be a String or a FieldPath. - void update(DocumentReference document, Map data) { + void update(DocumentReference document, T data) { assert( document.firestore == _firestore, 'the document provided is from a different Firestore instance', ); + + Map firestoreData; + if (data is Map) { + firestoreData = data; + } else { + final withConverterDoc = document as _WithConverterDocumentReference; + firestoreData = withConverterDoc._toFirestore(data, null); + } + return _delegate.update( document.path, - _CodecUtility.replaceValueWithDelegatesInMapFieldPath(data)!, + _CodecUtility.replaceValueWithDelegatesInMapFieldPath(firestoreData)!, ); } } diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift index e32940c49da7..fa4433624d5b 100644 --- a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "6.1.3" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "6.6.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "cloud_firestore", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "cloud-firestore", targets: ["cloud_firestore"]), + .library(name: "cloud-firestore", targets: ["cloud_firestore"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,14 +30,14 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include/cloud_firestore/Private"), .headerSearchPath("include/cloud_firestore/Public"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-fst\""), ] - ), + ) ] ) diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m new file mode 120000 index 000000000000..0ab2d722c4a0 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m @@ -0,0 +1 @@ +../../../../ios/cloud_firestore/Sources/cloud_firestore/FLTPipelineParser.m \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h new file mode 120000 index 000000000000..fb086ff6b78c --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/macos/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h @@ -0,0 +1 @@ +../../../../../../../ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTPipelineParser.h \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/.gitignore b/packages/cloud_firestore/cloud_firestore/pipeline_example/.gitignore new file mode 100644 index 000000000000..3820a95c65c3 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/.metadata b/packages/cloud_firestore/cloud_firestore/pipeline_example/.metadata new file mode 100644 index 000000000000..2f3a59454284 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + base_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + - platform: android + create_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + base_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + - platform: ios + create_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + base_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + - platform: linux + create_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + base_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + - platform: macos + create_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + base_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + - platform: web + create_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + base_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + - platform: windows + create_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + base_revision: a0e9b9dbf78c8a5ef39b45a7efd40ed2de19c1a7 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/README.md b/packages/cloud_firestore/cloud_firestore/pipeline_example/README.md new file mode 100644 index 000000000000..115008c73129 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/README.md @@ -0,0 +1,16 @@ +# pipeline_example + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/analysis_options.yaml b/packages/cloud_firestore/cloud_firestore/pipeline_example/analysis_options.yaml new file mode 100644 index 000000000000..2f61fa3242f7 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/analysis_options.yaml @@ -0,0 +1,34 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +analyzer: + # Pipeline e2e tests uses firebase_options.dart injected in CI; exclude integration tests + # from analyze so analyze-ci passes. + exclude: + - integration_test/** + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/.gitignore b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/.gitignore new file mode 100644 index 000000000000..be3943c96d8e --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/build.gradle.kts b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/build.gradle.kts new file mode 100644 index 000000000000..9300930bc97a --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id("com.android.application") + // START: FlutterFire Configuration + id("com.google.gms.google-services") + // END: FlutterFire Configuration + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.pipeline_example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.pipeline_example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/debug/AndroidManifest.xml b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..399f6981d5d3 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/AndroidManifest.xml b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..8a1e2a2fd055 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/kotlin/com/example/pipeline_example/MainActivity.kt b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/kotlin/com/example/pipeline_example/MainActivity.kt new file mode 100644 index 000000000000..205920783386 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/kotlin/com/example/pipeline_example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.pipeline_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000000..f74085f3f6a2 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/drawable/launch_background.xml b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000000..db77bb4b7b09 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000000..17987b79bb8a Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000000..09d4391482be Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000000..d5f1c8d34e7a Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000000..4d6372eebdb2 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/values-night/styles.xml b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000000..06952be745f9 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/values/styles.xml b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..cb1ef88056ed --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/profile/AndroidManifest.xml b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000000..399f6981d5d3 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/build.gradle.kts b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/build.gradle.kts new file mode 100644 index 000000000000..dbee657bb5b9 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/gradle.properties b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/gradle.properties new file mode 100644 index 000000000000..fbee1d8cdafc --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/gradle/wrapper/gradle-wrapper.properties b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..e4ef43fb98df --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/android/settings.gradle.kts b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/settings.gradle.kts new file mode 100644 index 000000000000..174f408284fe --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/android/settings.gradle.kts @@ -0,0 +1,29 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + // START: FlutterFire Configuration + id("com.google.gms.google-services") version("4.3.15") apply false + // END: FlutterFire Configuration + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app") diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_add_fields_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_add_fields_e2e.dart new file mode 100644 index 000000000000..323e30163e35 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_add_fields_e2e.dart @@ -0,0 +1,42 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:firebase_core/firebase_core.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineAddFieldsTests() { + group('Pipeline addFields', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test( + 'addFields with expression returns expected transformed data', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('add-fields')) + .sort(Expression.field('title').ascending()) + .addFields(Expression.field('score').abs().as('abs_score')) + .limit(3) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'title': 'alpha', 'score': -7, 'abs_score': 7}, + {'title': 'beta', 'score': 42, 'abs_score': 42}, + {'title': 'gamma', 'score': 0, 'abs_score': 0}, + ]); + }, + ); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_aggregate_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_aggregate_e2e.dart new file mode 100644 index 000000000000..200b491dc964 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_aggregate_e2e.dart @@ -0,0 +1,109 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineAggregateTests() { + group('Pipeline aggregate', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('aggregate count and sum returns expected single result', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('aggregate')) + .aggregate( + CountAll().as('total'), + Expression.field('score').sum().as('total_score'), + ) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'total': 4, 'total_score': 100}, + ]); + }); + + test( + 'aggregateWithOptions with groups returns one row per group', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('aggregate')) + .aggregateWithOptions( + AggregateStageOptions( + accumulators: [ + Expression.field('score').sum().as('total_score'), + CountAll().as('count'), + ], + groups: [Expression.field('category')], + ), + ) + .execute(); + expectResultCount(snapshot, 2); + final results = snapshot.result.map((r) => r.data()!).toList(); + results.sort( + (a, b) => + (a['category'] as String).compareTo(b['category'] as String), + ); + expect(results[0]['category'], 'x'); + expect(results[0]['total_score'], 30); + expect(results[0]['count'], 2); + expect(results[1]['category'], 'y'); + expect(results[1]['total_score'], 70); + expect(results[1]['count'], 2); + }, + ); + + test('aggregate first and last score with sort', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('aggregate')) + .sort(Expression.field('score').ascending()) + .aggregate( + Expression.field('score').first().as('first_s'), + Expression.field('score').last().as('last_s'), + ) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'first_s': 10, 'last_s': 40}, + ]); + }, skip: !kIsWeb); + + test('aggregate array_agg and array_agg_distinct on category', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('aggregate')) + .sort(Expression.field('score').ascending()) + .aggregate( + Expression.field('category').arrayAgg().as('cats'), + Expression.field('category').arrayAggDistinct().as('cats_d'), + ) + .execute(); + expectResultCount(snapshot, 1); + final data = snapshot.result[0].data()!; + final cats = data['cats'] as List; + expect(cats.length, 4); + expect(cats.map((e) => e as String).toSet(), {'x', 'y'}); + final catsD = data['cats_d'] as List; + expect(catsD.length, 2); + expect(catsD.map((e) => e as String).toSet(), {'x', 'y'}); + }, skip: !kIsWeb); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_execute_options_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_execute_options_e2e.dart new file mode 100644 index 000000000000..4968b3aba36a --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_execute_options_e2e.dart @@ -0,0 +1,37 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineExecuteOptionsTests() { + group('Pipeline execute with options', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('execute with ExecuteOptions returns expected results', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('add-fields')) + .sort(Expression.field('title').ascending()) + .limit(2) + .execute( + options: const ExecuteOptions(indexMode: IndexMode.recommended), + ); + expectResultCount(snapshot, 2); + expect(snapshot.result[0].data()!['title'], 'alpha'); + expect(snapshot.result[1].data()!['title'], 'beta'); + }, skip: true); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_expressions_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_expressions_e2e.dart new file mode 100644 index 000000000000..4835839efb89 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_expressions_e2e.dart @@ -0,0 +1,1106 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, TargetPlatform, kIsWeb; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineExpressionsTests() { + group('Pipeline expressions in where and addFields', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + /// Single doc: `test==expressions`, `score==50` (see pipeline seed). + Future expressionsDocScore50(Selectable field) { + return firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(50)) + .addFields(field) + .limit(1) + .execute(); + } + + test('where with greaterThan filters and returns expected docs', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').greaterThan(Expression.constant(50))) + .sort(Expression.field('score').ascending()) + .limit(5) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'score': 60, 'a': 1, 'b': 2}, + {'score': 70, 'a': 10, 'b': 20}, + {'score': 80, 'a': 0, 'b': 100}, + ]); + }); + + test('addFields with add expression returns expected values', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .sort(Expression.field('score').ascending()) + .addFields( + Expression.field('a').add(Expression.field('b')).as('sum_ab'), + ) + .limit(5) + .execute(); + expectResultCount(snapshot, 5); + expectResultsData(snapshot, [ + {'score': 40, 'a': 5, 'b': 5, 'sum_ab': 10}, + {'score': 50, 'a': 1, 'b': 2, 'sum_ab': 3}, + {'score': 60, 'a': 1, 'b': 2, 'sum_ab': 3}, + {'score': 70, 'a': 10, 'b': 20, 'sum_ab': 30}, + {'score': 80, 'a': 0, 'b': 100, 'sum_ab': 100}, + ]); + }); + + test('addFields with conditional returns expected band', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .sort(Expression.field('score').ascending()) + .addFields( + Expression.conditional( + Expression.field('score').greaterThan(Expression.constant(50)), + Expression.constant('high'), + Expression.constant('low'), + ).as('band'), + ) + .limit(5) + .execute(); + expectResultCount(snapshot, 5); + expectResultsData(snapshot, [ + {'score': 40, 'band': 'low'}, + {'score': 50, 'band': 'low'}, + {'score': 60, 'band': 'high'}, + {'score': 70, 'band': 'high'}, + {'score': 80, 'band': 'high'}, + ]); + }); + + test('addFields with arrayLength returns length for array field', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .sort(Expression.field('score').ascending()) + .addFields(Expression.field('tags').arrayLength().as('tags_len')) + .limit(5) + .execute(); + expectResultCount(snapshot, 5); + final withTags = snapshot.result + .where((r) => r.data()!['tags_len'] == 2) + .toList(); + expect(withTags.length, 1); + expect(withTags.first.data()!['score'], 50); + }); + + test('where with lessThan filters correctly', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').lessThan(Expression.constant(60))) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 2); + expectResultsData(snapshot, [ + {'score': 40, 'a': 5, 'b': 5}, + {'score': 50, 'a': 1, 'b': 2}, + ]); + }); + + test('where with equalValue filters correctly', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(50)) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'score': 50, 'a': 1, 'b': 2}, + ]); + }); + + test('where with equal(Expression) filters correctly', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equal(Expression.constant(50))) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'score': 50, 'a': 1, 'b': 2}, + ]); + }); + + test('where with notEqualValue filters correctly', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').notEqualValue(50)) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 4); + expectResultsData(snapshot, [ + {'score': 40, 'a': 5, 'b': 5}, + {'score': 60, 'a': 1, 'b': 2}, + {'score': 70, 'a': 10, 'b': 20}, + {'score': 80, 'a': 0, 'b': 100}, + ]); + }); + + test('where with notEqual(Expression) filters correctly', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').notEqual(Expression.constant(50))) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 4); + expectResultsData(snapshot, [ + {'score': 40, 'a': 5, 'b': 5}, + {'score': 60, 'a': 1, 'b': 2}, + {'score': 70, 'a': 10, 'b': 20}, + {'score': 80, 'a': 0, 'b': 100}, + ]); + }); + + test( + 'where with greaterThanValue and lessThanValue filter correctly', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').greaterThanValue(40)) + .where(Expression.field('score').lessThanValue(80)) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'score': 50, 'a': 1, 'b': 2}, + {'score': 60, 'a': 1, 'b': 2}, + {'score': 70, 'a': 10, 'b': 20}, + ]); + }, + ); + + test( + 'where with greaterThanOrEqual and lessThanOrEqual filter correctly', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where( + Expression.field( + 'score', + ).greaterThanOrEqual(Expression.constant(50)), + ) + .where( + Expression.field( + 'score', + ).lessThanOrEqual(Expression.constant(70)), + ) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'score': 50, 'a': 1, 'b': 2}, + {'score': 60, 'a': 1, 'b': 2}, + {'score': 70, 'a': 10, 'b': 20}, + ]); + }, + ); + + test( + 'addFields with subtract, multiply, divide, modulo return expected values', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .sort(Expression.field('score').ascending()) + .addFields( + Expression.field('a').subtract(Expression.field('b')).as('diff'), + Expression.field( + 'a', + ).multiply(Expression.field('b')).as('product'), + Expression.field( + 'score', + ).divide(Expression.constant(10)).as('score_div_10'), + Expression.field( + 'score', + ).modulo(Expression.constant(30)).as('score_mod_30'), + ) + .limit(3) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + { + 'score': 40, + 'a': 5, + 'b': 5, + 'diff': 0, + 'product': 25, + 'score_div_10': 4, + 'score_mod_30': 10, + }, + { + 'score': 50, + 'a': 1, + 'b': 2, + 'diff': -1, + 'product': 2, + 'score_div_10': 5, + 'score_mod_30': 20, + }, + { + 'score': 60, + 'a': 1, + 'b': 2, + 'diff': -1, + 'product': 2, + 'score_div_10': 6, + 'score_mod_30': 0, + }, + ]); + }, + ); + + test('where with and returns intersection', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where( + Expression.and( + Expression.field('score').greaterThan(Expression.constant(40)), + Expression.field('score').lessThan(Expression.constant(80)), + ), + ) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'score': 50, 'a': 1, 'b': 2}, + {'score': 60, 'a': 1, 'b': 2}, + {'score': 70, 'a': 10, 'b': 20}, + ]); + }); + + test('where with or returns union', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where( + Expression.or( + Expression.field('score').equalValue(40), + Expression.field('score').equalValue(80), + ), + ) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 2); + expectResultsData(snapshot, [ + {'score': 40, 'a': 5, 'b': 5}, + {'score': 80, 'a': 0, 'b': 100}, + ]); + }); + + test('where with not inverts condition', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where( + Expression.not( + Expression.field( + 'score', + ).greaterThanOrEqual(Expression.constant(60)), + ), + ) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 2); + expectResultsData(snapshot, [ + {'score': 40, 'a': 5, 'b': 5}, + {'score': 50, 'a': 1, 'b': 2}, + ]); + }); + + test( + 'addFields with ifAbsentValue uses default when field missing', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .sort(Expression.field('score').ascending()) + .addFields( + Expression.field( + 'tags', + ).ifAbsentValue('default_value').as('tags_or_empty'), + ) + .limit(2) + .execute(); + expectResultCount(snapshot, 2); + expect(snapshot.result[0].data()!['tags_or_empty'], 'default_value'); + expect(snapshot.result[1].data()!['tags_or_empty'], ['p', 'q']); + }, + ); + + test( + 'addFields with ifAbsent(Expression) uses else expression when field missing', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .sort(Expression.field('score').ascending()) + .addFields( + Expression.field('tags') + .ifAbsent(Expression.constant('default_value')) + .as('tags_or_default'), + ) + .limit(2) + .execute(); + expectResultCount(snapshot, 2); + expect(snapshot.result[0].data()!['tags_or_default'], 'default_value'); + expect(snapshot.result[1].data()!['tags_or_default'], ['p', 'q']); + }, + ); + + test( + 'where arrayContainsValue filters docs with array containing value', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('tags').arrayContainsValue('p')) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + { + 'score': 50, + 'a': 1, + 'b': 2, + 'tags': ['p', 'q'], + }, + ]); + }, + ); + + test( + 'where arrayContainsElement(Expression) filters docs with array containing element', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where( + Expression.field( + 'tags', + ).arrayContainsElement(Expression.constant('q')), + ) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + { + 'score': 50, + 'a': 1, + 'b': 2, + 'tags': ['p', 'q'], + }, + ]); + }, + ); + + test( + 'addFields with string expressions (concat, length, toLower, toUpper, trim)', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('s').concat(['!']).as('s_concat'), + Expression.field('s').length().as('s_len'), + Expression.field('s').toLowerCase().as('s_lower'), + Expression.field('s').toUpperCase().as('s_upper'), + Expression.field('s').trim().as('s_trim'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + { + 'score': 60, + 's_concat': ' AbC !', + 's_len': 7, + 's_lower': ' abc ', + 's_upper': ' ABC ', + 's_trim': 'AbC', + }, + ]); + }, + ); + + test('addFields with substring returns expected slice', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(70)) + .addFields( + Expression.field('s') + .substring(Expression.constant(0), Expression.constant(1)) + .as('s_first'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['s_first'], 'x'); + }); + + test('addFields with map_get returns nested value', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('m').mapGet(Expression.constant('x')).as('m_x'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['m_x'], 10); + }); + + test('where with exists filters docs where tags exists', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('tags').exists()) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['score'], 50); + }); + + test( + 'where with xor returns docs matching exactly one condition', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where( + Expression.xor( + Expression.field('score').equalValue(40), + Expression.field('score').equalValue(50), + ), + ) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 2); + expectResultsData(snapshot, [ + {'score': 40}, + {'score': 50}, + ]); + }, + ); + + test('where with equalAny and notEqualAny filters correctly', () async { + final inSnapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.equalAny(Expression.field('score'), [40, 70])) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(inSnapshot, 2); + expectResultsData(inSnapshot, [ + {'score': 40}, + {'score': 70}, + ]); + + final notInSnapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.notEqualAny(Expression.field('score'), [40, 70])) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(notInSnapshot, 3); + expectResultsData(notInSnapshot, [ + {'score': 50}, + {'score': 60}, + {'score': 80}, + ]); + }); + + test('addFields split', () async { + final snapshot = await expressionsDocScore50( + Expression.field('s').splitLiteral('-').as('s_split'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['s_split'], ['a', 'b', 'c']); + }); + + test('addFields join', () async { + final snapshot = await expressionsDocScore50( + Expression.field('tags').joinLiteral('|').as('tags_joined'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['tags_joined'], 'p|q'); + }); + + test('addFields arrayConcat', () async { + final snapshot = await expressionsDocScore50( + Expression.field('arr').arrayConcat([9]).as('arr_concat'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['arr_concat'], [2, 4, 6, 9]); + }); + + test('addFields arrayConcatMultiple', () async { + final snapshot = await expressionsDocScore50( + Expression.field('arr') + .arrayConcatMultiple([ + [10], + [11], + ]) + .as('arr_concat_multi'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['arr_concat_multi'], [2, 4, 6, 10, 11]); + }); + + test('addFields arrayContainsAny', () async { + final snapshot = await expressionsDocScore50( + Expression.field('arr').arrayContainsAny([2, 99]).as('arr_has_any'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['arr_has_any'], true); + }); + + test('addFields arrayContainsAll values', () async { + final snapshot = await expressionsDocScore50( + Expression.field( + 'arr', + ).arrayContainsAll([2, 4]).as('arr_has_all_values'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['arr_has_all_values'], true); + }); + + test('addFields arrayContainsAllFrom expression', () async { + final snapshot = await expressionsDocScore50( + Expression.field('arr') + .arrayContainsAllFrom(Expression.array([Expression.constant(2), 4])) + .as('arr_has_all_expr'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['arr_has_all_expr'], true); + }); + + test('addFields map constructor', () async { + final snapshot = await expressionsDocScore50( + Expression.map({ + 'left': Expression.field('a'), + 'right': Expression.field('b'), + }).as('mapped'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['mapped'], {'left': 1, 'right': 2}); + }); + + test('addFields nullValue', () async { + final snapshot = await expressionsDocScore50( + Expression.nullValue().as('explicit_null'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['explicit_null'], null); + }); + + test('addFields ifError uses catch when expression errors', () async { + final snapshot = await expressionsDocScore50( + Expression.field( + 'score', + ).divide(Expression.constant(0)).ifErrorValue('safe').as('safe_div'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['safe_div'], 'safe'); + }); + + test('addFields isAbsent true for missing field', () async { + final snapshot = await expressionsDocScore50( + Expression.field('missing_field').isAbsent().as('missing_absent'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['missing_absent'], true); + }); + + test('addFields isError false for missing field', () async { + final snapshot = await expressionsDocScore50( + Expression.field('missing_field').isError().as('missing_error'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['missing_error'], false); + }); + + test('addFields asBoolean coerces numeric field', () async { + final snapshot = await expressionsDocScore50( + Expression.field('a').asBoolean().as('a_bool'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['a_bool'], 1); + }); + + test( + 'addFields bitwise expressions (bitAnd/Or/Xor/Not/shifts)', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field( + 'bit_a', + ).bitAnd(Expression.constant(3)).as('b_and'), + Expression.field( + 'bit_a', + ).bitOr(Expression.constant(1)).as('b_or'), + Expression.field( + 'bit_a', + ).bitXor(Expression.constant(7)).as('b_xor'), + Expression.field('bit_a').bitNot().as('b_not'), + Expression.field('bit_a').bitLeftShiftLiteral(1).as('b_lsh'), + Expression.field('bit_a').bitRightShiftLiteral(1).as('b_rsh'), + ) + .limit(1) + .execute(); + + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'b_and': 2, 'b_or': 7, 'b_xor': 1, 'b_lsh': 12, 'b_rsh': 3}, + ]); + expect(snapshot.result[0].data()!['b_not'], isNotNull); + }, + // Bitwise pipeline addFields not supported on Android native SDK yet. + skip: true, + ); + + test('addFields documentId, collectionId and documentIdFromRef', () async { + final col = firestore.collection('pipeline-e2e'); + final seedQuery = await col + .where('test', isEqualTo: 'expressions') + .where('score', isEqualTo: 60) + .limit(1) + .get(); + expect(seedQuery.docs, isNotEmpty); + final docRef = seedQuery.docs.first.reference; + final expectedDocId = seedQuery.docs.first.id; + + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('__name__').documentId().as('doc_id'), + Expression.field('__name__').collectionId().as('coll_id'), + Expression.documentIdFromRef(docRef).as('doc_id_from_ref'), + ) + .limit(1) + .execute(); + + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + { + 'coll_id': 'pipeline-e2e', + 'doc_id': expectedDocId, + 'doc_id_from_ref': expectedDocId, + }, + ]); + }, skip: kIsWeb); + + test( + 'addFields with currentTimestamp, timestampAdd/Subtract/Truncate', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.currentTimestamp().as('now'), + Expression.timestampAddLiteral( + Expression.field('ts'), + 'day', + 1, + ).as('ts_plus_1d'), + Expression.timestampSubtractLiteral( + Expression.field('ts'), + 'hour', + 1, + ).as('ts_minus_1h'), + Expression.timestampTruncate( + Expression.field('ts'), + 'day', + ).as('ts_day'), + ) + .limit(1) + .execute(); + + expectResultCount(snapshot, 1); + final data = snapshot.result[0].data()!; + expect(data['now'], isNotNull); + expect(data['ts_plus_1d'], isNotNull); + expect(data['ts_minus_1h'], isNotNull); + expect(data['ts_day'], isNotNull); + }, + skip: kIsWeb, + ); + + test('select with alias() returns renamed field', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .select(Expression.field('score').alias('renamed_score')) + .limit(1) + .execute(); + + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['renamed_score'], 60); + }); + + test('addFields with array_reverse returns reversed array', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(50)) + .addFields(Expression.field('tags').arrayReverse().as('tags_rev')) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['tags_rev'], ['q', 'p']); + }); + + test('addFields with arraySlice returns sliced array', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(50)) + .addFields(Expression.field('arr').arraySlice(1, 2).as('arr_slice')) + .limit(1) + .execute(); + + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + { + 'arr_slice': [4, 6], + }, + ]); + }); + + test( + 'arraySum addFields succeeds on Android', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .addFields(Expression.array([1, 2, 3]).arraySum().as('x')) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['x'], 6); + }, + skip: defaultTargetPlatform != TargetPlatform.android, + ); + + test( + 'unsupported expression returns parse-error with informative message', + () async { + try { + await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .addFields(Expression.array([1, 2, 3]).arraySum().as('x')) + .limit(1) + .execute(); + return; + } on FirebaseException catch (e) { + expect(e.code, 'parse-error'); + expect(e.message, isNotNull); + expect(e.message!, contains('Unsupported expression')); + } + }, + skip: + defaultTargetPlatform != TargetPlatform.iOS && + defaultTargetPlatform != TargetPlatform.macOS, + ); + + test('addFields regexFind on email', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('email').regexFind('@.+').as('domain_part'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['domain_part'], '@example.com'); + }, skip: !kIsWeb); + + test('addFields regexFindAll on email', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('email').regexFindAll('[a-z]+').as('chunks'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + final chunks = snapshot.result[0].data()!['chunks'] as List?; + expect(chunks, isNotNull); + expect(chunks, contains('demo')); + expect(chunks, contains('com')); + }, skip: !kIsWeb); + + test( + 'addFields stringReplaceOne stringIndexOf stringRepeat on s', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(70)) + .addFields( + Expression.field('s').stringReplaceOneLiteral('x', 'Z').as('s1'), + Expression.field('s').stringIndexOf('y').as('iy'), + Expression.field('s').stringRepeat(2).as('s2'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'s1': 'Zy', 'iy': 1, 's2': 'xyxy'}, + ]); + }, + skip: !kIsWeb, + ); + + test('addFields ltrim rtrim on padded s', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('s').ltrim().as('lt'), + Expression.field('s').rtrim().as('rt'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'lt': 'AbC ', 'rt': ' AbC'}, + ]); + }, skip: !kIsWeb); + + test('addFields mapSet and mapEntries on m', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('m').mapSet('z', 99).as('m2'), + Expression.field('m').mapEntries().as('entries'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + final data = snapshot.result[0].data()!; + expect(data['m2'], {'x': 10, 'y': 20, 'z': 99}); + final entries = data['entries'] as List?; + expect(entries, isNotNull); + expect(entries!.length, 2); + }, skip: !kIsWeb); + + test('addFields type(score) is int64 string', () async { + final snapshot = await expressionsDocScore50( + Expression.field('score').type().as('stype'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['stype'], 'int64'); + }, skip: !kIsWeb); + + test('where isType int64 on score keeps integer score docs', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').isType(Type.int64)) + .sort(Expression.field('score').ascending()) + .execute(); + expectResultCount(snapshot, 5); + }, skip: !kIsWeb); + + test('addFields trunc pi and rand', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('pi').trunc().as('pi0'), + Expression.field('pi').trunc(Expression.constant(2)).as('pi2'), + Expression.rand().as('r'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + final data = snapshot.result[0].data()!; + expect(data['pi0'], 3); + expect(data['pi2'], closeTo(3.14, 0.01)); + expect(data['r'], isA()); + }, skip: !kIsWeb); + + test('addFields arrayFirst arrayLast on tags', () async { + final snapshot = await expressionsDocScore50( + Expression.field('tags').arrayFirst().as('t0'), + ); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'t0': 'p'}, + ]); + final snap2 = await expressionsDocScore50( + Expression.field('tags').arrayLast().as('t1'), + ); + expect(snap2.result[0].data()!['t1'], 'q'); + }, skip: !kIsWeb); + + test('addFields arrayFirstN arrayLastN on tags', () async { + final snapshot = await expressionsDocScore50( + Expression.field('tags').arrayFirstN(1).as('head'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['head'], ['p']); + final snap2 = await expressionsDocScore50( + Expression.field('tags').arrayLastN(1).as('tail'), + ); + expect(snap2.result[0].data()!['tail'], ['q']); + }, skip: !kIsWeb); + + test('addFields arrayMaximum arrayMinimum on arr', () async { + final snapshot = await expressionsDocScore50( + Expression.field('arr').arrayMaximum().as('mx'), + ); + expectResultCount(snapshot, 1); + expect(snapshot.result[0].data()!['mx'], 6); + final snap2 = await expressionsDocScore50( + Expression.field('arr').arrayMinimum().as('mn'), + ); + expect(snap2.result[0].data()!['mn'], 2); + }, skip: !kIsWeb); + + test('addFields arrayMaximumN arrayMinimumN on arr', () async { + final snapshot = await expressionsDocScore50( + Expression.field('arr').arrayMaximumN(2).as('top2'), + ); + expectResultCount(snapshot, 1); + final top2 = snapshot.result[0].data()!['top2'] as List; + expect(top2.length, 2); + expect(top2.map((e) => e as int).toSet(), {4, 6}); + final snap2 = await expressionsDocScore50( + Expression.field('arr').arrayMinimumN(2).as('bot2'), + ); + final bot2 = snap2.result[0].data()!['bot2'] as List; + expect(bot2.length, 2); + expect(bot2.map((e) => e as int).toSet(), {2, 4}); + }, skip: !kIsWeb); + + test( + 'addFields arrayIndexOf arrayLastIndexOf arrayIndexOfAll on dup_tags', + () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.field('score').equalValue(60)) + .addFields( + Expression.field('dup_tags').arrayIndexOf('a').as('i0'), + Expression.field('dup_tags').arrayLastIndexOf('a').as('i1'), + Expression.field('dup_tags').arrayIndexOfAll('a').as('all'), + ) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + { + 'i0': 0, + 'i1': 2, + 'all': [0, 2], + }, + ]); + }, + skip: !kIsWeb, + ); + + test('Expression.isTypeStatic equals where isType', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('expressions')) + .where(Expression.isTypeStatic(Expression.field('score'), Type.int64)) + .sort(Expression.field('score').ascending()) + .limit(2) + .execute(); + expectResultCount(snapshot, 2); + }, skip: !kIsWeb); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_filter_sort_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_filter_sort_e2e.dart new file mode 100644 index 000000000000..95e52f52e5b4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_filter_sort_e2e.dart @@ -0,0 +1,89 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:firebase_core/firebase_core.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineFilterSortTests() { + group('Pipeline where, sort, limit, offset, distinct', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('where + limit returns expected count and data', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('filter-sort')) + .where(Expression.field('active').equalValue(true)) + .sort(Expression.field('score').ascending()) + .limit(5) + .execute(); + expectResultCount(snapshot, 4); + expectResultsData(snapshot, [ + {'active': true, 'score': 10, 'category': 'a'}, + {'active': true, 'score': 15, 'category': 'b'}, + {'active': true, 'score': 20, 'category': 'b'}, + {'active': true, 'score': 30, 'category': 'c'}, + ]); + }); + + test('sort + limit returns expected order', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('filter-sort')) + .sort(Expression.field('score').descending()) + .limit(3) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'score': 30, 'category': 'c'}, + {'score': 20, 'category': 'b'}, + {'score': 15, 'category': 'b'}, + ]); + }); + + test('offset + limit returns expected slice', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('filter-sort')) + .sort(Expression.field('score').ascending()) + .offset(2) + .limit(5) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'score': 15, 'category': 'b'}, + {'score': 20, 'category': 'b'}, + {'score': 30, 'category': 'c'}, + ]); + }); + + test('distinct returns unique category values', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('filter-sort')) + .distinct(Expression.field('category').as('category')) + .sort(Expression.field('category').ascending()) + .limit(10) + .execute(); + expectResultCount(snapshot, 3); + final categories = snapshot.result + .map((r) => r.data()!['category']) + .toList(); + expect(categories..sort(), ['a', 'b', 'c']); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_find_nearest_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_find_nearest_e2e.dart new file mode 100644 index 000000000000..35d5b6e75062 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_find_nearest_e2e.dart @@ -0,0 +1,45 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void runPipelineFindNearestTests() { + group('Pipeline findNearest', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test( + 'findNearest returns results ordered by distance when vector index exists', + () async { + final pipeline = firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('find-nearest')) + .findNearest( + Field('embedding'), + [0.1, 0.2, 0.3], + DistanceMeasure.cosine, + limit: 5, + ); + final snapshot = await pipeline.execute(); + expect(snapshot, isNotNull); + expect(snapshot.result, isA>()); + if (snapshot.result.isNotEmpty) { + final first = snapshot.result.first.data(); + if (first != null && first.containsKey('label')) { + expect(first['label'], 'near'); + } + } + }, + ); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_live_test.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_live_test.dart new file mode 100644 index 000000000000..d3f679955d58 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_live_test.dart @@ -0,0 +1,55 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Pipeline E2E runs against live Firebase (do not use emulator). + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:pipeline_example/firebase_options.dart'; + +import 'pipeline_add_fields_e2e.dart'; +import 'pipeline_aggregate_e2e.dart'; +import 'pipeline_expressions_e2e.dart'; +import 'pipeline_filter_sort_e2e.dart'; +import 'pipeline_find_nearest_e2e.dart'; +import 'pipeline_remove_fields_e2e.dart'; +import 'pipeline_replace_with_e2e.dart'; +import 'pipeline_sample_e2e.dart'; +import 'pipeline_search_e2e.dart'; +import 'pipeline_seed.dart'; +import 'pipeline_select_e2e.dart'; +import 'pipeline_unnest_union_e2e.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('pipeline (live)', () { + setUpAll(() async { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + final firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + firestore.settings = const Settings(persistenceEnabled: true); + await seedPipelineE2ECollections(firestore); + await seedPipelineSearchE2ECollection(firestore); + }); + + runPipelineFilterSortTests(); + runPipelineAddFieldsTests(); + runPipelineSelectTests(); + runPipelineRemoveFieldsTests(); + runPipelineReplaceWithTests(); + runPipelineAggregateTests(); + runPipelineUnnestUnionTests(); + runPipelineSampleTests(); + runPipelineFindNearestTests(); + runPipelineSearchTests(); + runPipelineExpressionsTests(); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_remove_fields_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_remove_fields_e2e.dart new file mode 100644 index 000000000000..51840cbd4691 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_remove_fields_e2e.dart @@ -0,0 +1,44 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineRemoveFieldsTests() { + group('Pipeline removeFields', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('removeFields drops specified fields only', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('remove-fields')) + .sort(Expression.field('keep').ascending()) + .removeFields('internal_id', 'debug_flag') + .limit(3) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'keep': 'x'}, + {'keep': 'y'}, + {'keep': 'z'}, + ]); + for (final r in snapshot.result) { + final data = r.data()!; + expect(data.containsKey('internal_id'), false); + expect(data.containsKey('debug_flag'), false); + } + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_replace_with_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_replace_with_e2e.dart new file mode 100644 index 000000000000..68f3730180f5 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_replace_with_e2e.dart @@ -0,0 +1,53 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineReplaceWithTests() { + group('Pipeline replaceWith', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('replaceWith emits nested map as document', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('replace-with')) + .sort(Expression.field('name').ascending()) + .replaceWith(Expression.field('nested')) + .limit(1) + .execute(); + expectResultCount(snapshot, 1); + expectResultsData(snapshot, [ + {'father': 'John Doe Sr.', 'mother': 'Jane Doe'}, + ]); + }); + + test('replaceWith emits each nested map as a result document', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('replace-with')) + .sort(Expression.field('name').ascending()) + .replaceWith(Expression.field('nested')) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'father': 'John Doe Sr.', 'mother': 'Jane Doe'}, + {'a': 1, 'b': 2}, + {'x': 'foo', 'y': 'bar'}, + ]); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_sample_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_sample_e2e.dart new file mode 100644 index 000000000000..4357d8c23df6 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_sample_e2e.dart @@ -0,0 +1,47 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineSampleTests() { + group('Pipeline sample', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('sample withSize returns exactly requested count', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('sample')) + .sample(PipelineSample.withSize(5)) + .execute(); + expectResultCount(snapshot, 5); + for (final r in snapshot.result) { + expect(r.data(), isNotNull); + expect(r.data()!.containsKey('n'), true); + } + }); + + test('sample withPercentage returns results in expected range', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('sample')) + .sample(PipelineSample.withPercentage(0.2)) + .execute(); + expect(snapshot.result.length, greaterThanOrEqualTo(0)); + expect(snapshot.result.length, lessThanOrEqualTo(10)); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_search_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_search_e2e.dart new file mode 100644 index 000000000000..5ac53182acca --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_search_e2e.dart @@ -0,0 +1,114 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +const String _searchCollection = 'pipeline-search-e2e'; + +void runPipelineSearchTests() { + group('Pipeline search', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('withQuery returns matching search results', () async { + final snapshot = await firestore + .pipeline() + .collection(_searchCollection) + .search(SearchStage.withQuery('pancakes', limit: 10)) + .execute(); + + expect(_resultNames(snapshot), contains('Pancake House')); + }); + + test('withQuery passes options and returns expected result list', () async { + final snapshot = await firestore + .pipeline() + .collection(_searchCollection) + .search( + SearchStage.withQuery( + 'breakfast', + languageCode: 'en', + retrievalDepth: 10, + offset: 0, + limit: 10, + ), + ) + .execute(); + + expect(_sortedResultValues(snapshot, 'name'), [ + 'Coffee Bar', + 'Pancake House', + ]); + expect(_resultNames(snapshot), isNot(contains('Burger Diner'))); + }); + + test('withQueryExpression returns matching search results', () async { + final snapshot = await firestore + .pipeline() + .collection(_searchCollection) + .search( + SearchStage.withQueryExpression( + Expression.documentMatches('pancakes'), + limit: 10, + ), + ) + .execute(); + + expect(_resultNames(snapshot), contains('Pancake House')); + }); + + test( + 'withQueryExpression supports combined document match queries', + () async { + final snapshot = await firestore + .pipeline() + .collection(_searchCollection) + .search( + SearchStage.withQueryExpression( + Expression.and( + Expression.documentMatches('pancakes'), + Expression.documentMatches('breakfast'), + ), + limit: 10, + ), + ) + .execute(); + + expect(_resultNames(snapshot), contains('Pancake House')); + }, + skip: + true, // 'Native search does not support AND in query expressions yet.' + ); + + test('withQuery returns empty results when nothing matches', () async { + final snapshot = await firestore + .pipeline() + .collection(_searchCollection) + .search(SearchStage.withQuery('No match', limit: 10)) + .execute(); + + expect(snapshot.result, isEmpty); + }); + }); +} + +List _resultNames(PipelineSnapshot snapshot) { + return snapshot.result.map((result) => result.data()?['name']).toList(); +} + +List _sortedResultValues(PipelineSnapshot snapshot, String field) { + return snapshot.result + .map((result) => result.data()?[field]) + .whereType() + .toList() + ..sort(); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_seed.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_seed.dart new file mode 100644 index 000000000000..9e4d8293ef69 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_seed.dart @@ -0,0 +1,195 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; + +const String _col = 'pipeline-e2e'; +const String _searchCol = 'pipeline-search-e2e'; +const int _maxBatchSize = 500; + +Future seedPipelineE2ECollections(FirebaseFirestore firestore) async { + final docs = >[ + ..._withTest('filter-sort', [ + {'active': true, 'score': 10, 'category': 'a'}, + {'active': true, 'score': 20, 'category': 'b'}, + {'active': false, 'score': 5, 'category': 'a'}, + {'active': true, 'score': 30, 'category': 'c'}, + {'active': true, 'score': 15, 'category': 'b'}, + ]), + ..._withTest('add-fields', [ + {'title': 'alpha', 'score': -7}, + {'title': 'beta', 'score': 42}, + {'title': 'gamma', 'score': 0}, + ]), + ..._withTest('select', [ + {'name': 'doc1', 'score': 1}, + {'name': 'doc2', 'score': 2}, + {'name': 'doc3', 'score': 3}, + ]), + ..._withTest('remove-fields', [ + {'keep': 'x', 'internal_id': 'id1', 'debug_flag': true}, + {'keep': 'y', 'internal_id': 'id2', 'debug_flag': false}, + {'keep': 'z', 'internal_id': 'id3', 'debug_flag': true}, + ]), + ..._withTest('replace-with', [ + { + 'name': 'Doc 1', + 'nested': {'father': 'John Doe Sr.', 'mother': 'Jane Doe'}, + }, + { + 'name': 'Doc 2', + 'nested': {'a': 1, 'b': 2}, + }, + { + 'name': 'Doc 3', + 'nested': {'x': 'foo', 'y': 'bar'}, + }, + ]), + ..._withTest('aggregate', [ + {'score': 10, 'category': 'x'}, + {'score': 20, 'category': 'x'}, + {'score': 30, 'category': 'y'}, + {'score': 40, 'category': 'y'}, + ]), + ..._withTest('unnest', [ + { + 'tags': ['dart', 'flutter'], + }, + { + 'tags': ['firestore'], + }, + { + 'tags': ['dart', 'firestore'], + }, + ]), + ..._withTest('union-a', [ + {'id': 'a1'}, + {'id': 'a2'}, + {'id': 'a3'}, + ]), + ..._withTest('union-b', [ + {'id': 'b1'}, + {'id': 'b2'}, + {'id': 'b3'}, + ]), + ..._withTest('sample', [ + {'n': 1}, + {'n': 2}, + {'n': 3}, + {'n': 4}, + {'n': 5}, + {'n': 6}, + {'n': 7}, + {'n': 8}, + {'n': 9}, + {'n': 10}, + ]), + ..._withTest('find-nearest', [ + { + 'embedding': VectorValue([0.1, 0.2, 0.3]), + 'label': 'near', + }, + { + 'embedding': VectorValue([0.15, 0.25, 0.35]), + 'label': 'near2', + }, + { + 'embedding': VectorValue([1.0, 0.0, 0.0]), + 'label': 'far', + }, + ]), + ..._withTest('expressions', [ + { + 'score': 60, + 'a': 1, + 'b': 2, + 's': ' AbC ', + 'm': {'x': 10, 'y': 20}, + 'email': 'demo@example.com', + 'pi': 3.14159, + 'dup_tags': ['a', 'b', 'a'], + 'bit_a': 6, + 'bit_b': 3, + 'arr': [1, 2, 3], + 'arr_b': [4, 5], + 'maybe_null': null, + 'ts': Timestamp.fromMillisecondsSinceEpoch(1700000000000), + }, + { + 'score': 70, + 'a': 10, + 'b': 20, + 's': 'xy', + 'm': {'x': 1}, + 'bit_a': 5, + 'bit_b': 1, + 'arr': [7, 8, 9], + 'arr_b': [10], + 'ts': Timestamp.fromMillisecondsSinceEpoch(1700003600000), + }, + {'score': 40, 'a': 5, 'b': 5}, + {'score': 80, 'a': 0, 'b': 100, 's': 'Hi'}, + { + 'score': 50, + 'a': 1, + 'b': 2, + 'tags': ['p', 'q'], + 'arr': [2, 4, 6], + 'arr_b': [8], + 's': 'a-b-c', + 'ts': Timestamp.fromMillisecondsSinceEpoch(1700007200000), + }, + ]), + ]; + await _clearAndSeed(firestore, _col, docs); +} + +Future seedPipelineSearchE2ECollection( + FirebaseFirestore firestore, +) async { + final docs = >[ + {'name': 'Pancake House', 'description': 'waffles pancakes breakfast'}, + {'name': 'Burger Diner', 'description': 'burgers fries lunch'}, + {'name': 'Coffee Bar', 'description': 'coffee breakfast pastries'}, + ]; + await _clearAndSeed(firestore, _searchCol, docs); +} + +List> _withTest( + String test, + List> maps, +) { + return maps.map((m) => {'test': test, ...m}).toList(); +} + +Future _clearAndSeed( + FirebaseFirestore firestore, + String collectionPath, + List> docs, +) async { + final col = firestore.collection(collectionPath); + final snapshot = await col.get(); + for (final chunk in _chunk(snapshot.docs, _maxBatchSize)) { + final batch = firestore.batch(); + for (final doc in chunk) { + batch.delete(doc.reference); + } + await batch.commit(); + } + var docIndex = 0; + for (final chunk in _chunk(docs, _maxBatchSize)) { + final batch = firestore.batch(); + for (final data in chunk) { + batch.set(col.doc('seed_$docIndex'), data); + docIndex++; + } + await batch.commit(); + } +} + +Iterable> _chunk(List list, int size) sync* { + for (var i = 0; i < list.length; i += size) { + yield list.sublist(i, i + size > list.length ? list.length : i + size); + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_select_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_select_e2e.dart new file mode 100644 index 000000000000..4b14ad5864c8 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_select_e2e.dart @@ -0,0 +1,42 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineSelectTests() { + group('Pipeline select', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('select returns only selected fields in expected order', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('select')) + .sort(Expression.field('score').ascending()) + .select( + Expression.field('name').as('name'), + Expression.field('score').as('score'), + ) + .limit(3) + .execute(); + expectResultCount(snapshot, 3); + expectResultsData(snapshot, [ + {'name': 'doc1', 'score': 1}, + {'name': 'doc2', 'score': 2}, + {'name': 'doc3', 'score': 3}, + ]); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_test_helpers.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_test_helpers.dart new file mode 100644 index 000000000000..34a0cef69cfd --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_test_helpers.dart @@ -0,0 +1,50 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// Asserts that [snapshot] has exactly [count] results. +void expectResultCount(PipelineSnapshot snapshot, int count) { + expect( + snapshot.result.length, + count, + reason: 'Expected $count pipeline results', + ); +} + +/// Asserts that [snapshot] has the same number of results as [expectedData] +/// and each result's data() deep-equals the corresponding expected map. +/// Keys in [expectedData] are checked; extra keys in actual data are ignored +/// when [exactMatch] is false (default). Set [exactMatch] to true to require +/// identical keys. +void expectResultsData( + PipelineSnapshot snapshot, + List> expectedData, { + bool exactMatch = false, +}) { + expect( + snapshot.result.length, + expectedData.length, + reason: 'Result count mismatch', + ); + for (var i = 0; i < expectedData.length; i++) { + final result = snapshot.result[i]; + final data = result.data(); + expect(data, isNotNull, reason: 'Result $i has null data'); + final actual = data!; + final expected = expectedData[i]; + if (exactMatch) { + expect(actual, expected, reason: 'Result $i data mismatch'); + } else { + for (final entry in expected.entries) { + expect( + actual[entry.key], + entry.value, + reason: 'Result $i field ${entry.key}', + ); + } + } + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_unnest_union_e2e.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_unnest_union_e2e.dart new file mode 100644 index 000000000000..dac760d19043 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/integration_test/pipeline/pipeline_unnest_union_e2e.dart @@ -0,0 +1,63 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'pipeline_test_helpers.dart'; + +void runPipelineUnnestUnionTests() { + group('Pipeline unnest and union', () { + late FirebaseFirestore firestore; + + setUpAll(() { + firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + }); + + test('unnest produces one row per array element with tag field', () async { + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('unnest')) + .unnest(Expression.field('tags').as('tag')) + .sort(Expression.field('tag').ascending()) + .limit(10) + .execute(); + expectResultCount(snapshot, 5); + final tags = snapshot.result + .map((r) => r.data()!['tag'] as String) + .toList(); + expect(tags..sort(), [ + 'dart', + 'dart', + 'firestore', + 'firestore', + 'flutter', + ]); + }); + + test( + 'union concatenates both pipelines with deterministic total count', + () async { + final other = firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('union-b')) + .limit(5); + final snapshot = await firestore + .pipeline() + .collection('pipeline-e2e') + .where(Expression.field('test').equalValue('union-a')) + .limit(5) + .union(other) + .execute(); + expectResultCount(snapshot, 6); + }, + ); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/.gitignore b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/.gitignore new file mode 100644 index 000000000000..7a7f9873ad7d --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/AppFrameworkInfo.plist b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..1dc6cf7652ba --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 13.0 + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/Debug.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..ec97fc6f3021 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/Release.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..c4855bfe2000 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Podfile b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Podfile new file mode 100644 index 000000000000..6649374d4c19 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '15.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.pbxproj b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..238fb056204e --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,741 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2B45BBBA04F26970F9DC2428 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9D2411717BE352C92EC6263 /* Pods_RunnerTests.framework */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + D3B189D93111F21B593E07CE /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = FC9AF0E7C3463CF0E820724D /* GoogleService-Info.plist */; }; + ED8175A9DDB4B6F569867736 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 344F3B3BBCDBB7DF16FCD695 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 060D24293863437D08A9EF6D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 0A1494B03E7E4E5E8FCD37E4 /* 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 = ""; }; + 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 = ""; }; + 24A69F3F28EEBABDA3E3C6E5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 344F3B3BBCDBB7DF16FCD695 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9A6E43430478085B3F4B8179 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 9B52183BF5A24CC0FD358BC5 /* 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 = ""; }; + D44D6373BC6CC1B25B52520C /* 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 = ""; }; + D9D2411717BE352C92EC6263 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FC9AF0E7C3463CF0E820724D /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7A5921923875F445DD42E396 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2B45BBBA04F26970F9DC2428 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ED8175A9DDB4B6F569867736 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33AEEB36D1DBFD083FE19F82 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 344F3B3BBCDBB7DF16FCD695 /* Pods_Runner.framework */, + D9D2411717BE352C92EC6263 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 726BAFFB79E9610C171E5B05 /* Pods */ = { + isa = PBXGroup; + children = ( + 9B52183BF5A24CC0FD358BC5 /* Pods-Runner.debug.xcconfig */, + D44D6373BC6CC1B25B52520C /* Pods-Runner.release.xcconfig */, + 0A1494B03E7E4E5E8FCD37E4 /* Pods-Runner.profile.xcconfig */, + 060D24293863437D08A9EF6D /* Pods-RunnerTests.debug.xcconfig */, + 24A69F3F28EEBABDA3E3C6E5 /* Pods-RunnerTests.release.xcconfig */, + 9A6E43430478085B3F4B8179 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + FC9AF0E7C3463CF0E820724D /* GoogleService-Info.plist */, + 726BAFFB79E9610C171E5B05 /* Pods */, + 33AEEB36D1DBFD083FE19F82 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 11FC74FA85B59C7E267B4550 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 7A5921923875F445DD42E396 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 6F498101593ECA79F30C31E4 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + B4380A12E6068F3FEA627E84 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + D3B189D93111F21B593E07CE /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 11FC74FA85B59C7E267B4550 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 6F498101593ECA79F30C31E4 /* [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; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + B4380A12E6068F3FEA627E84 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + ); + 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 */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 060D24293863437D08A9EF6D /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 24A69F3F28EEBABDA3E3C6E5 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9A6E43430478085B3F4B8179 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..e3773d42e24c --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/AppDelegate.swift b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000000..626664468b89 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d36b1fab2d9d --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 000000000000..dc9ada4725e9 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000000..28c6bf03016f Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000000..2ccbfd967d96 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000000..f091b6b0bca8 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000000..4cde12118dda Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 000000000000..d0ef06e7edb8 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 000000000000..dcdc2306c285 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000000..2ccbfd967d96 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 000000000000..c8f9ed8f5cee Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000000..a6d6b8609df0 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000000..a6d6b8609df0 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000000..75b2d164a5a9 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000000..c4df70d39da7 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000000..6a84f41e14e2 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000000..d0e1f5853602 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000000..0bedcf2fd467 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 000000000000..9da19eacad3b Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 000000000000..9da19eacad3b Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 000000000000..9da19eacad3b Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Base.lproj/Main.storyboard b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Info.plist b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Info.plist new file mode 100644 index 000000000000..68b861da4bf1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Info.plist @@ -0,0 +1,72 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Pipeline Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + pipeline_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneDelegateClassName + FlutterSceneDelegate + UISceneConfigurationName + flutter + UISceneStoryboardFile + Main + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Runner-Bridging-Header.h b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000000..308a2a560b42 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/RunnerTests/RunnerTests.swift b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000000..b0d10f2e4304 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,10 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/lib/main.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/lib/main.dart new file mode 100644 index 000000000000..db42d904e7ff --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/lib/main.dart @@ -0,0 +1,1377 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; + +// Import after file is generated through flutterfire_cli. +// import 'firebase_options.dart'; + +const String _collectionId = 'pipeline_test_2'; + +bool shouldUseFirestoreEmulator = false; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + // Enable this line instead once you have the firebase_options.dart generated and + // imported through flutterfire_cli. + // await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + if (shouldUseFirestoreEmulator) { + FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080); + } + runApp(const PipelineExampleApp()); +} + +class PipelineExampleApp extends StatelessWidget { + const PipelineExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Pipeline Example', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const PipelineExamplePage(), + ); + } +} + +class _PipelineExamplePageState extends State { + final List _log = []; + bool _loading = false; + List>> _seedDocRefs = []; + + final _firestore = FirebaseFirestore.instanceFor( + app: Firebase.app(), + databaseId: 'firestore-pipeline-test', + ); + + void _logMessage(String message) { + setState(() { + _log.insert( + 0, + '[${DateTime.now().toString().substring(11, 19)}] $message', + ); + }); + } + + void _logError(String context, Object error, StackTrace? st) { + debugPrint('$context: $error'); + if (st != null) debugPrintStack(stackTrace: st); + } + + Future _seedCollection() async { + if (_loading) return; + setState(() { + _loading = true; + _log.clear(); + }); + _logMessage('Seeding collection "$_collectionId"...'); + + try { + final col = _firestore.collection(_collectionId); + + final batch = _firestore.batch(); + _seedDocRefs = []; + + final items = [ + {'title': 'Item A', 'score': 10, 'year': 2022, 'category': 'tech'}, + {'title': 'Item B', 'score': 25, 'year': 2023, 'category': 'tech'}, + {'title': 'Item C', 'score': 5, 'year': 2021, 'category': 'news'}, + {'title': 'Item D', 'score': 40, 'year': 2023, 'category': 'news'}, + {'title': 'Item E', 'score': 15, 'year': 2022, 'category': 'tech'}, + {'title': 'Item F', 'score': 30, 'year': 2024, 'category': 'news'}, + { + 'title': 'Item G', + 'score': 20, + 'year': 2023, + 'tags': ['x', 'y', 'z'], + }, + { + 'title': 'Item H', + 'score': 20, + 'year': 2023, + 'items': {'a': 'b', 'c': 'd'}, + }, + // For string expressions: trim, substring, stringReplaceAll, split, join + {'title': ' Padded ', 'score': 7, 'year': 2022, 'category': 'tech'}, + // For abs and conditional + { + 'title': 'Item Negative', + 'score': -12, + 'year': 2023, + 'category': 'news', + }, + // For array_length, array_sum, array_slice, array_concat + { + 'title': 'Item With Arrays', + 'score': 50, + 'scores': [10, 20, 30], + 'tags': ['p', 'q', 'r'], + 'year': 2024, + }, + // For if_absent (missing optional_field) + {'title': 'Item No Optional', 'score': 11, 'year': 2022}, + // Matches integration_test pipeline_seed "expressions" (for NOT / OR tests). + { + 'test': 'expressions', + 'score': 60, + 'a': 1, + 'b': 2, + 's': ' AbC ', + 'm': {'x': 10, 'y': 20}, + }, + { + 'test': 'expressions', + 'score': 70, + 'a': 10, + 'b': 20, + 's': 'xy', + 'm': {'x': 1}, + }, + {'test': 'expressions', 'score': 40, 'a': 5, 'b': 5}, + {'test': 'expressions', 'score': 80, 'a': 0, 'b': 100, 's': 'Hi'}, + { + 'test': 'expressions', + 'score': 50, + 'a': 1, + 'b': 2, + 'tags': ['p', 'q'], + }, + { + 'title': 'Regex Email Doc', + 'score': 99, + 'year': 2024, + 'category': 'tech', + 'email': 'demo@example.com', + 'pi': 3.14159, + }, + { + 'title': 'Dup Tags', + 'score': 8, + 'year': 2023, + 'category': 'tech', + 'tags': ['a', 'b', 'a'], + }, + ]; + + for (final item in items) { + final ref = col.doc(); + batch.set(ref, item); + if (_seedDocRefs.length < 2) _seedDocRefs.add(ref); + } + await batch.commit(); + _logMessage('Seeded ${items.length} documents.'); + } catch (e, st) { + _logError('Seed error', e, st); + } finally { + setState(() => _loading = false); + } + } + + /// Seeds the same data using individual set() calls (no batch). Useful for + /// debugging emulator issues where batch writes might behave differently. + Future _seedCollectionNoBatch() async { + if (_loading) return; + setState(() { + _loading = true; + _log.clear(); + }); + _logMessage('Seeding collection "$_collectionId" (no batch)...'); + + try { + final col = _firestore.collection(_collectionId); + _seedDocRefs = []; + + final items = [ + {'title': 'Item A', 'score': 10, 'year': 2022, 'category': 'tech'}, + {'title': 'Item B', 'score': 25, 'year': 2023, 'category': 'tech'}, + {'title': 'Item C', 'score': 5, 'year': 2021, 'category': 'news'}, + {'title': 'Item D', 'score': 40, 'year': 2023, 'category': 'news'}, + {'title': 'Item E', 'score': 15, 'year': 2022, 'category': 'tech'}, + {'title': 'Item F', 'score': 30, 'year': 2024, 'category': 'news'}, + { + 'title': 'Item G', + 'score': 20, + 'year': 2023, + 'tags': ['x', 'y', 'z'], + }, + { + 'title': 'Item H', + 'score': 20, + 'year': 2023, + 'items': {'a': 'b', 'c': 'd'}, + }, + {'title': ' Padded ', 'score': 7, 'year': 2022, 'category': 'tech'}, + { + 'title': 'Item Negative', + 'score': -12, + 'year': 2023, + 'category': 'news', + }, + { + 'title': 'Item With Arrays', + 'score': 50, + 'scores': [10, 20, 30], + 'tags': ['p', 'q', 'r'], + 'year': 2024, + }, + {'title': 'Item No Optional', 'score': 11, 'year': 2022}, + { + 'test': 'expressions', + 'score': 60, + 'a': 1, + 'b': 2, + 's': ' AbC ', + 'm': {'x': 10, 'y': 20}, + }, + { + 'test': 'expressions', + 'score': 70, + 'a': 10, + 'b': 20, + 's': 'xy', + 'm': {'x': 1}, + }, + {'test': 'expressions', 'score': 40, 'a': 5, 'b': 5}, + {'test': 'expressions', 'score': 80, 'a': 0, 'b': 100, 's': 'Hi'}, + { + 'test': 'expressions', + 'score': 50, + 'a': 1, + 'b': 2, + 'tags': ['p', 'q'], + }, + { + 'title': 'Regex Email Doc', + 'score': 99, + 'year': 2024, + 'category': 'tech', + 'email': 'demo@example.com', + 'pi': 3.14159, + }, + { + 'title': 'Dup Tags', + 'score': 8, + 'year': 2023, + 'category': 'tech', + 'tags': ['a', 'b', 'a'], + }, + ]; + + for (final item in items) { + final ref = col.doc(); + await ref.set(item); + if (_seedDocRefs.length < 2) _seedDocRefs.add(ref); + } + _logMessage('Seeded ${items.length} documents (no batch).'); + } catch (e, st) { + _logError('Seed (no batch) error', e, st); + } finally { + setState(() => _loading = false); + } + } + + Future _runPipeline( + String description, + Future Function() run, + ) async { + if (_loading) return; + setState(() { + _loading = true; + _log.clear(); + }); + _logMessage(description); + + try { + final snapshot = await run(); + _logMessage( + 'Found ${snapshot.result.length} result(s). Execution: ${snapshot.executionTime}', + ); + for (final r in snapshot.result) { + _logMessage(' doc: ${r.data()?.toString()}'); + } + } catch (e, st) { + _logError(description, e, st); + } finally { + setState(() => _loading = false); + } + } + + // 1: where + limit + Future _runPipeline1() => _runPipeline( + 'Pipeline 1: collection → where(score > 10) → limit(3)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('score').greaterThan(Expression.constant(10))) + .limit(3) + .execute(), + ); + + // 1b: execute with ExecuteOptions (indexMode: recommended) + Future _runPipelineExecuteOptions() => _runPipeline( + 'Pipeline 1b: same as 1 but execute(options: ExecuteOptions(indexMode: recommended))', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('score').greaterThan(Expression.constant(10))) + .limit(3) + .execute( + options: const ExecuteOptions(indexMode: IndexMode.recommended), + ), + ); + + // 2: select + Future _runPipeline2() => _runPipeline( + 'Pipeline 2: collection → where(year > 2022) → select(title, score, year) → limit(4)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('year').greaterThan(Expression.constant(2022))) + .select( + Expression.field('title'), + Expression.field('score'), + Expression.field('year'), + ) + .limit(4) + .execute(), + ); + + // 3: aggregate + Future _runPipeline3() => _runPipeline( + 'Pipeline 3: collection → aggregate(sum, avg, count_all)', + () => _firestore + .pipeline() + .collection(_collectionId) + .aggregate( + Expression.field('score').sum().as('total_score'), + Expression.field('score').average().as('avg_score'), + CountAll().as('doc_count'), + ) + .execute(), + ); + + // 4: addFields + Future _runPipeline4() => _runPipeline( + 'Pipeline 4: collection → addFields(score+100 as bonus) → limit(2)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field( + 'score', + ).add(Expression.constant(100)).as('bonus_score'), + ) + .limit(2) + .execute(), + ); + + // 5: distinct + Future _runPipeline5() => _runPipeline( + 'Pipeline 5: collection → distinct(category) → limit(5)', + () => _firestore + .pipeline() + .collection(_collectionId) + .distinct(Expression.field('category')) + .limit(5) + .execute(), + ); + + // 6: offset + Future _runPipeline6() => _runPipeline( + 'Pipeline 6: collection → limit(4) → offset(2)', + () => _firestore + .pipeline() + .collection(_collectionId) + .limit(4) + .offset(2) + .execute(), + ); + + // 7: removeFields + Future _runPipeline7() => _runPipeline( + 'Pipeline 7: collection → removeFields(category) → limit(2)', + () => _firestore + .pipeline() + .collection(_collectionId) + .removeFields('category') + .limit(2) + .execute(), + ); + + // 8: replaceWith + Future _runPipeline8() => _runPipeline( + 'Pipeline 8: collection → replaceWith(constant) → limit(1)', + () => _firestore + .pipeline() + .collection(_collectionId) + .replaceWith(Expression.field('items')) + // .limit(1) + .execute(), + ); + + // 9: sample + Future _runPipeline9() => _runPipeline( + 'Pipeline 9: collection → sample(size: 3)', + () => _firestore + .pipeline() + .collection(_collectionId) + .sample(PipelineSample.withSize(3)) + .execute(), + ); + + // 10: sort + Future _runPipeline10() => _runPipeline( + 'Pipeline 10: collection → sort(score desc) → limit(3)', + () => _firestore + .pipeline() + .collection(_collectionId) + .sort(Expression.field('score').descending()) + .limit(3) + .execute(), + ); + + // 11: aggregateStage with groups + Future _runPipeline11() => _runPipeline( + 'Pipeline 11: collection → aggregateStage(groups: category)', + () => _firestore + .pipeline() + .collection(_collectionId) + .aggregateWithOptions( + AggregateStageOptions( + accumulators: [ + Expression.field('score').sum().as('total'), + CountAll().as('count'), + ], + groups: [Expression.field('category')], + ), + ) + .execute(), + ); + + // 12: collectionGroup + Future _runPipeline12() => _runPipeline( + 'Pipeline 12: collectionGroup → limit(2)', + () => + _firestore.pipeline().collectionGroup(_collectionId).limit(2).execute(), + ); + + // 13: documents + Future _runPipeline13() async { + final col = _firestore.collection(_collectionId); + final ref1 = col.doc(); + final ref2 = col.doc(); + final refs = [ref1, ref2]; + await ref1.set({'title': 'Pipeline 13 doc 1', 'n': 1}); + await ref2.set({'title': 'Pipeline 13 doc 2', 'n': 2}); + return _runPipeline( + 'Pipeline 13: documents(ref1, ref2) → addFields(extra)', + () => _firestore + .pipeline() + .documents(refs) + .addFields(Expression.constant(1).as('extra')) + .execute(), + ); + } + + // 14: database + Future _runPipeline14() => _runPipeline( + 'Pipeline 14: database() → limit(2)', + () => _firestore.pipeline().database().execute(), + ); + + // 15: findNearest (may fail without vector index) + Future _runPipeline15() => _runPipeline( + 'Pipeline 15: collection → findNearest (needs vector index)', + () => _firestore + .pipeline() + .collection(_collectionId) + .findNearest( + Field('embedding'), + [0.1, 0.2, 0.3], + DistanceMeasure.cosine, + limit: 2, + ) + .execute(), + ); + + // 16: unnest + Future _runPipeline16() => _runPipeline( + 'Pipeline 16: collection → where(has tags) → unnest(tags) → limit(5)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('tags').exists()) + .unnest(Expression.field('tags'), 'index') + .limit(5) + .execute(), + ); + + // 17: union + Future _runPipeline17() => _runPipeline( + 'Pipeline 17: collection limit 2 → union(collection offset 2 limit 2)', + () { + final p2 = _firestore + .pipeline() + .collection(_collectionId) + .offset(2) + .limit(2); + return _firestore + .pipeline() + .collection(_collectionId) + .limit(2) + .union(p2) + .execute(); + }, + ); + + // 18: Constant — one addFields field per supported constant type + Future _runPipeline18() { + final docRef = _firestore.collection(_collectionId).doc('constant-test'); + // _firestore.doc() + return _runPipeline( + 'Pipeline 18: constant types — null, String, int, double, bool, ' + 'DateTime, Timestamp, GeoPoint, List, Blob, DocumentReference, VectorValue', + () => _firestore + .pipeline() + .collection(_collectionId) + .limit(1) + .addFields( + // VectorValue + Constant(const VectorValue([1.0, 2.0, 3.0])).as('c_vector'), + + Constant(null).as('c_null'), + // String + Constant('hello').as('c_string'), + // int + Constant(42).as('c_int'), + // double + Constant(3.14).as('c_double'), + // bool + Constant(true).as('c_bool'), + // DateTime + Constant(DateTime.utc(2024, 6, 15, 12, 0, 0)).as('c_date_time'), + // Timestamp + Constant(Timestamp(1718449200, 0)).as('c_timestamp'), + // GeoPoint + Constant(const GeoPoint(37.7749, -122.4194)).as('c_geo_point'), + // List (raw bytes) + Constant([72, 101, 108, 108, 111]).as('c_bytes'), + // // Blob + // Constant(Blob(Uint8List.fromList([1, 2, 3, 4, 5]))).as('c_blob'), + // DocumentReference + Constant(docRef).as('c_doc_ref'), + ) + .execute(), + ); + } + + // 19: Expression.and + Future _runPipeline19() => _runPipeline( + 'Pipeline 19: collection → where(and(score > 50, year >= 2022)) → select(title, score, year) → limit(5)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where( + Expression.and( + Expression.field('score').greaterThan(Expression.constant(20)), + Expression.field( + 'year', + ).greaterThanOrEqual(Expression.constant(2022)), + ), + ) + .select( + Expression.field('title'), + Expression.field('score'), + Expression.field('year'), + ) + .limit(5) + .execute(), + ); + + // 20: Expression.or + Future _runPipeline20() => _runPipeline( + 'Pipeline 20: collection → where(or(score > 80, year < 2021)) → select(title, score, year) → limit(5)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where( + Expression.or( + Expression.field('score').greaterThan(Expression.constant(30)), + Expression.field('year').lessThan(Expression.constant(2022)), + ), + ) + .select( + Expression.field('title'), + Expression.field('score'), + Expression.field('year'), + ) + .limit(5) + .execute(), + ); + + // 20b: Expression.not (same pattern as pipeline_expressions_e2e "where with not") + Future _runPipeline20b() => _runPipeline( + 'Pipeline 20b: where(test=expressions) + NOT(score>=60) + sort(score)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('test').equalValue('expressions')) + .where( + Expression.not( + Expression.field( + 'score', + ).greaterThanOrEqual(Expression.constant(60)), + ), + ) + .sort(Expression.field('score').ascending()) + .execute(), + ); + + // 21: arrayContainsAny + Future _runPipeline21() => _runPipeline( + 'Pipeline 21: collection → where(tags arrayContainsAny [x, z]) → select(title, tags) → limit(5)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('tags').arrayContainsAny(['x', 'z'])) + .select(Expression.field('title'), Expression.field('tags')) + .limit(5) + .execute(), + ); + + // ── New expression examples (22+) ───────────────────────────────────── + + // 22: concat + Future _runPipeline22() => _runPipeline( + 'Pipeline 22: addFields concat(title, " | ", category)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field( + 'title', + ).concat([' | ', Expression.field('category')]).as('title_category'), + ) + .limit(3) + .execute(), + ); + + // 23: length (string) + Future _runPipeline23() => _runPipeline( + 'Pipeline 23: addFields title.length()', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields(Expression.field('title').length().as('title_len')) + .limit(4) + .execute(), + ); + + // 24: toLowerCase / toUpperCase + Future _runPipeline24() => _runPipeline( + 'Pipeline 24: addFields toLowerCase(title), toUpperCase(category)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field('title').toLowerCase().as('title_lower'), + Expression.field('category').toUpperCase().as('category_upper'), + ) + .limit(3) + .execute(), + ); + + // 25: trim + Future _runPipeline25() => _runPipeline( + 'Pipeline 25: where(has title) → addFields trim(title)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('title').exists()) + .addFields(Expression.field('title').trim().as('title_trimmed')) + .limit(5) + .execute(), + ); + + // 26: substring + Future _runPipeline26() => _runPipeline( + 'Pipeline 26: addFields substring(title, 0, 5)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field('title').substringLiteral(0, 5).as('title_prefix'), + ) + .limit(4) + .execute(), + ); + + // 27: stringReplaceAll + // Future _runPipeline27() => _runPipeline( + // 'Pipeline 27: addFields stringReplaceAll(title, "Item", "Doc")', + // () => _firestore + // .pipeline() + // .collection(_collectionId) + // .addFields( + // Expression.field('title') + // .stringReplaceAllLiteral('Item', 'Doc') + // .as('title_replaced'), + // ) + // .limit(3) + // .execute(), + // ); + + // 28: split + Future _runPipeline28() => _runPipeline( + 'Pipeline 28: addFields split(title, " ")', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field('title').splitLiteral(' ').as('title_parts'), + ) + .limit(3) + .execute(), + ); + + // 29: join + Future _runPipeline29() => _runPipeline( + 'Pipeline 29: where(has tags) → addFields join(tags, "-")', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('tags').exists()) + .addFields(Expression.field('tags').joinLiteral('-').as('tags_joined')) + .limit(3) + .execute(), + ); + + // 30: if_absent + Future _runPipeline30() => _runPipeline( + 'Pipeline 30: addFields if_absent(optional_field, "default")', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field( + 'optional_field', + ).ifAbsentValue('default').as('opt_or_default'), + ) + .limit(4) + .execute(), + ); + + // 30b: if_error (e.g. safe divide) + Future _runPipeline30b() => _runPipeline( + 'Pipeline 30b: addFields score/0 with ifError("N/A")', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field( + 'score', + ).divide(Expression.constant(0)).ifErrorValue('N/A').as('safe_ratio'), + ) + .limit(2) + .execute(), + ); + + // 31: conditional + Future _runPipeline31() => _runPipeline( + 'Pipeline 31: addFields conditional(score > 20, "high", "low")', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.conditionalValues( + Expression.field('score').greaterThan(Expression.constant(20)), + 'high', + 'low', + ).as('score_tier'), + ) + .limit(5) + .execute(), + ); + + // 32: document_id (current document ID) + Future _runPipeline32() => _runPipeline( + 'Pipeline 32: addFields documentId()', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields(Expression.field('__path__').documentId().as('doc_id')) + .limit(3) + .execute(), + ); + + // 33: collection_id (current collection ID) + Future _runPipeline33() => _runPipeline( + 'Pipeline 33: addFields collectionId()', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields(Expression.field('__path__').collectionId().as('coll_id')) + .limit(2) + .execute(), + ); + + // 34: map_get, map_keys, map_values + Future _runPipeline34() => _runPipeline( + 'Pipeline 34: where(has items) → addFields mapGet, mapKeys, mapValues', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('items').exists()) + .addFields(Expression.field('items').mapGetLiteral('a').as('items_a')) + .limit(2) + .execute(), + ); + + // 35: current_timestamp, timestamp_add, timestamp_subtract, timestamp_truncate + Future _runPipeline35() => _runPipeline( + 'Pipeline 35: addFields currentTimestamp, timestampAdd(1 day)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.currentTimestamp().as('now'), + Expression.timestampAddLiteral( + Expression.currentTimestamp(), + 'day', + 1, + ).as('tomorrow'), + ) + .limit(1) + .execute(), + ); + + // 36: abs + Future _runPipeline36() => _runPipeline( + 'Pipeline 36: addFields abs(score)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields(Expression.field('score').abs().as('score_abs')) + .limit(5) + .execute(), + ); + + // 37: array_length + Future _runPipeline37() => _runPipeline( + 'Pipeline 37: where(has tags) → addFields arrayLength(tags)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('tags').exists()) + .addFields(Expression.field('tags').arrayLength().as('tags_len')) + .limit(5) + .execute(), + ); + + Future _runPipeline37b() => _runPipeline( + 'Pipeline 37b: where(has scores) → addFields arraySum(scores)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('scores').exists()) + .addFields(Expression.field('scores').arraySum().as('scores_total')) + .limit(3) + .execute(), + ); + + // 38: array_concat + Future _runPipeline38() => _runPipeline( + 'Pipeline 38: where(has tags) → addFields arrayConcat(tags, [extra])', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('tags').exists()) + .addFields( + Expression.field('tags').arrayConcat(['extra']).as('tags_extended'), + ) + .limit(2) + .execute(), + ); + + // 40: array (construct) + Future _runPipeline40() => _runPipeline( + 'Pipeline 40: addFields array([title, score, year])', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.array([ + Expression.field('title'), + Expression.field('score'), + Expression.field('year'), + ]).as('tuple'), + ) + .limit(2) + .execute(), + ); + + // 41: map (construct) + Future _runPipeline41() => _runPipeline( + 'Pipeline 41: addFields map({ t: title, s: score })', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.map({ + 't': Expression.field('title'), + 's': Expression.field('score'), + }).as('mini_map'), + ) + .limit(2) + .execute(), + ); + + // 42: array_contains_all (values list) + Future _runPipeline42() => _runPipeline( + 'Pipeline 42: where(tags arrayContainsAll [x, y]) → select title, tags', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('tags').arrayContainsAll(['x', 'y'])) + .select(Expression.field('title'), Expression.field('tags')) + .limit(5) + .execute(), + ); + + // 43: equal_any (IN) + Future _runPipeline43() => _runPipeline( + 'Pipeline 43: where(score equalAny [10, 25, 40]) → select title, score', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.equalAny(Expression.field('score'), [10, 25, 40])) + .select(Expression.field('title'), Expression.field('score')) + .limit(5) + .execute(), + ); + + // 44: not_equal_any (NOT IN) + Future _runPipeline44() => _runPipeline( + 'Pipeline 44: where(category notEqualAny [news]) → select title, category', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.notEqualAny(Expression.field('category'), ['news'])) + .select(Expression.field('title'), Expression.field('category')) + .limit(5) + .execute(), + ); + + // 45: asBoolean (coerce numeric to boolean) + Future _runPipeline45() => _runPipeline( + 'Pipeline 45: addFields asBoolean(score)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields(Expression.field('score').asBoolean().as('score_bool')) + .limit(4) + .execute(), + ); + + // 46: isError (missing field vs divide-by-zero) + Future _runPipeline46() => _runPipeline( + 'Pipeline 46: addFields isError(missing field), isError(score/0)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields( + Expression.field('missing_field').isError().as('missing_is_err'), + Expression.field( + 'score', + ).divide(Expression.constant(0)).isError().as('div0_is_err'), + ) + .limit(3) + .execute(), + ); + + // ── New pipeline expressions (regex, map, string, array, agg) ─────────── + + Future _runPipeline47() => _runPipeline( + 'Pipeline 47: where(has email) → addFields regexFind(@.+)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('email').exists()) + .addFields(Expression.field('email').regexFind('@.+').as('at_domain')) + .limit(5) + .execute(), + ); + + Future _runPipeline48() => _runPipeline( + 'Pipeline 48: where(has email) → addFields regexFindAll([a-z]+)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('email').exists()) + .addFields( + Expression.field('email').regexFindAll('[a-z]+').as('word_chunks'), + ) + .limit(5) + .execute(), + ); + + Future _runPipeline49() => _runPipeline( + 'Pipeline 49: test=expressions + has s → stringReplaceOne, stringIndexOf, stringRepeat', + () => _firestore + .pipeline() + .collection(_collectionId) + .where( + Expression.and( + Expression.field('test').equalValue('expressions'), + Expression.field('s').exists(), + ), + ) + .addFields( + Expression.field( + 's', + ).stringReplaceOneLiteral('A', 'Z').as('s_replace_one'), + Expression.field('s').stringIndexOf('y').as('idx_y'), + Expression.field('s').stringRepeat(2).as('s_twice'), + ) + .limit(8) + .execute(), + ); + + Future _runPipeline50() => _runPipeline( + 'Pipeline 50: title " Padded " → ltrim, rtrim', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('title').equalValue(' Padded ')) + .addFields( + Expression.field('title').ltrim().as('lt'), + Expression.field('title').rtrim().as('rt'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline51() => _runPipeline( + 'Pipeline 51: where(has items) → mapSet(z), mapEntries()', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('items').exists()) + .addFields( + Expression.field('items').mapSet('z', 'added').as('items_plus'), + Expression.field('items').mapEntries().as('items_entries'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline52() => _runPipeline( + 'Pipeline 52: addFields type(score)', + () => _firestore + .pipeline() + .collection(_collectionId) + .addFields(Expression.field('score').type().as('score_type')) + .limit(6) + .execute(), + ); + + Future _runPipeline53() => _runPipeline( + 'Pipeline 53: where(score isType int64) → select title, score', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('score').isType(Type.int64)) + .select(Expression.field('title'), Expression.field('score')) + .limit(8) + .execute(), + ); + + Future _runPipeline54() => _runPipeline( + 'Pipeline 54: where(has pi) → trunc(pi), trunc(2dp), rand()', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('pi').exists()) + .addFields( + Expression.field('pi').trunc().as('pi_trunc'), + Expression.field('pi').trunc(Expression.constant(2)).as('pi_2'), + Expression.rand().as('rnd'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline55() => _runPipeline( + 'Pipeline 55: title Item G → arrayFirst, arrayLast(tags)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('title').equalValue('Item G')) + .addFields( + Expression.field('tags').arrayFirst().as('tag_first'), + Expression.field('tags').arrayLast().as('tag_last'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline56() => _runPipeline( + 'Pipeline 56: Item G → arrayFirstN(2), arrayLastN(2)(tags)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('title').equalValue('Item G')) + .addFields( + Expression.field('tags').arrayFirstN(2).as('tags_head'), + Expression.field('tags').arrayLastN(2).as('tags_tail'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline57() => _runPipeline( + 'Pipeline 57: where(has scores) → arrayMaximum, arrayMinimum', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('scores').exists()) + .addFields( + Expression.field('scores').arrayMaximum().as('smax'), + Expression.field('scores').arrayMinimum().as('smin'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline58() => _runPipeline( + 'Pipeline 58: where(has scores) → arrayMaximumN(2), arrayMinimumN(2)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('scores').exists()) + .addFields( + Expression.field('scores').arrayMaximumN(2).as('top2'), + Expression.field('scores').arrayMinimumN(2).as('bottom2'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline59() => _runPipeline( + 'Pipeline 59: Dup Tags → arrayIndexOf(a), arrayLastIndexOf(a), arrayIndexOfAll(a)', + () => _firestore + .pipeline() + .collection(_collectionId) + .where(Expression.field('title').equalValue('Dup Tags')) + .addFields( + Expression.field('tags').arrayIndexOf('a').as('idx_first_a'), + Expression.field('tags').arrayLastIndexOf('a').as('idx_last_a'), + Expression.field('tags').arrayIndexOfAll('a').as('all_a'), + ) + .limit(3) + .execute(), + ); + + Future _runPipeline60() => _runPipeline( + 'Pipeline 60: aggregate first(score), last(score)', + () => _firestore + .pipeline() + .collection(_collectionId) + .limit(50) + .aggregate( + Expression.field('score').first().as('first_score'), + Expression.field('score').last().as('last_score'), + ) + .execute(), + ); + + Future _runPipeline61() => _runPipeline( + 'Pipeline 61: limit 25 → aggregate array_agg(title)', + () => _firestore + .pipeline() + .collection(_collectionId) + .limit(25) + .aggregate(Expression.field('title').arrayAgg().as('all_titles')) + .execute(), + ); + + Future _runPipeline62() => _runPipeline( + 'Pipeline 62: limit 25 → aggregate array_agg_distinct(category)', + () => _firestore + .pipeline() + .collection(_collectionId) + .limit(25) + .aggregate(Expression.field('category').arrayAggDistinct().as('cats')) + .execute(), + ); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Pipeline Example'), + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + '1. Seed data, then run pipeline queries. Errors go to console only.', + style: TextStyle(fontWeight: FontWeight.w500), + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: FilledButton( + onPressed: _loading ? null : _seedCollection, + child: const Text('Seed collection'), + ), + ), + const SizedBox(width: 8), + Expanded( + child: FilledButton( + onPressed: _loading ? null : _seedCollectionNoBatch, + child: const Text('Seed (no batch)'), + ), + ), + ], + ), + if (_loading) const LinearProgressIndicator(), + ], + ), + ), + const SizedBox(height: 8), + Expanded( + flex: 1, + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _btn('1: where+limit', _runPipeline1), + _btn('1b: execute(options)', _runPipelineExecuteOptions), + _btn('2: select', _runPipeline2), + _btn('3: aggregate', _runPipeline3), + _btn('4: addFields', _runPipeline4), + _btn('5: distinct', _runPipeline5), + _btn('6: offset', _runPipeline6), + _btn('7: removeFields', _runPipeline7), + _btn('8: replaceWith', _runPipeline8), + _btn('9: sample', _runPipeline9), + _btn('10: sort', _runPipeline10), + _btn('11: aggregateStage', _runPipeline11), + _btn('12: collectionGroup', _runPipeline12), + _btn('13: documents', _runPipeline13), + _btn('14: database', _runPipeline14), + _btn('15: findNearest', _runPipeline15), + _btn('16: unnest', _runPipeline16), + _btn('17: union', _runPipeline17), + _btn('18: constants', _runPipeline18), + _btn('19: and', _runPipeline19), + _btn('20: or', _runPipeline20), + _btn('20b: NOT (score≥60)', _runPipeline20b), + _btn('21: arrayContainsAny', _runPipeline21), + _btn('22: concat', _runPipeline22), + _btn('23: length', _runPipeline23), + _btn('24: lower/upper', _runPipeline24), + _btn('25: trim', _runPipeline25), + _btn('26: substring', _runPipeline26), + _btn('28: split', _runPipeline28), + _btn('29: join', _runPipeline29), + _btn('30: if_absent', _runPipeline30), + _btn('30b: if_error', _runPipeline30b), + _btn('31: conditional', _runPipeline31), + _btn('32: documentId', _runPipeline32), + _btn('33: collectionId', _runPipeline33), + _btn('34: mapGet/Keys/Vals', _runPipeline34), + _btn('35: timestamp', _runPipeline35), + _btn('36: abs', _runPipeline36), + _btn('37: arrayLen', _runPipeline37), + _btn('37b: arraySum', _runPipeline37b), + _btn('38: arrayConcat', _runPipeline38), + _btn('40: array()', _runPipeline40), + _btn('41: map()', _runPipeline41), + _btn('42: arrayContainsAll', _runPipeline42), + _btn('43: equalAny', _runPipeline43), + _btn('44: notEqualAny', _runPipeline44), + _btn('45: asBoolean', _runPipeline45), + _btn('46: isError', _runPipeline46), + _btn('47: regexFind', _runPipeline47), + _btn('48: regexFindAll', _runPipeline48), + _btn('49: string idx/repeat', _runPipeline49), + _btn('50: ltrim/rtrim', _runPipeline50), + _btn('51: mapSet/entries', _runPipeline51), + _btn('52: type(score)', _runPipeline52), + _btn('53: isType int64', _runPipeline53), + _btn('54: trunc/rand', _runPipeline54), + _btn('55: array first/last', _runPipeline55), + _btn('56: arrayFirstN/LastN', _runPipeline56), + _btn('57: array max/min', _runPipeline57), + _btn('58: arrayMaxN/MinN', _runPipeline58), + _btn('59: arrayIndexOf*', _runPipeline59), + _btn('60: agg first/last', _runPipeline60), + _btn('61: agg array_agg', _runPipeline61), + _btn('62: agg array_agg_dist', _runPipeline62), + ], + ), + ), + ), + const Divider(height: 1), + Expanded( + flex: 2, + child: _log.isEmpty + ? const Center( + child: Text( + 'Log output will appear here.\nTap "Seed collection" then run a pipeline.', + textAlign: TextAlign.center, + ), + ) + : ListView.builder( + padding: const EdgeInsets.all(8), + itemCount: _log.length, + itemBuilder: (context, i) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: SelectableText( + _log[i], + style: const TextStyle( + fontFamily: 'monospace', + fontSize: 12, + ), + ), + ); + }, + ), + ), + ], + ), + ); + } + + Widget _btn(String label, VoidCallback onPressed) { + return FilledButton.tonal( + onPressed: _loading ? null : onPressed, + child: Text(label), + ); + } +} + +class PipelineExamplePage extends StatefulWidget { + const PipelineExamplePage({super.key}); + + @override + State createState() => _PipelineExamplePageState(); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/.gitignore b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/.gitignore new file mode 100644 index 000000000000..746adbb6b9e1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Flutter/Flutter-Debug.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000000..4b81f9b2d200 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Flutter/Flutter-Release.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000000..5caa9d1579e4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Podfile b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Podfile new file mode 100644 index 000000000000..ff5ddb3b8bdc --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/project.pbxproj b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..0d3da44c98d4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,805 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 4D21D57616F29244D9E0BEDC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92A5244FE5D2154A5DCF67B1 /* Pods_Runner.framework */; }; + 64BEAD2D383FBE055A9293F9 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD0A02E97A5F21BBF52ECDE7 /* Pods_RunnerTests.framework */; }; + C00753EECC149CA245836275 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 68F38BAC755C5AD9D75FABDC /* GoogleService-Info.plist */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* pipeline_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = pipeline_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 450310D9B717CF9798DF9983 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 4C6E70E606BD35A2DDE28810 /* 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 = ""; }; + 68F38BAC755C5AD9D75FABDC /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 80B3CDA44BB01B7665B13AC4 /* 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 = ""; }; + 80C48A48FDB72942482DF20F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 92A5244FE5D2154A5DCF67B1 /* 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 = ""; }; + BD0A02E97A5F21BBF52ECDE7 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C6DA53DEF649E7FAF765B034 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + EAA74D9E6B471BF39D05E2D5 /* 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 */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 64BEAD2D383FBE055A9293F9 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D21D57616F29244D9E0BEDC /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2ACCD496E1849E7F631515BA /* Pods */ = { + isa = PBXGroup; + children = ( + 80B3CDA44BB01B7665B13AC4 /* Pods-Runner.debug.xcconfig */, + EAA74D9E6B471BF39D05E2D5 /* Pods-Runner.release.xcconfig */, + 4C6E70E606BD35A2DDE28810 /* Pods-Runner.profile.xcconfig */, + 80C48A48FDB72942482DF20F /* Pods-RunnerTests.debug.xcconfig */, + C6DA53DEF649E7FAF765B034 /* Pods-RunnerTests.release.xcconfig */, + 450310D9B717CF9798DF9983 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 68F38BAC755C5AD9D75FABDC /* GoogleService-Info.plist */, + 2ACCD496E1849E7F631515BA /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* pipeline_example.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 92A5244FE5D2154A5DCF67B1 /* Pods_Runner.framework */, + BD0A02E97A5F21BBF52ECDE7 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 99A85E0C4CFD12F263C48149 /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 8E689660D67B59A471E12131 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + AFC4EC65143ED624B99B07E7 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* pipeline_example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + C00753EECC149CA245836275 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 8E689660D67B59A471E12131 /* [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; + }; + 99A85E0C4CFD12F263C48149 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + AFC4EC65143ED624B99B07E7 /* [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 */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 80C48A48FDB72942482DF20F /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pipeline_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pipeline_example"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C6DA53DEF649E7FAF765B034 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pipeline_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pipeline_example"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 450310D9B717CF9798DF9983 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pipeline_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pipeline_example"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..75fc171f713c --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/AppDelegate.swift b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000000..b3c176141221 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..a2ec33f19f11 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 000000000000..82b6f9d9a33e Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 000000000000..13b35eba55c6 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 000000000000..0a3f5fa40fb3 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 000000000000..bdb57226d5f2 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 000000000000..f083318e09ca Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 000000000000..326c0e72c9d8 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 000000000000..2f1632cfddf3 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Base.lproj/MainMenu.xib b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 000000000000..80e867a4e06b --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/AppInfo.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000000..62772134902e --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = pipeline_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.pipelineExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved. diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Debug.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000000..36b0fd9464f4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Release.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000000..dff4f49561c8 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Warnings.xcconfig b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000000..42bcbf4780b1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/DebugProfile.entitlements b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000000..dddb8a30c851 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Info.plist b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Info.plist new file mode 100644 index 000000000000..4789daa6a443 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/MainFlutterWindow.swift b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000000..3cc05eb23491 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Release.entitlements b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Release.entitlements new file mode 100644 index 000000000000..852fa1a4728a --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/RunnerTests/RunnerTests.swift b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000000..4fc85d1c0db6 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,10 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/pubspec.yaml b/packages/cloud_firestore/cloud_firestore/pipeline_example/pubspec.yaml new file mode 100644 index 000000000000..92730168658a --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/pubspec.yaml @@ -0,0 +1,96 @@ +name: pipeline_example +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 +resolution: workspace + +environment: + sdk: ^3.10.0-290.4.beta + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + cloud_firestore: + path: .. + firebase_core: ^4.11.0 + flutter: + sdk: flutter + http: ^1.0.0 + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^6.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/test/widget_test.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/test/widget_test.dart new file mode 100644 index 000000000000..7cae948eade1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/test/widget_test.dart @@ -0,0 +1,34 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:pipeline_example/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const PipelineExampleApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/test_driver/integration_test.dart b/packages/cloud_firestore/cloud_firestore/pipeline_example/test_driver/integration_test.dart new file mode 100644 index 000000000000..5366642ffab1 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/web/favicon.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/favicon.png new file mode 100644 index 000000000000..8aaa46ac1ae2 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/favicon.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-192.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-192.png new file mode 100644 index 000000000000..b749bfef0747 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-192.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-512.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-512.png new file mode 100644 index 000000000000..88cfd48dff11 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-512.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-maskable-192.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-maskable-192.png new file mode 100644 index 000000000000..eb9b4d76e525 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-maskable-192.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-maskable-512.png b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-maskable-512.png new file mode 100644 index 000000000000..d69c56691fbd Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/icons/Icon-maskable-512.png differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/web/index.html b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/index.html new file mode 100644 index 000000000000..1088bdfe5d11 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + pipeline_example + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/web/manifest.json b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/manifest.json new file mode 100644 index 000000000000..17f17dafb5ed --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "pipeline_example", + "short_name": "pipeline_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/cloud_firestore/cloud_firestore/pipeline_example/windows/.gitignore b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/CMakeLists.txt b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/CMakeLists.txt new file mode 100644 index 000000000000..0b27cba15e3a --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(pipeline_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "pipeline_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/flutter/CMakeLists.txt b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..903f4899d6fc --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/CMakeLists.txt b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..394917c053a0 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/Runner.rc b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/Runner.rc new file mode 100644 index 000000000000..c687adb36fc4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "pipeline_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "pipeline_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "pipeline_example.exe" "\0" + VALUE "ProductName", "pipeline_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/flutter_window.cpp b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..5ebed39dfc68 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/flutter_window.cpp @@ -0,0 +1,73 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { this->Show(); }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/flutter_window.h b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..7f8ccba43748 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/flutter_window.h @@ -0,0 +1,37 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/main.cpp b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/main.cpp new file mode 100644 index 000000000000..f465371f6806 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"pipeline_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/resource.h b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/resource.h new file mode 100644 index 000000000000..212b07c88aed --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/resource.h @@ -0,0 +1,20 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/resources/app_icon.ico b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/resources/app_icon.ico new file mode 100644 index 000000000000..c04e20caf637 Binary files /dev/null and b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/resources/app_icon.ico differ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/runner.exe.manifest b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..153653e8d67f --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/utils.cpp b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/utils.cpp new file mode 100644 index 000000000000..f3c6a5e94098 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/utils.cpp @@ -0,0 +1,69 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr) - + 1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, input_length, + utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/utils.h b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/utils.h new file mode 100644 index 000000000000..b2cf607427d7 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/win32_window.cpp b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..e25d13f9d076 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/win32_window.cpp @@ -0,0 +1,284 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: +/// https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = + L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { return ShowWindow(window_handle_, SW_SHOWNORMAL); } + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = + RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD, nullptr, + &light_mode, &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/win32_window.h b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/win32_window.h new file mode 100644 index 000000000000..190ad00d51e4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/pipeline_example/windows/runner/win32_window.h @@ -0,0 +1,104 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/cloud_firestore/cloud_firestore/pubspec.yaml b/packages/cloud_firestore/cloud_firestore/pubspec.yaml index 8b5e8c826ab6..9502c092d57b 100755 --- a/packages/cloud_firestore/cloud_firestore/pubspec.yaml +++ b/packages/cloud_firestore/cloud_firestore/pubspec.yaml @@ -4,7 +4,8 @@ description: live synchronization and offline support on Android and iOS. homepage: https://firebase.google.com/docs/firestore repository: https://github.com/firebase/flutterfire/tree/main/packages/cloud_firestore/cloud_firestore -version: 6.2.0 +version: 6.6.0 +resolution: workspace topics: - firebase - firestore @@ -16,15 +17,15 @@ false_secrets: - dartpad/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - cloud_firestore_platform_interface: ^7.1.0 - cloud_firestore_web: ^5.2.0 + cloud_firestore_platform_interface: ^8.0.3 + cloud_firestore_web: ^5.6.0 collection: ^1.0.0 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter meta: ^1.8.0 diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_aggregate_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_aggregate_test.dart new file mode 100644 index 000000000000..60c1ac3895ec --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_aggregate_test.dart @@ -0,0 +1,201 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import './mock.dart'; + +void main() { + setupCloudFirestoreMocks(); + + setUpAll(() async { + await Firebase.initializeApp(); + }); + + group('PipelineAggregateFunction', () { + test('CountAll has name count_all and simple toMap', () { + final fn = CountAll(); + expect(fn.name, 'count_all'); + expect(fn.toMap(), {'name': 'count_all'}); + }); + + test('Count serializes with expression', () { + final fn = Count(Field('amount')); + expect(fn.toMap(), { + 'name': 'count', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'amount'}, + }, + }, + }); + }); + + test('Sum serializes with expression', () { + final fn = Sum(Field('total')); + expect(fn.toMap(), { + 'name': 'sum', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'total'}, + }, + }, + }); + }); + + test('Average serializes with expression', () { + final fn = Average(Field('score')); + expect(fn.toMap(), { + 'name': 'average', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'score'}, + }, + }, + }); + }); + + test('CountDistinct serializes with expression', () { + final fn = CountDistinct(Field('category')); + expect(fn.toMap(), { + 'name': 'count_distinct', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'category'}, + }, + }, + }); + }); + + test('Minimum serializes with expression', () { + final fn = Minimum(Field('price')); + expect(fn.toMap(), { + 'name': 'minimum', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'price'}, + }, + }, + }); + }); + + test('Maximum serializes with expression', () { + final fn = Maximum(Field('price')); + expect(fn.toMap(), { + 'name': 'maximum', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'price'}, + }, + }, + }); + }); + + test('First serializes with expression', () { + final fn = First(Field('rating')); + expect(fn.toMap(), { + 'name': 'first', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'rating'}, + }, + }, + }); + }); + + test('Last serializes with expression', () { + final fn = Last(Field('rating')); + expect(fn.toMap(), { + 'name': 'last', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'rating'}, + }, + }, + }); + }); + + test('ArrayAgg serializes with expression', () { + final fn = ArrayAgg(Field('tags')); + expect(fn.toMap(), { + 'name': 'array_agg', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'tags'}, + }, + }, + }); + }); + + test('ArrayAggDistinct serializes with expression', () { + final fn = ArrayAggDistinct(Field('tags')); + expect(fn.toMap(), { + 'name': 'array_agg_distinct', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'tags'}, + }, + }, + }); + }); + }); + + group('AliasedAggregateFunction', () { + test('toMap includes alias and aggregate_function', () { + final aliased = CountAll().as('totalCount'); + expect(aliased.alias, 'totalCount'); + final map = aliased.toMap(); + expect(map['name'], 'alias'); + expect(map['args']['alias'], 'totalCount'); + expect(map['args']['aggregate_function'], {'name': 'count_all'}); + }); + + test('wraps expression-based aggregate', () { + final aliased = Sum(Field('amount')).as('total'); + expect(aliased.alias, 'total'); + final map = aliased.toMap(); + expect(map['args']['aggregate_function']['name'], 'sum'); + }); + }); + + group('AggregateStageOptions', () { + test('toMap with accumulators only', () { + final options = AggregateStageOptions( + accumulators: [CountAll().as('count')], + ); + final map = options.toMap(); + expect(map['accumulators'], hasLength(1)); + expect(map.containsKey('groups'), isFalse); + }); + + test('toMap with accumulators and groups', () { + final options = AggregateStageOptions( + accumulators: [Sum(Field('x')).as('sumX')], + groups: [Field('category')], + ); + final map = options.toMap(); + expect(map['accumulators'], hasLength(1)); + expect(map['groups'], hasLength(1)); + }); + }); + + group('AggregateOptions', () { + test('toMap returns empty map', () { + final options = AggregateOptions(); + expect(options.toMap(), isEmpty); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_distance_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_distance_test.dart new file mode 100644 index 000000000000..b4d85b489ee5 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_distance_test.dart @@ -0,0 +1,29 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('DistanceMeasure', () { + test('cosine has expected name', () { + expect(DistanceMeasure.cosine.name, 'cosine'); + }); + + test('euclidean has expected name', () { + expect(DistanceMeasure.euclidean.name, 'euclidean'); + }); + + test('dotProduct has expected name', () { + expect(DistanceMeasure.dotProduct.name, 'dotProduct'); + }); + + test('has exactly three values', () { + expect(DistanceMeasure.values, hasLength(3)); + expect(DistanceMeasure.values, contains(DistanceMeasure.cosine)); + expect(DistanceMeasure.values, contains(DistanceMeasure.euclidean)); + expect(DistanceMeasure.values, contains(DistanceMeasure.dotProduct)); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_execute_options_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_execute_options_test.dart new file mode 100644 index 000000000000..cdd91f7b32c7 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_execute_options_test.dart @@ -0,0 +1,21 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('IndexMode', () { + test('recommended has expected name', () { + expect(IndexMode.recommended.name, 'recommended'); + }); + }); + + group('ExecuteOptions', () { + test('default indexMode is recommended', () { + const options = ExecuteOptions(); + expect(options.indexMode, IndexMode.recommended); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_expression_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_expression_test.dart new file mode 100644 index 000000000000..468f373ba404 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_expression_test.dart @@ -0,0 +1,1105 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import './mock.dart'; + +void main() { + setupCloudFirestoreMocks(); + + late FirebaseFirestore firestore; + + setUpAll(() async { + await Firebase.initializeApp(); + firestore = FirebaseFirestore.instance; + }); + + group('Field', () { + test('toMap returns field name structure', () { + final expr = Field('name'); + expect(expr.toMap(), { + 'name': 'field', + 'args': {'field': 'name'}, + }); + }); + + test('nested field path serializes correctly', () { + final expr = Field('user.profile.displayName'); + expect(expr.toMap()['args']['field'], 'user.profile.displayName'); + }); + }); + + group('Constant', () { + test('toMap for null', () { + final expr = Constant(null); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': null}, + }); + }); + + test('toMap for number', () { + final expr = Constant(42); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': 42}, + }); + }); + + test('toMap for double', () { + final expr = Constant(3.14); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': 3.14}, + }); + }); + + test('toMap for string', () { + final expr = Constant('hello'); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': 'hello'}, + }); + }); + + test('toMap for bool', () { + final expr = Constant(true); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': true}, + }); + }); + + test('toMap for DateTime', () { + final dt = DateTime.utc(2025, 1, 15); + final expr = Constant(dt); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': dt}, + }); + }); + + test('toMap for Timestamp', () { + final ts = Timestamp.fromDate(DateTime.utc(2025, 3, 10)); + final expr = Constant(ts); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': ts}, + }); + }); + + test('toMap for GeoPoint', () { + const gp = GeoPoint(52, 4); + final expr = Constant(gp); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': gp}, + }); + }); + + test('toMap for List (bytes)', () { + final bytes = [1, 2, 3]; + final expr = Constant(bytes); + expect( + expr.toMap(), + { + 'name': 'constant', + 'args': { + 'value': [1, 2, 3], + }, + }, + ); + }); + + test('toMap for Blob', () { + final blob = Blob(Uint8List.fromList([1, 2, 3])); + final expr = Constant(blob); + expect( + expr.toMap(), + { + 'name': 'constant', + 'args': { + 'value': blob, + }, + }, + ); + }); + + test('toMap for DocumentReference serializes path', () { + final ref = firestore.collection('users').doc('alice'); + final expr = Constant(ref); + expect(expr.toMap(), { + 'name': 'constant', + 'args': { + 'value': { + 'path': 'users/alice', + }, + }, + }); + }); + + test('toMap for VectorValue', () { + const vec = VectorValue([1.0, 2.0, 3.0]); + final expr = Constant(vec); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': vec}, + }); + }); + + test('throws ArgumentError for invalid value type', () { + expect( + () => Constant({'key': 'value'}), + throwsA( + isA().having( + (e) => e.message, + 'message', + allOf(contains('Constant value must be'), contains('Got:')), + ), + ), + ); + }); + + test('throws ArgumentError for List (not List)', () { + expect( + () => Constant(['a', 'b']), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('Got:'), + ), + ), + ); + }); + }); + + group('Expression static constructors', () { + test('Expression.field() returns Field with path', () { + final expr = Expression.field('amount'); + expect(expr, isA()); + expect(expr.toMap()['args']['field'], 'amount'); + }); + + test('Expression.constant() wraps value', () { + final expr = Expression.constant(100); + expect(expr.toMap(), { + 'name': 'constant', + 'args': {'value': 100}, + }); + }); + + test('Expression.documentMatches() serializes search query', () { + final expr = Expression.documentMatches('breakfast -diner'); + expect(expr.toMap(), { + 'name': 'document_matches', + 'args': {'query': 'breakfast -diner'}, + }); + }); + }); + + group('BooleanExpression from Field', () { + test('equal serializes correctly', () { + final expr = Field('age').equal(Constant(18)); + expect(expr.toMap(), { + 'name': 'equal', + 'args': { + 'left': { + 'name': 'field', + 'args': {'field': 'age'}, + }, + 'right': { + 'name': 'constant', + 'args': {'value': 18}, + }, + }, + }); + }); + + test('greaterThan serializes correctly', () { + final expr = Field('score').greaterThan(Constant(0)); + expect(expr.toMap(), { + 'name': 'greater_than', + 'args': { + 'left': { + 'name': 'field', + 'args': {'field': 'score'}, + }, + 'right': { + 'name': 'constant', + 'args': {'value': 0}, + }, + }, + }); + }); + + test('exists serializes correctly', () { + final expr = Field('email').exists(); + expect(expr.toMap(), { + 'name': 'exists', + 'args': { + 'expression': { + 'name': 'field', + 'args': {'field': 'email'}, + }, + }, + }); + }); + + test('notEqual serializes correctly', () { + final expr = Field('x').notEqual(Constant(0)); + expect(expr.toMap(), { + 'name': 'not_equal', + 'args': { + 'left': { + 'name': 'field', + 'args': {'field': 'x'}, + }, + 'right': { + 'name': 'constant', + 'args': {'value': 0}, + }, + }, + }); + }); + + test('lessThan serializes correctly', () { + final expr = Field('n').lessThan(Constant(10)); + expect(expr.toMap()['name'], 'less_than'); + expect(expr.toMap()['args']['left']['args']['field'], 'n'); + expect(expr.toMap()['args']['right']['args']['value'], 10); + }); + + test('lessThanOrEqual serializes correctly', () { + final expr = Field('n').lessThanOrEqual(Constant(5)); + expect(expr.toMap()['name'], 'less_than_or_equal'); + }); + + test('greaterThanOrEqual serializes correctly', () { + final expr = Field('n').greaterThanOrEqual(Constant(1)); + expect(expr.toMap()['name'], 'greater_than_or_equal'); + }); + }); + + group('Ordering from Expression', () { + test('ascending() returns Ordering with asc', () { + final ordering = Field('name').ascending(); + expect(ordering.direction, OrderDirection.asc); + expect(ordering.toMap()['order_direction'], 'asc'); + }); + + test('descending() returns Ordering with desc', () { + final ordering = Field('created').descending(); + expect(ordering.direction, OrderDirection.desc); + expect(ordering.toMap()['order_direction'], 'desc'); + }); + }); + + group('Aliased expression', () { + test('as() wraps expression with alias', () { + final aliased = Field('total').as('sumTotal'); + expect(aliased.toMap(), { + 'name': 'alias', + 'args': { + 'alias': 'sumTotal', + 'expression': { + 'name': 'field', + 'args': {'field': 'total'}, + }, + }, + }); + }); + }); + + group('Expression static boolean helpers', () { + test('Expression.equalStatic produces equal expression', () { + final expr = Expression.equalStatic( + Field('a'), + Constant(1), + ); + expect(expr.toMap()['name'], 'equal'); + }); + + test('Expression.not inverts boolean expression', () { + final inner = Field('active').equal(Constant(true)); + final expr = Expression.not(inner); + expect(expr.toMap(), { + 'name': 'not', + 'args': { + 'expression': { + 'name': 'equal', + 'args': { + 'left': { + 'name': 'field', + 'args': {'field': 'active'}, + }, + 'right': { + 'name': 'constant', + 'args': {'value': true}, + }, + }, + }, + }, + }); + }); + }); + + group('Logic expressions (and, or, xor)', () { + test('Expression.and serializes correctly', () { + final a = Field('a').equal(Constant(1)); + final b = Field('b').equal(Constant(2)); + final expr = Expression.and(a, b); + expect(expr.toMap()['name'], 'and'); + expect(expr.toMap()['args']['expressions'], hasLength(2)); + }); + + test('Expression.or serializes correctly', () { + final a = Field('x').greaterThan(Constant(0)); + final b = Field('y').lessThan(Constant(0)); + final expr = Expression.or(a, b); + expect(expr.toMap()['name'], 'or'); + expect(expr.toMap()['args']['expressions'], hasLength(2)); + }); + + test('Expression.xor serializes correctly', () { + final a = Field('p').equal(Constant(true)); + final b = Field('q').equal(Constant(true)); + final expr = Expression.xor(a, b); + expect(expr.toMap()['name'], 'xor'); + expect(expr.toMap()['args']['expressions'], hasLength(2)); + }); + }); + + group('Conditional expression', () { + test('Expression.conditional serializes correctly', () { + final cond = Field('flag').equal(Constant(true)); + final thenExpr = Constant('yes'); + final elseExpr = Constant('no'); + final expr = Expression.conditional(cond, thenExpr, elseExpr); + expect(expr.toMap(), { + 'name': 'conditional', + 'args': { + 'condition': cond.toMap(), + 'then': thenExpr.toMap(), + 'else': elseExpr.toMap(), + }, + }); + }); + }); + + group('ifAbsent and ifError', () { + test('ifAbsent serializes correctly', () { + final base = Field('optional'); + final fallback = Constant('default'); + final expr = base.ifAbsent(fallback); + expect(expr.toMap(), { + 'name': 'if_absent', + 'args': { + 'expression': base.toMap(), + 'else': fallback.toMap(), + }, + }); + }); + + test('Expression.ifAbsentValueStatic serializes correctly', () { + final expr = Expression.ifAbsentValueStatic(Field('a'), 0); + expect(expr.toMap()['name'], 'if_absent'); + }); + + test('ifError serializes correctly', () { + final base = Field('risky'); + final catchExpr = Constant('error'); + final expr = base.ifError(catchExpr); + expect(expr.toMap(), { + 'name': 'if_error', + 'args': { + 'expression': base.toMap(), + 'catch': catchExpr.toMap(), + }, + }); + }); + }); + + group('Presence and error checks', () { + test('Expression.isAbsentStatic serializes correctly', () { + final expr = Expression.isAbsentStatic(Field('maybe')); + expect(expr.toMap(), { + 'name': 'is_absent', + 'args': {'expression': Field('maybe').toMap()}, + }); + }); + + test('Expression.isErrorStatic serializes correctly', () { + final expr = Expression.isErrorStatic(Field('x')); + expect(expr.toMap(), { + 'name': 'is_error', + 'args': {'expression': Field('x').toMap()}, + }); + }); + + test('Expression.existsField serializes correctly', () { + final expr = Expression.existsField('email'); + expect(expr.toMap()['name'], 'exists'); + }); + }); + + group('String expressions', () { + test('concat serializes correctly', () { + final expr = Field('first').concat([Constant(' '), Field('last')]); + expect(expr.toMap(), { + 'name': 'concat', + 'args': { + 'expressions': [ + Field('first').toMap(), + Constant(' ').toMap(), + Field('last').toMap(), + ], + }, + }); + }); + + test('length serializes correctly', () { + final expr = Field('title').length(); + expect(expr.toMap(), { + 'name': 'length', + 'args': {'expression': Field('title').toMap()}, + }); + }); + + test('toLowerCase serializes correctly', () { + final expr = Field('name').toLowerCase(); + expect(expr.toMap()['name'], 'to_lower_case'); + expect(expr.toMap()['args']['expression']['args']['field'], 'name'); + }); + + test('toUpperCase serializes correctly', () { + final expr = Field('code').toUpperCase(); + expect(expr.toMap()['name'], 'to_upper_case'); + }); + + test('trim serializes correctly', () { + final expr = Field('input').trim(); + expect(expr.toMap(), { + 'name': 'trim', + 'args': {'expression': Field('input').toMap()}, + }); + }); + + test('substring serializes correctly', () { + final expr = Field('text').substring(Constant(0), Constant(5)); + expect(expr.toMap(), { + 'name': 'substring', + 'args': { + 'expression': Field('text').toMap(), + 'start': Constant(0).toMap(), + 'end': Constant(5).toMap(), + }, + }); + }); + + test('stringReplaceAll serializes correctly', () { + final expr = + Field('s').stringReplaceAll(Constant('old'), Constant('new')); + expect(expr.toMap(), { + 'name': 'string_replace_all', + 'args': { + 'expression': Field('s').toMap(), + 'find': Constant('old').toMap(), + 'replacement': Constant('new').toMap(), + }, + }); + }); + + test('split serializes correctly', () { + final expr = Field('csv').split(Constant(',')); + expect(expr.toMap()['name'], 'split'); + expect(expr.toMap()['args']['expression']['args']['field'], 'csv'); + expect(expr.toMap()['args']['delimiter']['args']['value'], ','); + }); + + test('join serializes correctly', () { + final arr = Expression.array([Field('a'), Field('b')]); + final expr = arr.join(Constant('-')); + expect(expr.toMap(), { + 'name': 'join', + 'args': { + 'expression': arr.toMap(), + 'delimiter': Constant('-').toMap(), + }, + }); + }); + }); + + group('Array expressions', () { + test('Expression.array serializes correctly', () { + final expr = Expression.array([Constant(1), Constant(2), Field('x')]); + expect(expr.toMap(), { + 'name': 'array', + 'args': { + 'elements': [ + Constant(1).toMap(), + Constant(2).toMap(), + Field('x').toMap(), + ], + }, + }); + }); + + test('arrayContainsValue serializes correctly', () { + final expr = Field('tags').arrayContainsValue(Constant('flutter')); + expect(expr.toMap(), { + 'name': 'array_contains', + 'args': { + 'array': Field('tags').toMap(), + 'element': Constant('flutter').toMap(), + }, + }); + }); + + test('arrayContainsAny serializes correctly', () { + final expr = + Field('tags').arrayContainsAny([Constant('a'), Constant('b')]); + expect(expr.toMap()['name'], 'array_contains_any'); + expect(expr.toMap()['args']['array']['args']['field'], 'tags'); + expect(expr.toMap()['args']['values'], hasLength(2)); + }); + + test('arrayContainsAll with list serializes correctly', () { + final expr = Field('tags').arrayContainsAll(['a', Constant('b')]); + expect(expr.toMap(), { + 'name': 'array_contains_all', + 'args': { + 'array': Field('tags').toMap(), + 'values': [ + Constant('a').toMap(), + Constant('b').toMap(), + ], + }, + }); + }); + + test('arrayContainsAllFrom with array expression serializes correctly', () { + final elements = Expression.array([Field('tag1'), Constant('tag2')]); + final expr = Field('tags').arrayContainsAllFrom(elements); + expect(expr.toMap(), { + 'name': 'array_contains_all', + 'args': { + 'array': Field('tags').toMap(), + 'array_expression': elements.toMap(), + }, + }); + }); + + test( + 'Expression.arrayContainsAllWithExpression(array, arrayExpression) serializes correctly', + () { + final arrayExpr = + Expression.array([Field('required'), Constant('admin')]); + final expr = Expression.arrayContainsAllWithExpression( + Field('permissions'), + arrayExpr, + ); + expect(expr.toMap(), { + 'name': 'array_contains_all', + 'args': { + 'array': Field('permissions').toMap(), + 'array_expression': arrayExpr.toMap(), + }, + }); + }); + + test('Expression.arrayContainsAllValues(array, list) serializes correctly', + () { + final expr = Expression.arrayContainsAllValues( + Field('tags'), + [Constant('flutter'), Constant('dart')], + ); + expect(expr.toMap()['name'], 'array_contains_all'); + expect(expr.toMap()['args']['values'], hasLength(2)); + }); + + test('Expression.arrayContainsAllField serializes correctly', () { + final required = Expression.array([Field('requiredPermissions')]); + final expr = Expression.arrayContainsAllField('permissions', required); + expect(expr.toMap(), { + 'name': 'array_contains_all', + 'args': { + 'array': Field('permissions').toMap(), + 'array_expression': required.toMap(), + }, + }); + }); + + test('arrayLength serializes correctly', () { + final expr = Field('items').arrayLength(); + expect(expr.toMap(), { + 'name': 'array_length', + 'args': {'expression': Field('items').toMap()}, + }); + }); + + test('arrayConcat serializes correctly', () { + final a = Expression.array([Constant(1)]); + final b = Expression.array([Constant(2)]); + final expr = a.arrayConcat(b); + expect(expr.toMap(), { + 'name': 'array_concat', + 'args': { + 'first': a.toMap(), + 'second': b.toMap(), + }, + }); + }); + + test('arraySum serializes correctly', () { + final expr = Field('values').arraySum(); + expect(expr.toMap()['name'], 'array_sum'); + expect(expr.toMap()['args']['expression']['args']['field'], 'values'); + }); + + test('arrayReverse serializes correctly', () { + final expr = Field('order').arrayReverse(); + expect(expr.toMap()['name'], 'array_reverse'); + expect(expr.toMap()['args']['expression']['args']['field'], 'order'); + }); + + test('arraySlice serializes correctly', () { + final expr = Field('items').arraySlice(1, Field('count')); + expect(expr.toMap(), { + 'name': 'array_slice', + 'args': { + 'expression': Field('items').toMap(), + 'offset': Constant(1).toMap(), + 'length': Field('count').toMap(), + }, + }); + }); + + test('arraySlice without length serializes correctly', () { + final expr = Field('items').arraySlice(1); + expect(expr.toMap(), { + 'name': 'array_slice', + 'args': { + 'expression': Field('items').toMap(), + 'offset': Constant(1).toMap(), + }, + }); + }); + + test('arrayFilter serializes correctly', () { + final expr = Field('scores').arrayFilter( + 'item', + Field('item').greaterThanValue(10), + ); + expect(expr.toMap(), { + 'name': 'array_filter', + 'args': { + 'expression': Field('scores').toMap(), + 'alias': 'item', + 'filter': Field('item').greaterThanValue(10).toMap(), + }, + }); + }); + + test('arrayTransform serializes correctly', () { + final expr = Field('scores').arrayTransform( + 'score', + Field('score').multiplyNumber(10), + ); + expect(expr.toMap(), { + 'name': 'array_transform', + 'args': { + 'expression': Field('scores').toMap(), + 'element_alias': 'score', + 'transform': Field('score').multiplyNumber(10).toMap(), + }, + }); + }); + + test('arrayTransformWithIndex serializes correctly', () { + final expr = Field('scores').arrayTransformWithIndex( + 'score', + 'i', + Field('score').add(Field('i')), + ); + expect(expr.toMap(), { + 'name': 'array_transform_with_index', + 'args': { + 'expression': Field('scores').toMap(), + 'element_alias': 'score', + 'index_alias': 'i', + 'transform': Field('score').add(Field('i')).toMap(), + }, + }); + }); + }); + + group('Numeric expressions', () { + test('add serializes correctly', () { + final expr = Field('a').add(Field('b')); + expect(expr.toMap(), { + 'name': 'add', + 'args': { + 'left': Field('a').toMap(), + 'right': Field('b').toMap(), + }, + }); + }); + + test('subtract serializes correctly', () { + final expr = Field('x').subtract(Constant(1)); + expect(expr.toMap()['name'], 'subtract'); + expect(expr.toMap()['args']['left']['args']['field'], 'x'); + expect(expr.toMap()['args']['right']['args']['value'], 1); + }); + + test('multiply serializes correctly', () { + final expr = Field('qty').multiply(Field('price')); + expect(expr.toMap()['name'], 'multiply'); + }); + + test('divide serializes correctly', () { + final expr = Field('total').divide(Constant(2)); + expect(expr.toMap()['name'], 'divide'); + }); + + test('modulo serializes correctly', () { + final expr = Field('n').modulo(Constant(10)); + expect(expr.toMap()['name'], 'modulo'); + }); + + test('abs serializes correctly', () { + final expr = Field('diff').abs(); + expect(expr.toMap()['name'], 'abs'); + }); + }); + + group('Structure (nullValue, map)', () { + test('Expression.nullValue serializes correctly', () { + final expr = Expression.nullValue(); + expect(expr.toMap(), { + 'name': 'null', + 'args': {'value': null}, + }); + }); + + test('Expression.map serializes correctly', () { + final expr = Expression.map({ + 'k1': Constant(1), + 'k2': Field('v'), + }); + expect(expr.toMap(), { + 'name': 'map', + 'args': { + 'data': { + 'k1': Constant(1).toMap(), + 'k2': Field('v').toMap(), + }, + }, + }); + }); + }); + + group('Timestamp expressions', () { + test('Expression.currentTimestamp serializes correctly', () { + final expr = Expression.currentTimestamp(); + final map = expr.toMap(); + expect(map['name'], 'current_timestamp'); + }); + + test('timestampAddLiteral serializes correctly', () { + final ts = Field('created'); + final expr = Expression.timestampAddLiteral(ts, 'day', 1); + expect(expr.toMap(), { + 'name': 'timestamp_add', + 'args': { + 'timestamp': ts.toMap(), + 'unit': 'day', + 'amount': Constant(1).toMap(), + }, + }); + }); + + test('timestampTruncate serializes correctly', () { + final expr = Expression.timestampTruncate(Field('ts'), 'day'); + expect(expr.toMap(), { + 'name': 'timestamp_truncate', + 'args': { + 'timestamp': Field('ts').toMap(), + 'unit': 'day', + }, + }); + }); + }); + + group('Document and equality helpers', () { + test('Expression.documentIdFromRef serializes correctly', () { + final ref = firestore.collection('users').doc('alice'); + final expr = Expression.documentIdFromRef(ref); + expect(expr.toMap(), { + 'name': 'document_id_from_ref', + 'args': {'doc_ref': 'users/alice'}, + }); + }); + + test('equalAny serializes correctly', () { + final expr = Expression.equalAny(Field('status'), ['a', 'b']); + expect(expr.toMap(), { + 'name': 'equal_any', + 'args': { + 'value': Field('status').toMap(), + 'values': [Constant('a').toMap(), Constant('b').toMap()], + }, + }); + }); + + test('notEqualAny serializes correctly', () { + final expr = Expression.notEqualAny(Field('role'), ['admin']); + expect(expr.toMap(), { + 'name': 'not_equal_any', + 'args': { + 'value': Field('role').toMap(), + 'values': [Constant('admin').toMap()], + }, + }); + }); + }); + + group('asBoolean', () { + test('asBoolean serializes correctly', () { + final expr = Field('flag').asBoolean(); + expect(expr.toMap(), { + 'name': 'as_boolean', + 'args': {'expression': Field('flag').toMap()}, + }); + }); + }); + + group('Map mapSet / mapEntries', () { + test('mapSet serializes map and key_values pairs', () { + final expr = Field('meta').mapSet('k', 1, ['k2', 2]); + expect(expr.toMap(), { + 'name': 'map_set', + 'args': { + 'map': Field('meta').toMap(), + 'key_values': [ + Constant('k').toMap(), + Constant(1).toMap(), + Constant('k2').toMap(), + Constant(2).toMap(), + ], + }, + }); + }); + + test('mapSet throws when key/value list has odd length', () { + expect( + () => Field('m').mapSet('a', 1, ['orphan']), + throwsA(isA()), + ); + }); + + test('mapEntries serializes correctly', () { + final expr = Field('m').mapEntries(); + expect(expr.toMap(), { + 'name': 'map_entries', + 'args': {'expression': Field('m').toMap()}, + }); + }); + }); + + group('Regex and extended string expressions', () { + test('regexFind serializes correctly', () { + final expr = Field('email').regexFind(r'\w+'); + expect(expr.toMap(), { + 'name': 'regex_find', + 'args': { + 'expression': Field('email').toMap(), + 'pattern': Constant(r'\w+').toMap(), + }, + }); + }); + + test('regexFindAll serializes correctly', () { + final expr = Field('text').regexFindAll('[a-z]+'); + expect(expr.toMap()['name'], 'regex_find_all'); + }); + + test('stringReplaceOne serializes correctly', () { + final expr = Field('s').stringReplaceOne(Constant('a'), Constant('b')); + expect(expr.toMap(), { + 'name': 'string_replace_one', + 'args': { + 'expression': Field('s').toMap(), + 'find': Constant('a').toMap(), + 'replacement': Constant('b').toMap(), + }, + }); + }); + + test('stringIndexOf serializes correctly', () { + final expr = Field('s').stringIndexOf('needle'); + expect(expr.toMap()['name'], 'string_index_of'); + expect(expr.toMap()['args']['search'], Constant('needle').toMap()); + }); + + test('stringRepeat serializes correctly', () { + final expr = Field('s').stringRepeat(3); + expect(expr.toMap(), { + 'name': 'string_repeat', + 'args': { + 'expression': Field('s').toMap(), + 'repetitions': Constant(3).toMap(), + }, + }); + }); + + test('ltrim without value serializes correctly', () { + final expr = Field('s').ltrim(); + expect(expr.toMap(), { + 'name': 'ltrim', + 'args': {'expression': Field('s').toMap()}, + }); + }); + + test('ltrim with value serializes correctly', () { + final expr = Field('s').ltrim('"'); + expect(expr.toMap()['args']['value'], Constant('"').toMap()); + }); + + test('rtrim serializes correctly', () { + expect(Field('s').rtrim().toMap()['name'], 'rtrim'); + }); + }); + + group('type / isType / trunc / rand', () { + test('type() serializes correctly', () { + final expr = Field('x').type(); + expect(expr.toMap(), { + 'name': 'type', + 'args': {'expression': Field('x').toMap()}, + }); + }); + + test('isType serializes correctly', () { + final expr = Field('n').isType(Type.int64); + expect(expr.toMap(), { + 'name': 'is_type', + 'args': { + 'expression': Field('n').toMap(), + 'type': 'int64', + }, + }); + }); + + test('Expression.isTypeStatic matches instance isType', () { + expect( + Expression.isTypeStatic(Field('n'), Type.float64).toMap(), + Field('n').isType(Type.float64).toMap(), + ); + }); + + test('trunc without decimals serializes correctly', () { + final expr = Field('pi').trunc(); + expect(expr.toMap(), { + 'name': 'trunc', + 'args': {'expression': Field('pi').toMap()}, + }); + }); + + test('trunc with decimals serializes correctly', () { + final expr = Field('pi').trunc(Constant(2)); + expect(expr.toMap()['args']['decimals'], Constant(2).toMap()); + }); + + test('Expression.rand serializes correctly', () { + expect(Expression.rand().toMap(), { + 'name': 'rand', + 'args': {}, + }); + }); + }); + + group('Array analytics expressions', () { + test('arrayFirst serializes correctly', () { + expect(Field('tags').arrayFirst().toMap()['name'], 'array_first'); + }); + + test('arrayFirstN serializes correctly', () { + final expr = Field('tags').arrayFirstN(2); + expect(expr.toMap(), { + 'name': 'array_first_n', + 'args': { + 'expression': Field('tags').toMap(), + 'n': Constant(2).toMap(), + }, + }); + }); + + test('arrayLast / arrayLastN serialize correctly', () { + expect(Field('tags').arrayLast().toMap()['name'], 'array_last'); + expect(Field('tags').arrayLastN(1).toMap()['name'], 'array_last_n'); + }); + + test('arrayMaximum / arrayMinimum serialize correctly', () { + expect(Field('nums').arrayMaximum().toMap()['name'], 'maximum'); + expect(Field('nums').arrayMinimum().toMap()['name'], 'minimum'); + }); + + test('arrayMaximumN / arrayMinimumN serialize correctly', () { + expect(Field('nums').arrayMaximumN(2).toMap()['name'], 'maximum_n'); + expect(Field('nums').arrayMinimumN(2).toMap()['name'], 'minimum_n'); + }); + + test('arrayIndexOf serializes occurrence first', () { + final expr = Field('tags').arrayIndexOf('x'); + expect(expr.toMap(), { + 'name': 'array_index_of', + 'args': { + 'expression': Field('tags').toMap(), + 'element': Constant('x').toMap(), + 'occurrence': Constant('first').toMap(), + }, + }); + }); + + test('arrayLastIndexOf serializes occurrence last', () { + final expr = Field('tags').arrayLastIndexOf('x'); + expect(expr.toMap()['args']['occurrence'], Constant('last').toMap()); + }); + + test('arrayIndexOfAll serializes correctly', () { + expect( + Field('tags').arrayIndexOfAll('a').toMap()['name'], + 'array_index_of_all', + ); + }); + }); + + group('Expression aggregate helpers', () { + test('first() returns First aggregate', () { + expect(Field('s').first().toMap()['name'], 'first'); + }); + + test('last() returns Last aggregate', () { + expect(Field('s').last().toMap()['name'], 'last'); + }); + + test('arrayAgg returns ArrayAgg', () { + expect(Field('t').arrayAgg().toMap()['name'], 'array_agg'); + }); + + test('arrayAggDistinct returns ArrayAggDistinct', () { + expect( + Field('t').arrayAggDistinct().toMap()['name'], + 'array_agg_distinct', + ); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_ordering_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_ordering_test.dart new file mode 100644 index 000000000000..ccfeac4b922d --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_ordering_test.dart @@ -0,0 +1,62 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import './mock.dart'; + +void main() { + setupCloudFirestoreMocks(); + + setUpAll(() async { + await Firebase.initializeApp(); + }); + + group('OrderDirection', () { + test('asc has expected name', () { + expect(OrderDirection.asc.name, 'asc'); + }); + + test('desc has expected name', () { + expect(OrderDirection.desc.name, 'desc'); + }); + }); + + group('Ordering', () { + test('toMap() serializes ascending order', () { + final ordering = Ordering(Field('name'), OrderDirection.asc); + expect(ordering.toMap(), { + 'expression': { + 'name': 'field', + 'args': {'field': 'name'}, + }, + 'order_direction': 'asc', + }); + }); + + test('toMap() serializes descending order', () { + final ordering = Ordering(Field('score'), OrderDirection.desc); + expect(ordering.toMap(), { + 'expression': { + 'name': 'field', + 'args': {'field': 'score'}, + }, + 'order_direction': 'desc', + }); + }); + + test('toMap() includes expression toMap() result', () { + final ordering = Ordering(Constant(42), OrderDirection.asc); + expect(ordering.toMap(), { + 'expression': { + 'name': 'constant', + 'args': {'value': 42}, + }, + 'order_direction': 'asc', + }); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_sample_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_sample_test.dart new file mode 100644 index 000000000000..6f4a9c13d927 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_sample_test.dart @@ -0,0 +1,56 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import './mock.dart'; + +void main() { + setupCloudFirestoreMocks(); + + late FirebaseFirestore firestore; + + setUpAll(() async { + await Firebase.initializeApp(); + firestore = FirebaseFirestore.instance; + }); + + group('PipelineSample', () { + group('withSize()', () { + test('serializes as type size with value', () { + final pipeline = firestore + .pipeline() + .collection('users') + .sample(PipelineSample.withSize(100)); + final stage = pipeline.stages.last; + expect(stage['stage'], 'sample'); + expect(stage['args']['type'], 'size'); + expect(stage['args']['value'], 100); + }); + + test('accepts zero size', () { + final pipeline = firestore + .pipeline() + .collection('users') + .sample(PipelineSample.withSize(0)); + expect(pipeline.stages.last['args']['value'], 0); + }); + }); + + group('withPercentage()', () { + test('serializes as type percentage with value', () { + final pipeline = firestore + .pipeline() + .collection('users') + .sample(PipelineSample.withPercentage(0.6)); + final stage = pipeline.stages.last; + expect(stage['stage'], 'sample'); + expect(stage['args']['type'], 'percentage'); + expect(stage['args']['value'], 0.6); + }); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_snapshot_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_snapshot_test.dart new file mode 100644 index 000000000000..5d727129ab44 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_snapshot_test.dart @@ -0,0 +1,80 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import './mock.dart'; + +void main() { + setupCloudFirestoreMocks(); + + late FirebaseFirestore firestore; + + setUpAll(() async { + await Firebase.initializeApp(); + firestore = FirebaseFirestore.instance; + }); + + group('PipelineResult', () { + test('stores and returns data via data()', () { + final data = {'name': 'Alice', 'score': 100}; + final result = PipelineResult(data: data); + expect(result.data(), data); + }); + + test('data() returns null when data is null', () { + final result = PipelineResult(); + expect(result.data(), isNull); + }); + + test('stores document reference when provided', () { + final docRef = firestore.collection('users').doc('123'); + final result = PipelineResult(document: docRef); + expect(result.document, docRef); + }); + + test('document is null for aggregate-only result', () { + final result = PipelineResult( + data: {'count': 42}, + ); + expect(result.document, isNull); + }); + + test('stores createTime and updateTime', () { + final create = DateTime(2026); + final update = DateTime(2026, 1, 2); + final result = PipelineResult( + createTime: create, + updateTime: update, + ); + expect(result.createTime, create); + expect(result.updateTime, update); + }); + + test('stores empty data map', () { + final result = PipelineResult(data: {}); + expect(result.data(), isEmpty); + expect(result.data(), isNotNull); + }); + + test('stores all fields together', () { + final docRef = firestore.collection('orders').doc('o1'); + final dateTime1 = DateTime(2026, 2); + final dateTime2 = DateTime(2026, 2, 2); + final data = {'total': 99.99}; + final result = PipelineResult( + document: docRef, + createTime: dateTime1, + updateTime: dateTime2, + data: data, + ); + expect(result.document, docRef); + expect(result.createTime, dateTime1); + expect(result.updateTime, dateTime2); + expect(result.data(), data); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_source_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_source_test.dart new file mode 100644 index 000000000000..a7279c1b1896 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_source_test.dart @@ -0,0 +1,155 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import './mock.dart'; + +void main() { + setupCloudFirestoreMocks(); + + late FirebaseFirestore firestore; + + setUpAll(() async { + await Firebase.initializeApp(); + firestore = FirebaseFirestore.instance; + }); + + group('PipelineSource', () { + test('pipeline() returns a PipelineSource', () { + final source = firestore.pipeline(); + expect(source, isA()); + }); + + group('collection()', () { + test('creates pipeline with collection stage', () { + final pipeline = firestore.pipeline().collection('users'); + expect(pipeline.stages, hasLength(1)); + expect(pipeline.stages.first, { + 'stage': 'collection', + 'args': {'path': 'users'}, + }); + }); + + test('accepts nested collection path', () { + final pipeline = firestore.pipeline().collection('users/abc123/orders'); + expect(pipeline.stages.first['args'], {'path': 'users/abc123/orders'}); + }); + + test('throws on empty path', () { + expect( + () => firestore.pipeline().collection(''), + throwsArgumentError, + ); + }); + + test('throws on path containing double slash', () { + expect( + () => firestore.pipeline().collection('users//posts'), + throwsArgumentError, + ); + }); + }); + + group('collectionReference()', () { + test('creates pipeline from collection reference path', () { + final colRef = firestore.collection('products'); + final pipeline = firestore.pipeline().collectionReference(colRef); + expect(pipeline.stages, hasLength(1)); + expect(pipeline.stages.first, { + 'stage': 'collection', + 'args': {'path': 'products'}, + }); + }); + + test('uses path from nested collection reference', () { + final colRef = + firestore.collection('users').doc('u1').collection('posts'); + final pipeline = firestore.pipeline().collectionReference(colRef); + expect(pipeline.stages.first['args'], { + 'path': 'users/u1/posts', + }); + }); + }); + + group('collectionGroup()', () { + test('creates pipeline with collection_group stage', () { + final pipeline = firestore.pipeline().collectionGroup('posts'); + expect(pipeline.stages, hasLength(1)); + expect(pipeline.stages.first, { + 'stage': 'collection_group', + 'args': {'path': 'posts'}, + }); + }); + + test('throws on empty collection id', () { + expect( + () => firestore.pipeline().collectionGroup(''), + throwsArgumentError, + ); + }); + + test('throws when collection id contains slash', () { + expect( + () => firestore.pipeline().collectionGroup('users/posts'), + throwsArgumentError, + ); + }); + }); + + group('documents()', () { + test('creates pipeline with documents stage', () { + final docRef = firestore.collection('users').doc('123'); + final pipeline = firestore.pipeline().documents([docRef]); + expect(pipeline.stages, hasLength(1)); + expect(pipeline.stages.first['stage'], 'documents'); + expect( + (pipeline.stages.first['args'] as List).first, + {'path': 'users/123'}, + ); + }); + + test('supports multiple document references', () { + final refs = [ + firestore.collection('c').doc('1'), + firestore.collection('c').doc('2'), + ]; + final pipeline = firestore.pipeline().documents(refs); + final args = pipeline.stages.first['args'] as List; + expect(args, hasLength(2)); + }); + + test('throws on empty list', () { + expect( + () => firestore.pipeline().documents([]), + throwsArgumentError, + ); + }); + }); + + group('database()', () { + test('creates pipeline with database stage only', () { + final pipeline = firestore.pipeline().database(); + expect(pipeline.stages, hasLength(1)); + expect(pipeline.stages.first, {'stage': 'database'}); + }); + }); + + group('chaining from source', () { + test('returned pipeline accepts stage methods', () { + final pipeline = firestore + .pipeline() + .collection('users') + .where(Field('active').equal(Constant(true))) + .limit(5); + expect(pipeline.stages, hasLength(3)); + expect(pipeline.stages[0]['stage'], 'collection'); + expect(pipeline.stages[1]['stage'], 'where'); + expect(pipeline.stages[2]['stage'], 'limit'); + }); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/pipeline_stage_test.dart b/packages/cloud_firestore/cloud_firestore/test/pipeline_stage_test.dart new file mode 100644 index 000000000000..41a6fba094db --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/test/pipeline_stage_test.dart @@ -0,0 +1,531 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import './mock.dart'; + +void main() { + setupCloudFirestoreMocks(); + + late FirebaseFirestore firestore; + + setUpAll(() async { + await Firebase.initializeApp(); + firestore = FirebaseFirestore.instance; + }); + + group('Pipeline stages serialization', () { + group('_CollectionPipelineStage', () { + test('serializes collection path correctly', () { + final pipeline = firestore.pipeline().collection('users'); + expect(pipeline.stages.first, { + 'stage': 'collection', + 'args': {'path': 'users'}, + }); + }); + + test('serializes nested collection path', () { + final pipeline = firestore.pipeline().collection('users/123/posts'); + expect(pipeline.stages.first, { + 'stage': 'collection', + 'args': {'path': 'users/123/posts'}, + }); + }); + + test('throws on empty collection path', () { + expect( + () => firestore.pipeline().collection(''), + throwsArgumentError, + ); + }); + + test('throws on collection path with double slashes', () { + expect( + () => firestore.pipeline().collection('users//posts'), + throwsArgumentError, + ); + }); + }); + + group('_CollectionGroupPipelineStage', () { + test('serializes collection group path correctly', () { + final pipeline = firestore.pipeline().collectionGroup('posts'); + expect(pipeline.stages.first, { + 'stage': 'collection_group', + 'args': {'path': 'posts'}, + }); + }); + + test('throws on empty collection group id', () { + expect( + () => firestore.pipeline().collectionGroup(''), + throwsArgumentError, + ); + }); + + test('throws on collection group id containing slash', () { + expect( + () => firestore.pipeline().collectionGroup('users/posts'), + throwsArgumentError, + ); + }); + }); + + group('_DatabasePipelineStage', () { + test('serializes database stage correctly', () { + final pipeline = firestore.pipeline().database(); + expect(pipeline.stages.first, {'stage': 'database'}); + }); + }); + + group('_DocumentsPipelineStage', () { + test('serializes document references', () { + final docRef = firestore.collection('users').doc('123'); + final pipeline = firestore.pipeline().documents([docRef]); + expect(pipeline.stages.first['stage'], 'documents'); + final args = pipeline.stages.first['args'] as List; + expect(args, hasLength(1)); + expect(args.first, {'path': 'users/123'}); + }); + + test('serializes multiple document references', () { + final ref1 = firestore.collection('users').doc('1'); + final ref2 = firestore.collection('users').doc('2'); + final pipeline = firestore.pipeline().documents([ref1, ref2]); + final args = pipeline.stages.first['args'] as List; + expect(args, hasLength(2)); + }); + + test('throws on empty documents list', () { + expect( + () => firestore.pipeline().documents([]), + throwsArgumentError, + ); + }); + }); + + group('_WhereStage', () { + test('serializes where stage with a field filter', () { + final pipeline = firestore + .pipeline() + .collection('users') + .where(Field('age').greaterThan(Constant(18))); + final whereStage = pipeline.stages.last; + expect(whereStage['stage'], 'where'); + expect(whereStage['args'], isA()); + expect(whereStage['args']['expression'], isNotNull); + }); + }); + + group('_SelectStage', () { + test('serializes select stage with fields', () { + final pipeline = firestore + .pipeline() + .collection('users') + .select(Field('name'), Field('age')); + final selectStage = pipeline.stages.last; + expect(selectStage['stage'], 'select'); + expect(selectStage['args']['expressions'], hasLength(2)); + }); + + test('serializes select stage with alias', () { + final pipeline = firestore + .pipeline() + .collection('users') + .select(Field('name').as('userName')); + final selectStage = pipeline.stages.last; + expect(selectStage['stage'], 'select'); + expect(selectStage['args']['expressions'], hasLength(1)); + }); + }); + + group('_AddFieldsStage', () { + test('serializes addFields stage', () { + final pipeline = firestore + .pipeline() + .collection('users') + .addFields(Field('score').as('totalScore')); + final stage = pipeline.stages.last; + expect(stage['stage'], 'add_fields'); + expect(stage['args']['expressions'], hasLength(1)); + }); + + test('serializes addFields with multiple fields', () { + final pipeline = firestore.pipeline().collection('users').addFields( + Field('a').as('x'), + Field('b').as('y'), + Field('c').as('z'), + ); + final stage = pipeline.stages.last; + expect(stage['stage'], 'add_fields'); + expect(stage['args']['expressions'], hasLength(3)); + }); + }); + + group('_LimitStage', () { + test('serializes limit stage', () { + final pipeline = firestore.pipeline().collection('users').limit(10); + final stage = pipeline.stages.last; + expect(stage, { + 'stage': 'limit', + 'args': {'limit': 10}, + }); + }); + }); + + group('_OffsetStage', () { + test('serializes offset stage', () { + final pipeline = firestore.pipeline().collection('users').offset(5); + final stage = pipeline.stages.last; + expect(stage, { + 'stage': 'offset', + 'args': {'offset': 5}, + }); + }); + }); + + group('_SortStage', () { + test('serializes sort stage ascending', () { + final pipeline = firestore + .pipeline() + .collection('users') + .sort(Ordering(Field('name'), OrderDirection.asc)); + final stage = pipeline.stages.last; + expect(stage['stage'], 'sort'); + final orderings = stage['args']['orderings'] as List; + expect(orderings, hasLength(1)); + expect(orderings.first['order_direction'], 'asc'); + }); + + test('serializes sort stage descending', () { + final pipeline = firestore + .pipeline() + .collection('users') + .sort(Ordering(Field('score'), OrderDirection.desc)); + final stage = pipeline.stages.last; + final orderings = stage['args']['orderings'] as List; + expect(orderings.first['order_direction'], 'desc'); + }); + + test('serializes multiple orderings', () { + final pipeline = firestore.pipeline().collection('users').sort( + Ordering(Field('lastName'), OrderDirection.asc), + Ordering(Field('firstName'), OrderDirection.asc), + ); + final stage = pipeline.stages.last; + expect( + stage['args']['orderings'] as List, + hasLength(2), + ); + }); + }); + + group('_AggregateStage', () { + test('serializes aggregate stage', () { + final pipeline = firestore + .pipeline() + .collection('orders') + .aggregate(CountAll().as('totalCount')); + final stage = pipeline.stages.last; + expect(stage['stage'], 'aggregate'); + expect( + stage['args']['aggregate_functions'], + hasLength(1), + ); + }); + + test('serializes multiple aggregate functions', () { + final pipeline = firestore.pipeline().collection('orders').aggregate( + CountAll().as('count'), + Sum(Field('amount')).as('total'), + ); + final stage = pipeline.stages.last; + expect( + stage['args']['aggregate_functions'] as List, + hasLength(2), + ); + }); + }); + + group('_AggregateStageWithOptions', () { + test('serializes aggregate stage with accumulators only', () { + final pipeline = + firestore.pipeline().collection('orders').aggregateWithOptions( + AggregateStageOptions( + accumulators: [CountAll().as('count')], + ), + ); + final stage = pipeline.stages.last; + expect(stage['stage'], 'aggregate_with_options'); + final aggregateStage = + stage['args']['aggregate_stage'] as Map; + expect(aggregateStage['accumulators'], hasLength(1)); + expect(aggregateStage.containsKey('groups'), isFalse); + }); + + test('serializes aggregate stage with accumulators and groups', () { + final pipeline = + firestore.pipeline().collection('orders').aggregateWithOptions( + AggregateStageOptions( + accumulators: [ + Sum(Field('amount')).as('total'), + CountAll().as('count'), + ], + groups: [Field('category')], + ), + ); + final stage = pipeline.stages.last; + expect(stage['stage'], 'aggregate_with_options'); + final aggregateStage = + stage['args']['aggregate_stage'] as Map; + expect(aggregateStage['accumulators'], hasLength(2)); + expect(aggregateStage['groups'], hasLength(1)); + }); + + test('includes options map in args', () { + final pipeline = + firestore.pipeline().collection('orders').aggregateWithOptions( + AggregateStageOptions( + accumulators: [CountAll().as('count')], + ), + ); + final stage = pipeline.stages.last; + expect(stage['args'].containsKey('options'), isTrue); + }); + }); + + group('_DistinctStage', () { + test('serializes distinct stage', () { + final pipeline = + firestore.pipeline().collection('users').distinct(Field('country')); + final stage = pipeline.stages.last; + expect(stage['stage'], 'distinct'); + expect(stage['args']['expressions'], hasLength(1)); + }); + }); + + group('_RemoveFieldsStage', () { + test('serializes removeFields stage', () { + final pipeline = firestore + .pipeline() + .collection('users') + .removeFields('password', 'secretToken'); + final stage = pipeline.stages.last; + expect(stage, { + 'stage': 'remove_fields', + 'args': { + 'field_paths': ['password', 'secretToken'], + }, + }); + }); + }); + + group('_ReplaceWithStage', () { + test('serializes replaceWith stage', () { + final pipeline = firestore + .pipeline() + .collection('users') + .replaceWith(Field('profile')); + final stage = pipeline.stages.last; + expect(stage['stage'], 'replace_with'); + expect(stage['args']['expression'], isNotNull); + }); + }); + + group('_SampleStage', () { + test('serializes sample with size', () { + final pipeline = firestore + .pipeline() + .collection('users') + .sample(PipelineSample.withSize(100)); + final stage = pipeline.stages.last; + expect(stage['stage'], 'sample'); + expect(stage['args']['type'], 'size'); + expect(stage['args']['value'], 100); + }); + + test('serializes sample with percentage', () { + final pipeline = firestore + .pipeline() + .collection('users') + .sample(PipelineSample.withPercentage(0.1)); + final stage = pipeline.stages.last; + expect(stage['stage'], 'sample'); + expect(stage['args']['type'], 'percentage'); + expect(stage['args']['value'], 0.1); + }); + }); + + group('_FindNearestStage', () { + test('serializes findNearest without limit', () { + final pipeline = firestore.pipeline().collection('items').findNearest( + Field('embedding'), + [0.1, 0.2, 0.3], + DistanceMeasure.cosine, + ); + final stage = pipeline.stages.last; + expect(stage['stage'], 'find_nearest'); + expect(stage['args']['vector_field'], 'embedding'); + expect(stage['args']['vector_value'], [0.1, 0.2, 0.3]); + expect(stage['args']['distance_measure'], 'cosine'); + expect(stage['args'].containsKey('limit'), isFalse); + }); + + test('serializes findNearest with limit', () { + final pipeline = firestore.pipeline().collection('items').findNearest( + Field('embedding'), + [0.1, 0.2, 0.3], + DistanceMeasure.euclidean, + limit: 5, + ); + final stage = pipeline.stages.last; + expect(stage['args']['limit'], 5); + expect(stage['args']['distance_measure'], 'euclidean'); + }); + + test('serializes findNearest with dotProduct distance', () { + final pipeline = firestore.pipeline().collection('items').findNearest( + Field('embedding'), + [1.0, 0.0], + DistanceMeasure.dotProduct, + ); + final stage = pipeline.stages.last; + expect(stage['args']['distance_measure'], 'dotProduct'); + }); + }); + + group('_SearchStage', () { + test('serializes search with string query', () { + final pipeline = firestore.pipeline().collection('restaurants').search( + SearchStage.withQuery( + 'breakfast -diner', + limit: 10, + offset: 2, + retrievalDepth: 100, + languageCode: 'en', + ), + ); + final stage = pipeline.stages.last; + expect(stage['stage'], 'search'); + expect(stage['args']['query_type'], 'string'); + expect(stage['args']['query'], 'breakfast -diner'); + expect(stage['args']['limit'], 10); + expect(stage['args']['offset'], 2); + expect(stage['args']['retrieval_depth'], 100); + expect(stage['args']['language_code'], 'en'); + }); + + test('serializes search with query expression', () { + final pipeline = firestore.pipeline().collection('restaurants').search( + SearchStage.withQueryExpression( + Expression.documentMatches('waffles OR pancakes'), + ), + ); + final stage = pipeline.stages.last; + expect(stage['stage'], 'search'); + expect(stage['args']['query_type'], 'expression'); + expect(stage['args']['query'], { + 'name': 'document_matches', + 'args': {'query': 'waffles OR pancakes'}, + }); + }); + + test('serializes search sort and add fields', () { + final pipeline = firestore.pipeline().collection('restaurants').search( + SearchStage.withQuery( + 'breakfast', + sort: [Field('rating').descending()], + addFields: [Field('name')], + ), + ); + final stage = pipeline.stages.last; + expect(stage['args']['sort'], [ + { + 'expression': { + 'name': 'field', + 'args': {'field': 'rating'}, + }, + 'order_direction': 'desc', + }, + ]); + expect(stage['args']['add_fields'], [ + { + 'name': 'field', + 'args': {'field': 'name'}, + }, + ]); + }); + + test('throws if search is not first stage after source', () { + expect( + () => firestore + .pipeline() + .collection('restaurants') + .limit(10) + .search(SearchStage.withQuery('breakfast')), + throwsStateError, + ); + }); + }); + + group('_UnionStage', () { + test('serializes union stage with nested pipeline stages', () { + final innerPipeline = firestore.pipeline().collection('archived_users'); + final pipeline = + firestore.pipeline().collection('users').union(innerPipeline); + final stage = pipeline.stages.last; + expect(stage['stage'], 'union'); + expect(stage['args']['pipeline'], isA()); + expect( + stage['args']['pipeline'] as List, + hasLength(1), + ); + }); + }); + + group('_UnnestStage', () { + test('serializes unnest stage without indexField', () { + final pipeline = firestore + .pipeline() + .collection('users') + .unnest(Field('tags').as('tag')); + final stage = pipeline.stages.last; + expect(stage['stage'], 'unnest'); + expect(stage['args']['expression'], isNotNull); + expect(stage['args'].containsKey('index_field'), isFalse); + }); + + test('serializes unnest stage with indexField', () { + final pipeline = firestore + .pipeline() + .collection('users') + .unnest(Field('tags').as('tag'), 'idx'); + final stage = pipeline.stages.last; + expect(stage['args']['index_field'], 'idx'); + }); + }); + + group('Stage chaining', () { + test('accumulates stages in order', () { + final pipeline = firestore + .pipeline() + .collection('users') + .where(Field('age').greaterThan(Constant(18))) + .select(Field('name')) + .limit(10) + .offset(0); + + expect(pipeline.stages, hasLength(5)); + expect(pipeline.stages[0]['stage'], 'collection'); + expect(pipeline.stages[1]['stage'], 'where'); + expect(pipeline.stages[2]['stage'], 'select'); + expect(pipeline.stages[3]['stage'], 'limit'); + expect(pipeline.stages[4]['stage'], 'offset'); + }); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/test/test_firestore_message_codec.dart b/packages/cloud_firestore/cloud_firestore/test/test_firestore_message_codec.dart index 7a56ecd2b89a..c7ad874ffd2e 100644 --- a/packages/cloud_firestore/cloud_firestore/test/test_firestore_message_codec.dart +++ b/packages/cloud_firestore/cloud_firestore/test/test_firestore_message_codec.dart @@ -77,7 +77,7 @@ class TestFirestoreMessageCodec extends FirestoreMessageCodec { values['path'], FirestorePigeonFirebaseApp( appName: "['DEFAULT']", - settings: PigeonFirebaseSettings(ignoreUndefinedProperties: true), + settings: InternalFirebaseSettings(ignoreUndefinedProperties: true), databaseURL: '', ), ); diff --git a/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp b/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp index 4550d6b3acf0..46d7f52dd372 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp +++ b/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp @@ -15,9 +15,11 @@ #include #include +#include #include #include #include +#include #include #include "cloud_firestore/plugin_version.h" @@ -40,9 +42,160 @@ using flutter::EncodableValue; namespace cloud_firestore_windows { static std::string kLibraryName = "flutter-fire-fst"; + +namespace { + +constexpr wchar_t kTaskRunnerWindowClassName[] = + L"CloudFirestoreWindowsTaskRunnerWindow"; +constexpr UINT kTaskRunnerWindowMessage = WM_APP + 0x4673; + +class PlatformThreadDispatcher { + public: + static PlatformThreadDispatcher& GetInstance() { + static PlatformThreadDispatcher instance; + return instance; + } + + void Initialize() { + std::lock_guard lock(mutex_); + if (window_ != nullptr) { + return; + } + + platform_thread_id_ = GetCurrentThreadId(); + + WNDCLASSW window_class = {}; + window_class.lpfnWndProc = PlatformThreadDispatcher::WindowProc; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.lpszClassName = kTaskRunnerWindowClassName; + + RegisterClassW(&window_class); + window_ = + CreateWindowExW(0, kTaskRunnerWindowClassName, L"", 0, 0, 0, 0, 0, + HWND_MESSAGE, nullptr, window_class.hInstance, this); + } + + void Post(std::function task) { + if (GetCurrentThreadId() == platform_thread_id_) { + task(); + return; + } + + { + std::lock_guard lock(mutex_); + tasks_.push(std::move(task)); + } + PostMessageW(window_, kTaskRunnerWindowMessage, 0, 0); + } + + private: + static LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM wparam, + LPARAM lparam) { + if (message == WM_NCCREATE) { + auto create_struct = reinterpret_cast(lparam); + SetWindowLongPtr( + window, GWLP_USERDATA, + reinterpret_cast(create_struct->lpCreateParams)); + return TRUE; + } + + auto dispatcher = reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); + if (dispatcher != nullptr && message == kTaskRunnerWindowMessage) { + dispatcher->ProcessTasks(); + return 0; + } + + return DefWindowProc(window, message, wparam, lparam); + } + + void ProcessTasks() { + std::queue> tasks; + { + std::lock_guard lock(mutex_); + tasks.swap(tasks_); + } + + while (!tasks.empty()) { + tasks.front()(); + tasks.pop(); + } + } + + PlatformThreadDispatcher() = default; + + HWND window_ = nullptr; + DWORD platform_thread_id_ = 0; + std::mutex mutex_; + std::queue> tasks_; +}; + +struct EventSinkState { + std::mutex mutex; + std::unique_ptr> events; + bool active = true; +}; + +void SendSuccessOnPlatformThread(std::shared_ptr state, + flutter::EncodableValue value) { + if (!state) { + return; + } + + PlatformThreadDispatcher::GetInstance().Post( + [state, value = std::move(value)]() mutable { + std::lock_guard lock(state->mutex); + if (state->active && state->events) { + state->events->Success(value); + } + }); +} + +void SendErrorOnPlatformThread(std::shared_ptr state, + const std::string& code, + const std::string& message, + flutter::EncodableValue details, + bool end_stream = false) { + if (!state) { + return; + } + + PlatformThreadDispatcher::GetInstance().Post( + [state, code, message, details = std::move(details), end_stream]() { + std::lock_guard lock(state->mutex); + if (!state->active || !state->events) { + return; + } + + state->events->Error(code, message, details); + if (end_stream) { + state->events->EndOfStream(); + state->active = false; + } + }); +} + +void EndStreamOnPlatformThread(std::shared_ptr state) { + if (!state) { + return; + } + + PlatformThreadDispatcher::GetInstance().Post([state]() { + std::lock_guard lock(state->mutex); + if (state->active && state->events) { + state->events->EndOfStream(); + state->active = false; + } + }); +} + +} // namespace + // static void CloudFirestorePlugin::RegisterWithRegistrar( flutter::PluginRegistrarWindows* registrar) { + PlatformThreadDispatcher::GetInstance().Initialize(); + auto channel = std::make_unique>( registrar->messenger(), "cloud_firestore", @@ -130,8 +283,7 @@ std::map>> stream_handlers_; -std::map>> +std::map*> cloud_firestore_windows::CloudFirestorePlugin::transaction_handlers_; std::map> cloud_firestore_windows::CloudFirestorePlugin::transactions_; @@ -291,12 +443,12 @@ FlutterError CloudFirestorePlugin::ParseError( firebase::firestore::Source GetSourceFromPigeon(const Source& pigeonSource) { switch (pigeonSource) { - case Source::serverAndCache: + case Source::kServerAndCache: default: return firebase::firestore::Source::kDefault; - case Source::server: + case Source::kServer: return firebase::firestore::Source::kServer; - case Source::cache: + case Source::kCache: return firebase::firestore::Source::kCache; } } @@ -305,13 +457,13 @@ firebase::firestore::DocumentSnapshot::ServerTimestampBehavior GetServerTimestampBehaviorFromPigeon( const ServerTimestampBehavior& pigeonServerTimestampBehavior) { switch (pigeonServerTimestampBehavior) { - case ServerTimestampBehavior::estimate: + case ServerTimestampBehavior::kEstimate: return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: kEstimate; - case ServerTimestampBehavior::previous: + case ServerTimestampBehavior::kPrevious: return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: kPrevious; - case ServerTimestampBehavior::none: + case ServerTimestampBehavior::kNone: default: return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: kNone; @@ -382,27 +534,27 @@ flutter::EncodableMap ConvertToEncodableMap( return convertedMap; } -PigeonSnapshotMetadata ParseSnapshotMetadata( +InternalSnapshotMetadata ParseSnapshotMetadata( const firebase::firestore::SnapshotMetadata& metadata) { - PigeonSnapshotMetadata pigeonSnapshotMetadata = PigeonSnapshotMetadata( + InternalSnapshotMetadata pigeonSnapshotMetadata = InternalSnapshotMetadata( metadata.has_pending_writes(), metadata.is_from_cache()); return pigeonSnapshotMetadata; } -PigeonDocumentSnapshot ParseDocumentSnapshot( +InternalDocumentSnapshot ParseDocumentSnapshot( DocumentSnapshot document, DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { flutter::EncodableMap tempMap = ConvertToEncodableMap(document.GetData(serverTimestampBehavior)); if (tempMap.empty()) { - return PigeonDocumentSnapshot(document.reference().path(), nullptr, - ParseSnapshotMetadata(document.metadata())); + return InternalDocumentSnapshot(document.reference().path(), nullptr, + ParseSnapshotMetadata(document.metadata())); } - PigeonDocumentSnapshot pigeonDocumentSnapshot = - PigeonDocumentSnapshot(document.reference().path(), &tempMap, - ParseSnapshotMetadata(document.metadata())); + InternalDocumentSnapshot pigeonDocumentSnapshot = + InternalDocumentSnapshot(document.reference().path(), &tempMap, + ParseSnapshotMetadata(document.metadata())); return pigeonDocumentSnapshot; } @@ -422,20 +574,20 @@ DocumentChangeType ParseDocumentChangeType( const firebase::firestore::DocumentChange::Type& type) { switch (type) { case firebase::firestore::DocumentChange::Type::kAdded: - return DocumentChangeType::added; + return DocumentChangeType::kAdded; case firebase::firestore::DocumentChange::Type::kRemoved: - return DocumentChangeType::removed; + return DocumentChangeType::kRemoved; case firebase::firestore::DocumentChange::Type::kModified: - return DocumentChangeType::modified; + return DocumentChangeType::kModified; } throw std::invalid_argument("Invalid DocumentChangeType"); } -PigeonDocumentChange ParseDocumentChange( +InternalDocumentChange ParseDocumentChange( const firebase::firestore::DocumentChange& document_change, DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { - PigeonDocumentChange pigeonDocumentChange = PigeonDocumentChange( + InternalDocumentChange pigeonDocumentChange = InternalDocumentChange( ParseDocumentChangeType(document_change.type()), ParseDocumentSnapshot(document_change.document(), serverTimestampBehavior), @@ -454,10 +606,10 @@ flutter::EncodableList ParseDocumentChanges( return pigeonDocumentChanges; } -PigeonQuerySnapshot ParseQuerySnapshot( +InternalQuerySnapshot ParseQuerySnapshot( const firebase::firestore::QuerySnapshot* query_snapshot, DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { - PigeonQuerySnapshot pigeonQuerySnapshot = PigeonQuerySnapshot( + InternalQuerySnapshot pigeonQuerySnapshot = InternalQuerySnapshot( ParseDocumentSnapshots(query_snapshot->documents(), serverTimestampBehavior), ParseDocumentChanges(query_snapshot->DocumentChanges(), @@ -542,10 +694,12 @@ class LoadBundleStreamHandler const flutter::EncodableValue* arguments, std::unique_ptr>&& events) override { - events_ = std::move(events); + events_state_ = std::make_shared(); + events_state_->events = std::move(events); events.reset(); firestore_->LoadBundle( - bundle_, [this](const LoadBundleTaskProgress& progress) { + bundle_, + [events_state = events_state_](const LoadBundleTaskProgress& progress) { flutter::EncodableMap map; map[flutter::EncodableValue("bytesLoaded")] = flutter::EncodableValue(progress.bytes_loaded()); @@ -563,9 +717,9 @@ class LoadBundleStreamHandler details[EncodableValue("message")] = EncodableValue("Error loading the bundle"); - events_->Error("firebase_firestore", "Error loading the bundle", - details); - events_->EndOfStream(); + SendErrorOnPlatformThread(events_state, "firebase_firestore", + "Error loading the bundle", + EncodableValue(details), true); return; } case LoadBundleTaskProgress::State::kInProgress: { @@ -574,7 +728,7 @@ class LoadBundleStreamHandler map[flutter::EncodableValue("taskState")] = flutter::EncodableValue("running"); - events_->Success(map); + SendSuccessOnPlatformThread(events_state, EncodableValue(map)); break; } case LoadBundleTaskProgress::State::kSuccess: { @@ -582,8 +736,8 @@ class LoadBundleStreamHandler map[flutter::EncodableValue("taskState")] = flutter::EncodableValue("success"); - events_->Success(map); - events_->EndOfStream(); + SendSuccessOnPlatformThread(events_state, EncodableValue(map)); + EndStreamOnPlatformThread(events_state); break; } } @@ -593,13 +747,13 @@ class LoadBundleStreamHandler std::unique_ptr> OnCancelInternal(const flutter::EncodableValue* arguments) override { - events_->EndOfStream(); + EndStreamOnPlatformThread(events_state_); return nullptr; } private: Firestore* firestore_; - std::unique_ptr> events_; + std::shared_ptr events_state_; std::string bundle_; }; @@ -625,8 +779,8 @@ using firebase::firestore::QuerySnapshot; void CloudFirestorePlugin::NamedQueryGet( const FirestorePigeonFirebaseApp& app, const std::string& name, - const PigeonGetOptions& options, - std::function reply)> result) { + const InternalGetOptions& options, + std::function reply)> result) { Firestore* firestore = GetFirestoreFromPigeon(app); Future future = firestore->NamedQuery(name.c_str()); @@ -762,7 +916,8 @@ class SnapshotInSyncStreamHandler const flutter::EncodableValue* arguments, std::unique_ptr>&& events) override { - events_ = std::move(events); + events_state_ = std::make_shared(); + events_state_->events = std::move(events); events.reset(); // We do this to bind the event to the main channel auto boundSendEvent = @@ -778,7 +933,7 @@ class SnapshotInSyncStreamHandler std::unique_ptr> OnCancelInternal(const flutter::EncodableValue* arguments) override { listener_.Remove(); - events_->EndOfStream(); + EndStreamOnPlatformThread(events_state_); return nullptr; } @@ -786,12 +941,14 @@ class SnapshotInSyncStreamHandler sendEventFunc_ = func; } - void SendEvent() { events_->Success(flutter::EncodableValue()); } + void SendEvent() { + SendSuccessOnPlatformThread(events_state_, flutter::EncodableValue()); + } private: Firestore* firestore_; ListenerRegistration listener_; - std::unique_ptr> events_; + std::shared_ptr events_state_; std::function sendEventFunc_; }; @@ -825,8 +982,8 @@ class TransactionStreamHandler transactionId_(transactionId) {} void ReceiveTransactionResponse( - PigeonTransactionResult resultType, - std::vector commands) { + InternalTransactionResult resultType, + std::vector commands) { std::lock_guard lock(commands_mutex_); resultType_ = resultType; commands_ = commands; @@ -838,7 +995,8 @@ class TransactionStreamHandler const flutter::EncodableValue* arguments, std::unique_ptr>&& events) override { - events_ = std::move(events); + events_state_ = std::make_shared(); + events_state_->events = std::move(events); events.reset(); TransactionOptions options; options.set_max_attempts(maxAttempts_); @@ -854,22 +1012,27 @@ class TransactionStreamHandler flutter::EncodableMap map; map.emplace("appName", firestore_->app()->name()); - events_->Success(flutter::EncodableValue(map)); + SendSuccessOnPlatformThread(events_state_, + flutter::EncodableValue(map)); std::unique_lock lock(mtx_); if (cv_.wait_for(lock, std::chrono::milliseconds(timeout_)) == std::cv_status::timeout) { - events_->Error("Timeout", "Transaction timed out."); - events_->EndOfStream(); + SendErrorOnPlatformThread(events_state_, "Timeout", + "Transaction timed out.", + flutter::EncodableValue(), true); return Error::kErrorDeadlineExceeded; } std::lock_guard command_lock(commands_mutex_); + if (resultType_ == InternalTransactionResult::kFailure) { + return Error::kErrorAborted; + } if (commands_.empty()) return Error::kErrorOk; - for (PigeonTransactionCommand& command : commands_) { + for (InternalTransactionCommand& command : commands_) { std::string path = command.path(); - PigeonTransactionType type = command.type(); + InternalTransactionType type = command.type(); if (path.empty() /* or some other invalid condition */) { std::cerr << "Path is invalid: " << path << std::endl; continue; // Skip this iteration. @@ -882,7 +1045,7 @@ class TransactionStreamHandler << std::endl; // debug print switch (type) { - case PigeonTransactionType::set: + case InternalTransactionType::kSet: std::cout << "Transaction set" << path << std::endl; // Debug print. @@ -902,14 +1065,14 @@ class TransactionStreamHandler } break; - case PigeonTransactionType::update: + case InternalTransactionType::kUpdate: std::cout << "Transaction update" << path << std::endl; // Debug print. transaction.Update( reference, ConvertToMapFieldPathValue(*command.data())); break; - case PigeonTransactionType::deleteType: + case InternalTransactionType::kDeleteType: std::cout << "Transaction delete" << path << std::endl; // Debug print. @@ -924,12 +1087,14 @@ class TransactionStreamHandler if (completed_future.error() == firebase::firestore::kErrorOk) { result.insert(std::make_pair(flutter::EncodableValue("complete"), flutter::EncodableValue(true))); - events_->Success(result); + SendSuccessOnPlatformThread(events_state_, + flutter::EncodableValue(result)); } else { - events_->Error("transaction_error", - completed_future.error_message()); + SendErrorOnPlatformThread(events_state_, "transaction_error", + completed_future.error_message(), + flutter::EncodableValue()); } - events_->EndOfStream(); + EndStreamOnPlatformThread(events_state_); }); return nullptr; @@ -939,7 +1104,7 @@ class TransactionStreamHandler OnCancelInternal(const flutter::EncodableValue* arguments) override { std::unique_lock lock(mtx_); cv_.notify_one(); - events_->EndOfStream(); + EndStreamOnPlatformThread(events_state_); return nullptr; } @@ -948,12 +1113,12 @@ class TransactionStreamHandler long timeout_; int maxAttempts_; std::string transactionId_; - std::vector commands_; - PigeonTransactionResult resultType_; + std::vector commands_; + InternalTransactionResult resultType_ = InternalTransactionResult::kSuccess; std::mutex mtx_; std::mutex commands_mutex_; std::condition_variable cv_; - std::unique_ptr> events_; + std::shared_ptr events_state_; }; void CloudFirestorePlugin::TransactionCreate( @@ -971,16 +1136,13 @@ void CloudFirestorePlugin::TransactionCreate( auto handler = std::make_unique( firestore, static_cast(timeout), static_cast(max_attempts), transactionId); - - // Temporarily release the ownership. - TransactionStreamHandler* raw_handler = handler.release(); - CloudFirestorePlugin::transaction_handlers_[transactionId] = - std::unique_ptr(raw_handler); + TransactionStreamHandler* raw_handler = handler.get(); + CloudFirestorePlugin::transaction_handlers_[transactionId] = raw_handler; // Register the event channel. std::string channelName = RegisterEventChannelWithUUID( "plugins.flutter.io/firebase_firestore/transaction/", transactionId, - std::unique_ptr(raw_handler)); + std::move(handler)); // Return the result (assumed to be transaction ID in this example). result(transactionId); @@ -990,18 +1152,23 @@ using flutter::CustomEncodableValue; void CloudFirestorePlugin::TransactionStoreResult( const std::string& transaction_id, - const PigeonTransactionResult& result_type, + const InternalTransactionResult& result_type, const flutter::EncodableList* commands, std::function reply)> result) { - if (CloudFirestorePlugin::transaction_handlers_[transaction_id]) { - TransactionStreamHandler& handler = *static_cast( - CloudFirestorePlugin::transaction_handlers_[transaction_id].get()); - std::vector commandVector; - for (const auto& element : *commands) { - const PigeonTransactionCommand& command = - std::any_cast( - std::get(element)); - commandVector.push_back(command); + auto handler_it = + CloudFirestorePlugin::transaction_handlers_.find(transaction_id); + if (handler_it != CloudFirestorePlugin::transaction_handlers_.end() && + handler_it->second) { + TransactionStreamHandler& handler = + *static_cast(handler_it->second); + std::vector commandVector; + if (commands) { + for (const auto& element : *commands) { + const InternalTransactionCommand& command = + std::any_cast( + std::get(element)); + commandVector.push_back(command); + } } handler.ReceiveTransactionResponse(result_type, commandVector); result(std::nullopt); @@ -1015,7 +1182,7 @@ void CloudFirestorePlugin::TransactionStoreResult( void CloudFirestorePlugin::TransactionGet( const FirestorePigeonFirebaseApp& app, const std::string& transaction_id, const std::string& path, - std::function reply)> result) { + std::function reply)> result) { Firestore* firestore = GetFirestoreFromPigeon(app); DocumentReference reference = firestore->Document(path); @@ -1114,7 +1281,7 @@ void CloudFirestorePlugin::DocumentReferenceUpdate( void CloudFirestorePlugin::DocumentReferenceGet( const FirestorePigeonFirebaseApp& app, const DocumentReferenceRequest& request, - std::function reply)> result) { + std::function reply)> result) { Firestore* firestore = GetFirestoreFromPigeon(app); DocumentReference document_reference = firestore->Document(request.path()); @@ -1249,10 +1416,9 @@ firebase::firestore::Filter filterFromJson(const EncodableMap& map) { throw std::runtime_error("Invalid operator"); } -firebase::firestore::Query ParseQuery(firebase::firestore::Firestore* firestore, - const std::string& path, - bool isCollectionGroup, - const PigeonQueryParameters& parameters) { +firebase::firestore::Query ParseQuery( + firebase::firestore::Firestore* firestore, const std::string& path, + bool isCollectionGroup, const InternalQueryParameters& parameters) { try { firebase::firestore::Query query; @@ -1369,9 +1535,9 @@ firebase::firestore::Query ParseQuery(firebase::firestore::Firestore* firestore, void CloudFirestorePlugin::QueryGet( const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, - std::function reply)> result) { + bool is_collection_group, const InternalQueryParameters& parameters, + const InternalGetOptions& options, + std::function reply)> result) { Firestore* firestore = GetFirestoreFromPigeon(app); Query query = ParseQuery(firestore, path, is_collection_group, parameters); @@ -1397,7 +1563,7 @@ void CloudFirestorePlugin::QueryGet( firebase::firestore::AggregateSource GetAggregateSourceFromPigeon( const AggregateSource& source) { switch (source) { - case AggregateSource::server: + case AggregateSource::kServer: default: return firebase::firestore::AggregateSource::kServer; } @@ -1405,7 +1571,7 @@ firebase::firestore::AggregateSource GetAggregateSourceFromPigeon( void CloudFirestorePlugin::AggregateQuery( const FirestorePigeonFirebaseApp& app, const std::string& path, - const PigeonQueryParameters& parameters, const AggregateSource& source, + const InternalQueryParameters& parameters, const AggregateSource& source, const flutter::EncodableList& queries, bool is_collection_group, std::function reply)> result) { Firestore* firestore = GetFirestoreFromPigeon(app); @@ -1420,13 +1586,13 @@ void CloudFirestorePlugin::AggregateQuery( std::get(queryRequest)); switch (queryRequestTyped.type()) { - case AggregateType::count: + case AggregateType::kCount: aggregate_query = query.Count(); break; - case AggregateType::sum: + case AggregateType::kSum: std::cout << "Sum is not supported on C++" << std::endl; break; - case AggregateType::average: + case AggregateType::kAverage: std::cout << "Average is not supported on C++" << std::endl; break; } @@ -1449,20 +1615,20 @@ void CloudFirestorePlugin::AggregateQuery( std::get(queryRequest)); switch (queryRequestTyped.type()) { - case AggregateType::count: { + case AggregateType::kCount: { double doubleValue = static_cast(aggregateQuerySnapshot->count()); - AggregateQueryResponse aggregateResponse(AggregateType::count, + AggregateQueryResponse aggregateResponse(AggregateType::kCount, nullptr, &doubleValue); aggregateResponses.push_back( CustomEncodableValue(aggregateResponse)); break; } - case AggregateType::sum: { + case AggregateType::kSum: { std::cout << "Sum is not supported on C++" << std::endl; break; } - case AggregateType::average: { + case AggregateType::kAverage: { std::cout << "Average is not supported on C++" << std::endl; break; } @@ -1484,11 +1650,11 @@ void CloudFirestorePlugin::WriteBatchCommit( firebase::firestore::WriteBatch batch = firestore->batch(); for (const auto& write : writes) { - const PigeonTransactionCommand& transaction = - std::any_cast( + const InternalTransactionCommand& transaction = + std::any_cast( std::get(write)); - PigeonTransactionType type = transaction.type(); + InternalTransactionType type = transaction.type(); std::string path = transaction.path(); auto data = transaction.data(); @@ -1496,14 +1662,14 @@ void CloudFirestorePlugin::WriteBatchCommit( firestore->Document(path); switch (type) { - case PigeonTransactionType::deleteType: + case InternalTransactionType::kDeleteType: batch.Delete(documentReference); break; - case PigeonTransactionType::update: + case InternalTransactionType::kUpdate: batch.Update(documentReference, ConvertToMapFieldPathValue(*data)); break; - case PigeonTransactionType::set: - const PigeonDocumentOption* options = transaction.option(); + case InternalTransactionType::kSet: + const InternalDocumentOption* options = transaction.option(); if (options->merge()) { batch.Set(documentReference, ConvertToMapFieldValue(*data), @@ -1558,49 +1724,41 @@ class QuerySnapshotStreamHandler ? MetadataChanges::kInclude : MetadataChanges::kExclude; - events_ = std::move(events); + events_state_ = std::make_shared(); + events_state_->events = std::move(events); events.reset(); listener_ = query_->AddSnapshotListener( metadataChanges, - [this, serverTimestampBehavior = serverTimestampBehavior_, + [events_state = events_state_, + serverTimestampBehavior = serverTimestampBehavior_, metadataChanges](const firebase::firestore::QuerySnapshot& snapshot, firebase::firestore::Error error, const std::string& errorMessage) mutable { if (error == firebase::firestore::kErrorOk) { - flutter::EncodableList toListResult(3); - std::vector documents; - std::vector documentChanges; - - for (const auto& documentSnapshot : snapshot.documents()) { - documents.push_back(ParseDocumentSnapshot(documentSnapshot, - serverTimestampBehavior) - .ToEncodableList()); - } - - // Assuming querySnapshot.getDocumentChanges() returns an iterable - // collection - for (const auto& documentChange : - snapshot.DocumentChanges(metadataChanges)) { - documentChanges.push_back( - ParseDocumentChange(documentChange, serverTimestampBehavior) - .ToEncodableList()); - } - - toListResult[0] = documents; - toListResult[1] = documentChanges; - toListResult[2] = - ParseSnapshotMetadata(snapshot.metadata()).ToEncodableList(); - - events_->Success(toListResult); + // Emit the Pigeon object directly so the Pigeon-aware codec on + // the EventChannel serializes it end-to-end. Pigeon 26 no longer + // flattens nested types, so sending a raw list here would cause + // the Dart side to receive a List it can no longer + // decode into InternalQuerySnapshot. + SendSuccessOnPlatformThread( + events_state, + CustomEncodableValue(InternalQuerySnapshot( + ParseDocumentSnapshots(snapshot.documents(), + serverTimestampBehavior), + ParseDocumentChanges( + snapshot.DocumentChanges(metadataChanges), + serverTimestampBehavior), + ParseSnapshotMetadata(snapshot.metadata())))); } else { EncodableMap details; details[EncodableValue("code")] = EncodableValue(CloudFirestorePlugin::GetErrorCode(error)); details[EncodableValue("message")] = EncodableValue(errorMessage); - events_->Error("firebase_firestore", errorMessage, details); - events_->EndOfStream(); + SendErrorOnPlatformThread(events_state, "firebase_firestore", + errorMessage, EncodableValue(details), + true); } }); return nullptr; @@ -1609,14 +1767,14 @@ class QuerySnapshotStreamHandler std::unique_ptr> OnCancelInternal(const flutter::EncodableValue* arguments) override { listener_.Remove(); - events_->EndOfStream(); + EndStreamOnPlatformThread(events_state_); return nullptr; } private: ListenerRegistration listener_; std::unique_ptr query_; - std::unique_ptr> events_; + std::shared_ptr events_state_; bool includeMetadataChanges_; firebase::firestore::DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior_; @@ -1624,11 +1782,11 @@ class QuerySnapshotStreamHandler void CloudFirestorePlugin::QuerySnapshot( const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, bool include_metadata_changes, + bool is_collection_group, const InternalQueryParameters& parameters, + const InternalGetOptions& options, bool include_metadata_changes, const ListenSource& source, std::function reply)> result) { - if (source == ListenSource::cache) { + if (source == ListenSource::kCache) { result(FlutterError("Listening from cache isn't supported on Windows")); return; } @@ -1670,27 +1828,35 @@ class DocumentSnapshotStreamHandler ? MetadataChanges::kInclude : MetadataChanges::kExclude; - events_ = std::move(events); + events_state_ = std::make_shared(); + events_state_->events = std::move(events); events.reset(); listener_ = reference_->AddSnapshotListener( metadataChanges, - [this, serverTimestampBehavior = serverTimestampBehavior_, - metadataChanges](const firebase::firestore::DocumentSnapshot& snapshot, - firebase::firestore::Error error, - const std::string& errorMessage) mutable { + [events_state = events_state_, + serverTimestampBehavior = serverTimestampBehavior_]( + const firebase::firestore::DocumentSnapshot& snapshot, + firebase::firestore::Error error, + const std::string& errorMessage) mutable { if (error == firebase::firestore::kErrorOk) { - events_->Success( - ParseDocumentSnapshot(snapshot, serverTimestampBehavior) - .ToEncodableList()); + // Emit the Pigeon object directly so the Pigeon-aware codec on + // the EventChannel serializes it end-to-end. Pigeon 26 no longer + // flattens nested types, so sending a raw list here would cause + // the Dart side to receive a List it can no longer + // decode into InternalDocumentSnapshot. + SendSuccessOnPlatformThread( + events_state, CustomEncodableValue(ParseDocumentSnapshot( + snapshot, serverTimestampBehavior))); } else { EncodableMap details; details[EncodableValue("code")] = EncodableValue(CloudFirestorePlugin::GetErrorCode(error)); details[EncodableValue("message")] = EncodableValue(errorMessage); - events_->Error("firebase_firestore", errorMessage, details); - events_->EndOfStream(); + SendErrorOnPlatformThread(events_state, "firebase_firestore", + errorMessage, EncodableValue(details), + true); } }); return nullptr; @@ -1699,14 +1865,14 @@ class DocumentSnapshotStreamHandler std::unique_ptr> OnCancelInternal(const flutter::EncodableValue* arguments) override { listener_.Remove(); - events_->EndOfStream(); + EndStreamOnPlatformThread(events_state_); return nullptr; } private: firebase::firestore::ListenerRegistration listener_; std::unique_ptr reference_; - std::unique_ptr> events_; + std::shared_ptr events_state_; bool includeMetadataChanges_; firebase::firestore::DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior_; @@ -1717,7 +1883,7 @@ void CloudFirestorePlugin::DocumentReferenceSnapshot( const DocumentReferenceRequest& parameters, bool include_metadata_changes, const ListenSource& source, std::function reply)> result) { - if (source == ListenSource::cache) { + if (source == ListenSource::kCache) { result(FlutterError("Listening from cache isn't supported on Windows")); return; } @@ -1745,4 +1911,12 @@ void CloudFirestorePlugin::PersistenceCacheIndexManagerRequest( result(FlutterError("Not implemented on Windows")); } +void CloudFirestorePlugin::ExecutePipeline( + const FirestorePigeonFirebaseApp& app, + const ::flutter::EncodableList& stages, + const ::flutter::EncodableMap* options, + std::function reply)> result) { + result(FlutterError("Not implemented on Windows")); +} + } // namespace cloud_firestore_windows diff --git a/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.h b/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.h index ee0dcb07e331..84081f5ffd21 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.h +++ b/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.h @@ -48,8 +48,9 @@ class CloudFirestorePlugin : public flutter::Plugin, std::function reply)> result) override; virtual void NamedQueryGet( const FirestorePigeonFirebaseApp& app, const std::string& name, - const PigeonGetOptions& options, - std::function reply)> result) override; + const InternalGetOptions& options, + std::function reply)> result) + override; virtual void ClearPersistence( const FirestorePigeonFirebaseApp& app, std::function reply)> result) override; @@ -81,13 +82,13 @@ class CloudFirestorePlugin : public flutter::Plugin, std::function reply)> result) override; virtual void TransactionStoreResult( const std::string& transaction_id, - const PigeonTransactionResult& result_type, + const InternalTransactionResult& result_type, const flutter::EncodableList* commands, std::function reply)> result) override; virtual void TransactionGet( const FirestorePigeonFirebaseApp& app, const std::string& transaction_id, const std::string& path, - std::function reply)> result) + std::function reply)> result) override; virtual void DocumentReferenceSet( const FirestorePigeonFirebaseApp& app, @@ -100,7 +101,7 @@ class CloudFirestorePlugin : public flutter::Plugin, virtual void DocumentReferenceGet( const FirestorePigeonFirebaseApp& app, const DocumentReferenceRequest& request, - std::function reply)> result) + std::function reply)> result) override; virtual void DocumentReferenceDelete( const FirestorePigeonFirebaseApp& app, @@ -108,12 +109,13 @@ class CloudFirestorePlugin : public flutter::Plugin, std::function reply)> result) override; virtual void QueryGet( const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, - std::function reply)> result) override; + bool is_collection_group, const InternalQueryParameters& parameters, + const InternalGetOptions& options, + std::function reply)> result) + override; virtual void AggregateQuery( const FirestorePigeonFirebaseApp& app, const std::string& path, - const PigeonQueryParameters& parameters, const AggregateSource& source, + const InternalQueryParameters& parameters, const AggregateSource& source, const flutter::EncodableList& queries, bool is_collection_group, std::function reply)> result) override; @@ -123,8 +125,8 @@ class CloudFirestorePlugin : public flutter::Plugin, std::function reply)> result) override; virtual void QuerySnapshot( const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, bool include_metadata_changes, + bool is_collection_group, const InternalQueryParameters& parameters, + const InternalGetOptions& options, bool include_metadata_changes, const ListenSource& source, std::function reply)> result) override; virtual void DocumentReferenceSnapshot( @@ -136,6 +138,12 @@ class CloudFirestorePlugin : public flutter::Plugin, const FirestorePigeonFirebaseApp& app, const PersistenceCacheIndexManagerRequestEnum& request, std::function reply)> result) override; + virtual void ExecutePipeline( + const FirestorePigeonFirebaseApp& app, + const ::flutter::EncodableList& stages, + const ::flutter::EncodableMap* options, + std::function reply)> result) + override; static flutter::BinaryMessenger* messenger_; static std::map< @@ -144,8 +152,7 @@ class CloudFirestorePlugin : public flutter::Plugin, event_channels_; static std::map>> stream_handlers_; - static std::map>> - transaction_handlers_; + static std::map*> transaction_handlers_; static std::map> transactions_; diff --git a/packages/cloud_firestore/cloud_firestore/windows/firestore_codec.cpp b/packages/cloud_firestore/cloud_firestore/windows/firestore_codec.cpp index 14be5a1776b0..238479a4776b 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/firestore_codec.cpp +++ b/packages/cloud_firestore/cloud_firestore/windows/firestore_codec.cpp @@ -194,7 +194,7 @@ cloud_firestore_windows::FirestoreCodec::ReadValueOfType( } case DATA_TYPE_INCREMENT_INTEGER: { - int incrementValue = std::get(FirestoreCodec::ReadValue(stream)); + int64_t incrementValue = FirestoreCodec::ReadValue(stream).LongValue(); return CustomEncodableValue(FieldValue::Increment(incrementValue)); } diff --git a/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp b/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp index bb9f58d5775d..9c3c6b9dceec 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp +++ b/packages/cloud_firestore/cloud_firestore/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,27 +13,242 @@ #include #include +#include +#include #include #include #include namespace cloud_firestore_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; -// PigeonFirebaseSettings +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} -PigeonFirebaseSettings::PigeonFirebaseSettings(bool ignore_undefined_properties) +} // namespace +// InternalFirebaseSettings + +InternalFirebaseSettings::InternalFirebaseSettings( + bool ignore_undefined_properties) : ignore_undefined_properties_(ignore_undefined_properties) {} -PigeonFirebaseSettings::PigeonFirebaseSettings(const bool* persistence_enabled, - const std::string* host, - const bool* ssl_enabled, - const int64_t* cache_size_bytes, - bool ignore_undefined_properties) +InternalFirebaseSettings::InternalFirebaseSettings( + const bool* persistence_enabled, const std::string* host, + const bool* ssl_enabled, const int64_t* cache_size_bytes, + bool ignore_undefined_properties) : persistence_enabled_(persistence_enabled ? std::optional(*persistence_enabled) : std::nullopt), @@ -45,65 +260,65 @@ PigeonFirebaseSettings::PigeonFirebaseSettings(const bool* persistence_enabled, : std::nullopt), ignore_undefined_properties_(ignore_undefined_properties) {} -const bool* PigeonFirebaseSettings::persistence_enabled() const { +const bool* InternalFirebaseSettings::persistence_enabled() const { return persistence_enabled_ ? &(*persistence_enabled_) : nullptr; } -void PigeonFirebaseSettings::set_persistence_enabled(const bool* value_arg) { +void InternalFirebaseSettings::set_persistence_enabled(const bool* value_arg) { persistence_enabled_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseSettings::set_persistence_enabled(bool value_arg) { +void InternalFirebaseSettings::set_persistence_enabled(bool value_arg) { persistence_enabled_ = value_arg; } -const std::string* PigeonFirebaseSettings::host() const { +const std::string* InternalFirebaseSettings::host() const { return host_ ? &(*host_) : nullptr; } -void PigeonFirebaseSettings::set_host(const std::string_view* value_arg) { +void InternalFirebaseSettings::set_host(const std::string_view* value_arg) { host_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseSettings::set_host(std::string_view value_arg) { +void InternalFirebaseSettings::set_host(std::string_view value_arg) { host_ = value_arg; } -const bool* PigeonFirebaseSettings::ssl_enabled() const { +const bool* InternalFirebaseSettings::ssl_enabled() const { return ssl_enabled_ ? &(*ssl_enabled_) : nullptr; } -void PigeonFirebaseSettings::set_ssl_enabled(const bool* value_arg) { +void InternalFirebaseSettings::set_ssl_enabled(const bool* value_arg) { ssl_enabled_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseSettings::set_ssl_enabled(bool value_arg) { +void InternalFirebaseSettings::set_ssl_enabled(bool value_arg) { ssl_enabled_ = value_arg; } -const int64_t* PigeonFirebaseSettings::cache_size_bytes() const { +const int64_t* InternalFirebaseSettings::cache_size_bytes() const { return cache_size_bytes_ ? &(*cache_size_bytes_) : nullptr; } -void PigeonFirebaseSettings::set_cache_size_bytes(const int64_t* value_arg) { +void InternalFirebaseSettings::set_cache_size_bytes(const int64_t* value_arg) { cache_size_bytes_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseSettings::set_cache_size_bytes(int64_t value_arg) { +void InternalFirebaseSettings::set_cache_size_bytes(int64_t value_arg) { cache_size_bytes_ = value_arg; } -bool PigeonFirebaseSettings::ignore_undefined_properties() const { +bool InternalFirebaseSettings::ignore_undefined_properties() const { return ignore_undefined_properties_; } -void PigeonFirebaseSettings::set_ignore_undefined_properties(bool value_arg) { +void InternalFirebaseSettings::set_ignore_undefined_properties(bool value_arg) { ignore_undefined_properties_ = value_arg; } -EncodableList PigeonFirebaseSettings::ToEncodableList() const { +EncodableList InternalFirebaseSettings::ToEncodableList() const { EncodableList list; list.reserve(5); list.push_back(persistence_enabled_ ? EncodableValue(*persistence_enabled_) @@ -117,9 +332,9 @@ EncodableList PigeonFirebaseSettings::ToEncodableList() const { return list; } -PigeonFirebaseSettings PigeonFirebaseSettings::FromEncodableList( +InternalFirebaseSettings InternalFirebaseSettings::FromEncodableList( const EncodableList& list) { - PigeonFirebaseSettings decoded(std::get(list[4])); + InternalFirebaseSettings decoded(std::get(list[4])); auto& encodable_persistence_enabled = list[0]; if (!encodable_persistence_enabled.IsNull()) { decoded.set_persistence_enabled( @@ -135,20 +350,64 @@ PigeonFirebaseSettings PigeonFirebaseSettings::FromEncodableList( } auto& encodable_cache_size_bytes = list[3]; if (!encodable_cache_size_bytes.IsNull()) { - decoded.set_cache_size_bytes(encodable_cache_size_bytes.LongValue()); + decoded.set_cache_size_bytes(std::get(encodable_cache_size_bytes)); } return decoded; } +bool InternalFirebaseSettings::operator==( + const InternalFirebaseSettings& other) const { + return PigeonInternalDeepEquals(persistence_enabled_, + other.persistence_enabled_) && + PigeonInternalDeepEquals(host_, other.host_) && + PigeonInternalDeepEquals(ssl_enabled_, other.ssl_enabled_) && + PigeonInternalDeepEquals(cache_size_bytes_, other.cache_size_bytes_) && + PigeonInternalDeepEquals(ignore_undefined_properties_, + other.ignore_undefined_properties_); +} + +bool InternalFirebaseSettings::operator!=( + const InternalFirebaseSettings& other) const { + return !(*this == other); +} + +size_t InternalFirebaseSettings::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(persistence_enabled_); + result = result * 31 + PigeonInternalDeepHash(host_); + result = result * 31 + PigeonInternalDeepHash(ssl_enabled_); + result = result * 31 + PigeonInternalDeepHash(cache_size_bytes_); + result = result * 31 + PigeonInternalDeepHash(ignore_undefined_properties_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalFirebaseSettings& v) { + return v.Hash(); +} + // FirestorePigeonFirebaseApp FirestorePigeonFirebaseApp::FirestorePigeonFirebaseApp( - const std::string& app_name, const PigeonFirebaseSettings& settings, + const std::string& app_name, const InternalFirebaseSettings& settings, const std::string& database_u_r_l) : app_name_(app_name), - settings_(settings), + settings_(std::make_unique(settings)), database_u_r_l_(database_u_r_l) {} +FirestorePigeonFirebaseApp::FirestorePigeonFirebaseApp( + const FirestorePigeonFirebaseApp& other) + : app_name_(other.app_name_), + settings_(std::make_unique(*other.settings_)), + database_u_r_l_(other.database_u_r_l_) {} + +FirestorePigeonFirebaseApp& FirestorePigeonFirebaseApp::operator=( + const FirestorePigeonFirebaseApp& other) { + app_name_ = other.app_name_; + settings_ = std::make_unique(*other.settings_); + database_u_r_l_ = other.database_u_r_l_; + return *this; +} + const std::string& FirestorePigeonFirebaseApp::app_name() const { return app_name_; } @@ -157,13 +416,13 @@ void FirestorePigeonFirebaseApp::set_app_name(std::string_view value_arg) { app_name_ = value_arg; } -const PigeonFirebaseSettings& FirestorePigeonFirebaseApp::settings() const { - return settings_; +const InternalFirebaseSettings& FirestorePigeonFirebaseApp::settings() const { + return *settings_; } void FirestorePigeonFirebaseApp::set_settings( - const PigeonFirebaseSettings& value_arg) { - settings_ = value_arg; + const InternalFirebaseSettings& value_arg) { + settings_ = std::make_unique(value_arg); } const std::string& FirestorePigeonFirebaseApp::database_u_r_l() const { @@ -179,41 +438,66 @@ EncodableList FirestorePigeonFirebaseApp::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(EncodableValue(app_name_)); - list.push_back(EncodableValue(settings_.ToEncodableList())); + list.push_back(CustomEncodableValue(*settings_)); list.push_back(EncodableValue(database_u_r_l_)); return list; } FirestorePigeonFirebaseApp FirestorePigeonFirebaseApp::FromEncodableList( const EncodableList& list) { - FirestorePigeonFirebaseApp decoded(std::get(list[0]), - PigeonFirebaseSettings::FromEncodableList( - std::get(list[1])), - std::get(list[2])); + FirestorePigeonFirebaseApp decoded( + std::get(list[0]), + std::any_cast( + std::get(list[1])), + std::get(list[2])); return decoded; } -// PigeonSnapshotMetadata +bool FirestorePigeonFirebaseApp::operator==( + const FirestorePigeonFirebaseApp& other) const { + return PigeonInternalDeepEquals(app_name_, other.app_name_) && + PigeonInternalDeepEquals(settings_, other.settings_) && + PigeonInternalDeepEquals(database_u_r_l_, other.database_u_r_l_); +} -PigeonSnapshotMetadata::PigeonSnapshotMetadata(bool has_pending_writes, - bool is_from_cache) +bool FirestorePigeonFirebaseApp::operator!=( + const FirestorePigeonFirebaseApp& other) const { + return !(*this == other); +} + +size_t FirestorePigeonFirebaseApp::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(app_name_); + result = result * 31 + PigeonInternalDeepHash(settings_); + result = result * 31 + PigeonInternalDeepHash(database_u_r_l_); + return result; +} + +size_t PigeonInternalDeepHash(const FirestorePigeonFirebaseApp& v) { + return v.Hash(); +} + +// InternalSnapshotMetadata + +InternalSnapshotMetadata::InternalSnapshotMetadata(bool has_pending_writes, + bool is_from_cache) : has_pending_writes_(has_pending_writes), is_from_cache_(is_from_cache) {} -bool PigeonSnapshotMetadata::has_pending_writes() const { +bool InternalSnapshotMetadata::has_pending_writes() const { return has_pending_writes_; } -void PigeonSnapshotMetadata::set_has_pending_writes(bool value_arg) { +void InternalSnapshotMetadata::set_has_pending_writes(bool value_arg) { has_pending_writes_ = value_arg; } -bool PigeonSnapshotMetadata::is_from_cache() const { return is_from_cache_; } +bool InternalSnapshotMetadata::is_from_cache() const { return is_from_cache_; } -void PigeonSnapshotMetadata::set_is_from_cache(bool value_arg) { +void InternalSnapshotMetadata::set_is_from_cache(bool value_arg) { is_from_cache_ = value_arg; } -EncodableList PigeonSnapshotMetadata::ToEncodableList() const { +EncodableList InternalSnapshotMetadata::ToEncodableList() const { EncodableList list; list.reserve(2); list.push_back(EncodableValue(has_pending_writes_)); @@ -221,67 +505,107 @@ EncodableList PigeonSnapshotMetadata::ToEncodableList() const { return list; } -PigeonSnapshotMetadata PigeonSnapshotMetadata::FromEncodableList( +InternalSnapshotMetadata InternalSnapshotMetadata::FromEncodableList( const EncodableList& list) { - PigeonSnapshotMetadata decoded(std::get(list[0]), - std::get(list[1])); + InternalSnapshotMetadata decoded(std::get(list[0]), + std::get(list[1])); return decoded; } -// PigeonDocumentSnapshot +bool InternalSnapshotMetadata::operator==( + const InternalSnapshotMetadata& other) const { + return PigeonInternalDeepEquals(has_pending_writes_, + other.has_pending_writes_) && + PigeonInternalDeepEquals(is_from_cache_, other.is_from_cache_); +} + +bool InternalSnapshotMetadata::operator!=( + const InternalSnapshotMetadata& other) const { + return !(*this == other); +} + +size_t InternalSnapshotMetadata::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(has_pending_writes_); + result = result * 31 + PigeonInternalDeepHash(is_from_cache_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalSnapshotMetadata& v) { + return v.Hash(); +} + +// InternalDocumentSnapshot -PigeonDocumentSnapshot::PigeonDocumentSnapshot( - const std::string& path, const PigeonSnapshotMetadata& metadata) - : path_(path), metadata_(metadata) {} +InternalDocumentSnapshot::InternalDocumentSnapshot( + const std::string& path, const InternalSnapshotMetadata& metadata) + : path_(path), + metadata_(std::make_unique(metadata)) {} -PigeonDocumentSnapshot::PigeonDocumentSnapshot( +InternalDocumentSnapshot::InternalDocumentSnapshot( const std::string& path, const EncodableMap* data, - const PigeonSnapshotMetadata& metadata) + const InternalSnapshotMetadata& metadata) : path_(path), data_(data ? std::optional(*data) : std::nullopt), - metadata_(metadata) {} + metadata_(std::make_unique(metadata)) {} -const std::string& PigeonDocumentSnapshot::path() const { return path_; } +InternalDocumentSnapshot::InternalDocumentSnapshot( + const InternalDocumentSnapshot& other) + : path_(other.path_), + data_(other.data_ ? std::optional(*other.data_) + : std::nullopt), + metadata_(std::make_unique(*other.metadata_)) {} -void PigeonDocumentSnapshot::set_path(std::string_view value_arg) { +InternalDocumentSnapshot& InternalDocumentSnapshot::operator=( + const InternalDocumentSnapshot& other) { + path_ = other.path_; + data_ = other.data_; + metadata_ = std::make_unique(*other.metadata_); + return *this; +} + +const std::string& InternalDocumentSnapshot::path() const { return path_; } + +void InternalDocumentSnapshot::set_path(std::string_view value_arg) { path_ = value_arg; } -const EncodableMap* PigeonDocumentSnapshot::data() const { +const EncodableMap* InternalDocumentSnapshot::data() const { return data_ ? &(*data_) : nullptr; } -void PigeonDocumentSnapshot::set_data(const EncodableMap* value_arg) { +void InternalDocumentSnapshot::set_data(const EncodableMap* value_arg) { data_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonDocumentSnapshot::set_data(const EncodableMap& value_arg) { +void InternalDocumentSnapshot::set_data(const EncodableMap& value_arg) { data_ = value_arg; } -const PigeonSnapshotMetadata& PigeonDocumentSnapshot::metadata() const { - return metadata_; +const InternalSnapshotMetadata& InternalDocumentSnapshot::metadata() const { + return *metadata_; } -void PigeonDocumentSnapshot::set_metadata( - const PigeonSnapshotMetadata& value_arg) { - metadata_ = value_arg; +void InternalDocumentSnapshot::set_metadata( + const InternalSnapshotMetadata& value_arg) { + metadata_ = std::make_unique(value_arg); } -EncodableList PigeonDocumentSnapshot::ToEncodableList() const { +EncodableList InternalDocumentSnapshot::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(EncodableValue(path_)); list.push_back(data_ ? EncodableValue(*data_) : EncodableValue()); - list.push_back(EncodableValue(metadata_.ToEncodableList())); + list.push_back(CustomEncodableValue(*metadata_)); return list; } -PigeonDocumentSnapshot PigeonDocumentSnapshot::FromEncodableList( +InternalDocumentSnapshot InternalDocumentSnapshot::FromEncodableList( const EncodableList& list) { - PigeonDocumentSnapshot decoded(std::get(list[0]), - PigeonSnapshotMetadata::FromEncodableList( - std::get(list[2]))); + InternalDocumentSnapshot decoded( + std::get(list[0]), + std::any_cast( + std::get(list[2]))); auto& encodable_data = list[1]; if (!encodable_data.IsNull()) { decoded.set_data(std::get(encodable_data)); @@ -289,187 +613,499 @@ PigeonDocumentSnapshot PigeonDocumentSnapshot::FromEncodableList( return decoded; } -// PigeonDocumentChange +bool InternalDocumentSnapshot::operator==( + const InternalDocumentSnapshot& other) const { + return PigeonInternalDeepEquals(path_, other.path_) && + PigeonInternalDeepEquals(data_, other.data_) && + PigeonInternalDeepEquals(metadata_, other.metadata_); +} + +bool InternalDocumentSnapshot::operator!=( + const InternalDocumentSnapshot& other) const { + return !(*this == other); +} + +size_t InternalDocumentSnapshot::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(path_); + result = result * 31 + PigeonInternalDeepHash(data_); + result = result * 31 + PigeonInternalDeepHash(metadata_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalDocumentSnapshot& v) { + return v.Hash(); +} + +// InternalDocumentChange -PigeonDocumentChange::PigeonDocumentChange( - const DocumentChangeType& type, const PigeonDocumentSnapshot& document, +InternalDocumentChange::InternalDocumentChange( + const DocumentChangeType& type, const InternalDocumentSnapshot& document, int64_t old_index, int64_t new_index) : type_(type), - document_(document), + document_(std::make_unique(document)), old_index_(old_index), new_index_(new_index) {} -const DocumentChangeType& PigeonDocumentChange::type() const { return type_; } +InternalDocumentChange::InternalDocumentChange( + const InternalDocumentChange& other) + : type_(other.type_), + document_(std::make_unique(*other.document_)), + old_index_(other.old_index_), + new_index_(other.new_index_) {} -void PigeonDocumentChange::set_type(const DocumentChangeType& value_arg) { +InternalDocumentChange& InternalDocumentChange::operator=( + const InternalDocumentChange& other) { + type_ = other.type_; + document_ = std::make_unique(*other.document_); + old_index_ = other.old_index_; + new_index_ = other.new_index_; + return *this; +} + +const DocumentChangeType& InternalDocumentChange::type() const { return type_; } + +void InternalDocumentChange::set_type(const DocumentChangeType& value_arg) { type_ = value_arg; } -const PigeonDocumentSnapshot& PigeonDocumentChange::document() const { - return document_; +const InternalDocumentSnapshot& InternalDocumentChange::document() const { + return *document_; } -void PigeonDocumentChange::set_document( - const PigeonDocumentSnapshot& value_arg) { - document_ = value_arg; +void InternalDocumentChange::set_document( + const InternalDocumentSnapshot& value_arg) { + document_ = std::make_unique(value_arg); } -int64_t PigeonDocumentChange::old_index() const { return old_index_; } +int64_t InternalDocumentChange::old_index() const { return old_index_; } -void PigeonDocumentChange::set_old_index(int64_t value_arg) { +void InternalDocumentChange::set_old_index(int64_t value_arg) { old_index_ = value_arg; } -int64_t PigeonDocumentChange::new_index() const { return new_index_; } +int64_t InternalDocumentChange::new_index() const { return new_index_; } -void PigeonDocumentChange::set_new_index(int64_t value_arg) { +void InternalDocumentChange::set_new_index(int64_t value_arg) { new_index_ = value_arg; } -EncodableList PigeonDocumentChange::ToEncodableList() const { +EncodableList InternalDocumentChange::ToEncodableList() const { EncodableList list; list.reserve(4); - list.push_back(EncodableValue((int)type_)); - list.push_back(EncodableValue(document_.ToEncodableList())); + list.push_back(CustomEncodableValue(type_)); + list.push_back(CustomEncodableValue(*document_)); list.push_back(EncodableValue(old_index_)); list.push_back(EncodableValue(new_index_)); return list; } -PigeonDocumentChange PigeonDocumentChange::FromEncodableList( +InternalDocumentChange InternalDocumentChange::FromEncodableList( const EncodableList& list) { - PigeonDocumentChange decoded((DocumentChangeType)(std::get(list[0])), - PigeonDocumentSnapshot::FromEncodableList( - std::get(list[1])), - list[2].LongValue(), list[3].LongValue()); + InternalDocumentChange decoded(std::any_cast( + std::get(list[0])), + std::any_cast( + std::get(list[1])), + std::get(list[2]), + std::get(list[3])); return decoded; } -// PigeonQuerySnapshot +bool InternalDocumentChange::operator==( + const InternalDocumentChange& other) const { + return PigeonInternalDeepEquals(type_, other.type_) && + PigeonInternalDeepEquals(document_, other.document_) && + PigeonInternalDeepEquals(old_index_, other.old_index_) && + PigeonInternalDeepEquals(new_index_, other.new_index_); +} -PigeonQuerySnapshot::PigeonQuerySnapshot(const EncodableList& documents, - const EncodableList& document_changes, - const PigeonSnapshotMetadata& metadata) +bool InternalDocumentChange::operator!=( + const InternalDocumentChange& other) const { + return !(*this == other); +} + +size_t InternalDocumentChange::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(type_); + result = result * 31 + PigeonInternalDeepHash(document_); + result = result * 31 + PigeonInternalDeepHash(old_index_); + result = result * 31 + PigeonInternalDeepHash(new_index_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalDocumentChange& v) { + return v.Hash(); +} + +// InternalQuerySnapshot + +InternalQuerySnapshot::InternalQuerySnapshot( + const EncodableList& documents, const EncodableList& document_changes, + const InternalSnapshotMetadata& metadata) : documents_(documents), document_changes_(document_changes), - metadata_(metadata) {} + metadata_(std::make_unique(metadata)) {} + +InternalQuerySnapshot::InternalQuerySnapshot(const InternalQuerySnapshot& other) + : documents_(other.documents_), + document_changes_(other.document_changes_), + metadata_(std::make_unique(*other.metadata_)) {} + +InternalQuerySnapshot& InternalQuerySnapshot::operator=( + const InternalQuerySnapshot& other) { + documents_ = other.documents_; + document_changes_ = other.document_changes_; + metadata_ = std::make_unique(*other.metadata_); + return *this; +} -const EncodableList& PigeonQuerySnapshot::documents() const { +const EncodableList& InternalQuerySnapshot::documents() const { return documents_; } -void PigeonQuerySnapshot::set_documents(const EncodableList& value_arg) { +void InternalQuerySnapshot::set_documents(const EncodableList& value_arg) { documents_ = value_arg; } -const EncodableList& PigeonQuerySnapshot::document_changes() const { +const EncodableList& InternalQuerySnapshot::document_changes() const { return document_changes_; } -void PigeonQuerySnapshot::set_document_changes(const EncodableList& value_arg) { +void InternalQuerySnapshot::set_document_changes( + const EncodableList& value_arg) { document_changes_ = value_arg; } -const PigeonSnapshotMetadata& PigeonQuerySnapshot::metadata() const { - return metadata_; +const InternalSnapshotMetadata& InternalQuerySnapshot::metadata() const { + return *metadata_; } -void PigeonQuerySnapshot::set_metadata( - const PigeonSnapshotMetadata& value_arg) { - metadata_ = value_arg; +void InternalQuerySnapshot::set_metadata( + const InternalSnapshotMetadata& value_arg) { + metadata_ = std::make_unique(value_arg); } -EncodableList PigeonQuerySnapshot::ToEncodableList() const { +EncodableList InternalQuerySnapshot::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(EncodableValue(documents_)); list.push_back(EncodableValue(document_changes_)); - list.push_back(EncodableValue(metadata_.ToEncodableList())); + list.push_back(CustomEncodableValue(*metadata_)); return list; } -PigeonQuerySnapshot PigeonQuerySnapshot::FromEncodableList( +InternalQuerySnapshot InternalQuerySnapshot::FromEncodableList( const EncodableList& list) { - PigeonQuerySnapshot decoded(std::get(list[0]), - std::get(list[1]), - PigeonSnapshotMetadata::FromEncodableList( - std::get(list[2]))); + InternalQuerySnapshot decoded(std::get(list[0]), + std::get(list[1]), + std::any_cast( + std::get(list[2]))); return decoded; } -// PigeonGetOptions +bool InternalQuerySnapshot::operator==( + const InternalQuerySnapshot& other) const { + return PigeonInternalDeepEquals(documents_, other.documents_) && + PigeonInternalDeepEquals(document_changes_, other.document_changes_) && + PigeonInternalDeepEquals(metadata_, other.metadata_); +} + +bool InternalQuerySnapshot::operator!=( + const InternalQuerySnapshot& other) const { + return !(*this == other); +} + +size_t InternalQuerySnapshot::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(documents_); + result = result * 31 + PigeonInternalDeepHash(document_changes_); + result = result * 31 + PigeonInternalDeepHash(metadata_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalQuerySnapshot& v) { + return v.Hash(); +} + +// InternalPipelineResult + +InternalPipelineResult::InternalPipelineResult() {} + +InternalPipelineResult::InternalPipelineResult(const std::string* document_path, + const int64_t* create_time, + const int64_t* update_time, + const EncodableMap* data) + : document_path_(document_path ? std::optional(*document_path) + : std::nullopt), + create_time_(create_time ? std::optional(*create_time) + : std::nullopt), + update_time_(update_time ? std::optional(*update_time) + : std::nullopt), + data_(data ? std::optional(*data) : std::nullopt) {} + +const std::string* InternalPipelineResult::document_path() const { + return document_path_ ? &(*document_path_) : nullptr; +} + +void InternalPipelineResult::set_document_path( + const std::string_view* value_arg) { + document_path_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void InternalPipelineResult::set_document_path(std::string_view value_arg) { + document_path_ = value_arg; +} + +const int64_t* InternalPipelineResult::create_time() const { + return create_time_ ? &(*create_time_) : nullptr; +} + +void InternalPipelineResult::set_create_time(const int64_t* value_arg) { + create_time_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void InternalPipelineResult::set_create_time(int64_t value_arg) { + create_time_ = value_arg; +} + +const int64_t* InternalPipelineResult::update_time() const { + return update_time_ ? &(*update_time_) : nullptr; +} -PigeonGetOptions::PigeonGetOptions( +void InternalPipelineResult::set_update_time(const int64_t* value_arg) { + update_time_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void InternalPipelineResult::set_update_time(int64_t value_arg) { + update_time_ = value_arg; +} + +const EncodableMap* InternalPipelineResult::data() const { + return data_ ? &(*data_) : nullptr; +} + +void InternalPipelineResult::set_data(const EncodableMap* value_arg) { + data_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void InternalPipelineResult::set_data(const EncodableMap& value_arg) { + data_ = value_arg; +} + +EncodableList InternalPipelineResult::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(document_path_ ? EncodableValue(*document_path_) + : EncodableValue()); + list.push_back(create_time_ ? EncodableValue(*create_time_) + : EncodableValue()); + list.push_back(update_time_ ? EncodableValue(*update_time_) + : EncodableValue()); + list.push_back(data_ ? EncodableValue(*data_) : EncodableValue()); + return list; +} + +InternalPipelineResult InternalPipelineResult::FromEncodableList( + const EncodableList& list) { + InternalPipelineResult decoded; + auto& encodable_document_path = list[0]; + if (!encodable_document_path.IsNull()) { + decoded.set_document_path(std::get(encodable_document_path)); + } + auto& encodable_create_time = list[1]; + if (!encodable_create_time.IsNull()) { + decoded.set_create_time(std::get(encodable_create_time)); + } + auto& encodable_update_time = list[2]; + if (!encodable_update_time.IsNull()) { + decoded.set_update_time(std::get(encodable_update_time)); + } + auto& encodable_data = list[3]; + if (!encodable_data.IsNull()) { + decoded.set_data(std::get(encodable_data)); + } + return decoded; +} + +bool InternalPipelineResult::operator==( + const InternalPipelineResult& other) const { + return PigeonInternalDeepEquals(document_path_, other.document_path_) && + PigeonInternalDeepEquals(create_time_, other.create_time_) && + PigeonInternalDeepEquals(update_time_, other.update_time_) && + PigeonInternalDeepEquals(data_, other.data_); +} + +bool InternalPipelineResult::operator!=( + const InternalPipelineResult& other) const { + return !(*this == other); +} + +size_t InternalPipelineResult::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(document_path_); + result = result * 31 + PigeonInternalDeepHash(create_time_); + result = result * 31 + PigeonInternalDeepHash(update_time_); + result = result * 31 + PigeonInternalDeepHash(data_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalPipelineResult& v) { + return v.Hash(); +} + +// InternalPipelineSnapshot + +InternalPipelineSnapshot::InternalPipelineSnapshot(const EncodableList& results, + int64_t execution_time) + : results_(results), execution_time_(execution_time) {} + +const EncodableList& InternalPipelineSnapshot::results() const { + return results_; +} + +void InternalPipelineSnapshot::set_results(const EncodableList& value_arg) { + results_ = value_arg; +} + +int64_t InternalPipelineSnapshot::execution_time() const { + return execution_time_; +} + +void InternalPipelineSnapshot::set_execution_time(int64_t value_arg) { + execution_time_ = value_arg; +} + +EncodableList InternalPipelineSnapshot::ToEncodableList() const { + EncodableList list; + list.reserve(2); + list.push_back(EncodableValue(results_)); + list.push_back(EncodableValue(execution_time_)); + return list; +} + +InternalPipelineSnapshot InternalPipelineSnapshot::FromEncodableList( + const EncodableList& list) { + InternalPipelineSnapshot decoded(std::get(list[0]), + std::get(list[1])); + return decoded; +} + +bool InternalPipelineSnapshot::operator==( + const InternalPipelineSnapshot& other) const { + return PigeonInternalDeepEquals(results_, other.results_) && + PigeonInternalDeepEquals(execution_time_, other.execution_time_); +} + +bool InternalPipelineSnapshot::operator!=( + const InternalPipelineSnapshot& other) const { + return !(*this == other); +} + +size_t InternalPipelineSnapshot::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(results_); + result = result * 31 + PigeonInternalDeepHash(execution_time_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalPipelineSnapshot& v) { + return v.Hash(); +} + +// InternalGetOptions + +InternalGetOptions::InternalGetOptions( const Source& source, const ServerTimestampBehavior& server_timestamp_behavior) : source_(source), server_timestamp_behavior_(server_timestamp_behavior) {} -const Source& PigeonGetOptions::source() const { return source_; } +const Source& InternalGetOptions::source() const { return source_; } -void PigeonGetOptions::set_source(const Source& value_arg) { +void InternalGetOptions::set_source(const Source& value_arg) { source_ = value_arg; } -const ServerTimestampBehavior& PigeonGetOptions::server_timestamp_behavior() +const ServerTimestampBehavior& InternalGetOptions::server_timestamp_behavior() const { return server_timestamp_behavior_; } -void PigeonGetOptions::set_server_timestamp_behavior( +void InternalGetOptions::set_server_timestamp_behavior( const ServerTimestampBehavior& value_arg) { server_timestamp_behavior_ = value_arg; } -EncodableList PigeonGetOptions::ToEncodableList() const { +EncodableList InternalGetOptions::ToEncodableList() const { EncodableList list; list.reserve(2); - list.push_back(EncodableValue((int)source_)); - list.push_back(EncodableValue((int)server_timestamp_behavior_)); + list.push_back(CustomEncodableValue(source_)); + list.push_back(CustomEncodableValue(server_timestamp_behavior_)); return list; } -PigeonGetOptions PigeonGetOptions::FromEncodableList( +InternalGetOptions InternalGetOptions::FromEncodableList( const EncodableList& list) { - PigeonGetOptions decoded( - (Source)(std::get(list[0])), - (ServerTimestampBehavior)(std::get(list[1]))); + InternalGetOptions decoded( + std::any_cast(std::get(list[0])), + std::any_cast( + std::get(list[1]))); return decoded; } -// PigeonDocumentOption +bool InternalGetOptions::operator==(const InternalGetOptions& other) const { + return PigeonInternalDeepEquals(source_, other.source_) && + PigeonInternalDeepEquals(server_timestamp_behavior_, + other.server_timestamp_behavior_); +} + +bool InternalGetOptions::operator!=(const InternalGetOptions& other) const { + return !(*this == other); +} + +size_t InternalGetOptions::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(source_); + result = result * 31 + PigeonInternalDeepHash(server_timestamp_behavior_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalGetOptions& v) { return v.Hash(); } + +// InternalDocumentOption -PigeonDocumentOption::PigeonDocumentOption() {} +InternalDocumentOption::InternalDocumentOption() {} -PigeonDocumentOption::PigeonDocumentOption(const bool* merge, - const EncodableList* merge_fields) +InternalDocumentOption::InternalDocumentOption( + const bool* merge, const EncodableList* merge_fields) : merge_(merge ? std::optional(*merge) : std::nullopt), merge_fields_(merge_fields ? std::optional(*merge_fields) : std::nullopt) {} -const bool* PigeonDocumentOption::merge() const { +const bool* InternalDocumentOption::merge() const { return merge_ ? &(*merge_) : nullptr; } -void PigeonDocumentOption::set_merge(const bool* value_arg) { +void InternalDocumentOption::set_merge(const bool* value_arg) { merge_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonDocumentOption::set_merge(bool value_arg) { merge_ = value_arg; } +void InternalDocumentOption::set_merge(bool value_arg) { merge_ = value_arg; } -const EncodableList* PigeonDocumentOption::merge_fields() const { +const EncodableList* InternalDocumentOption::merge_fields() const { return merge_fields_ ? &(*merge_fields_) : nullptr; } -void PigeonDocumentOption::set_merge_fields(const EncodableList* value_arg) { +void InternalDocumentOption::set_merge_fields(const EncodableList* value_arg) { merge_fields_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonDocumentOption::set_merge_fields(const EncodableList& value_arg) { +void InternalDocumentOption::set_merge_fields(const EncodableList& value_arg) { merge_fields_ = value_arg; } -EncodableList PigeonDocumentOption::ToEncodableList() const { +EncodableList InternalDocumentOption::ToEncodableList() const { EncodableList list; list.reserve(2); list.push_back(merge_ ? EncodableValue(*merge_) : EncodableValue()); @@ -478,9 +1114,9 @@ EncodableList PigeonDocumentOption::ToEncodableList() const { return list; } -PigeonDocumentOption PigeonDocumentOption::FromEncodableList( +InternalDocumentOption InternalDocumentOption::FromEncodableList( const EncodableList& list) { - PigeonDocumentOption decoded; + InternalDocumentOption decoded; auto& encodable_merge = list[0]; if (!encodable_merge.IsNull()) { decoded.set_merge(std::get(encodable_merge)); @@ -492,78 +1128,121 @@ PigeonDocumentOption PigeonDocumentOption::FromEncodableList( return decoded; } -// PigeonTransactionCommand +bool InternalDocumentOption::operator==( + const InternalDocumentOption& other) const { + return PigeonInternalDeepEquals(merge_, other.merge_) && + PigeonInternalDeepEquals(merge_fields_, other.merge_fields_); +} + +bool InternalDocumentOption::operator!=( + const InternalDocumentOption& other) const { + return !(*this == other); +} -PigeonTransactionCommand::PigeonTransactionCommand( - const PigeonTransactionType& type, const std::string& path) +size_t InternalDocumentOption::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(merge_); + result = result * 31 + PigeonInternalDeepHash(merge_fields_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalDocumentOption& v) { + return v.Hash(); +} + +// InternalTransactionCommand + +InternalTransactionCommand::InternalTransactionCommand( + const InternalTransactionType& type, const std::string& path) : type_(type), path_(path) {} -PigeonTransactionCommand::PigeonTransactionCommand( - const PigeonTransactionType& type, const std::string& path, - const EncodableMap* data, const PigeonDocumentOption* option) +InternalTransactionCommand::InternalTransactionCommand( + const InternalTransactionType& type, const std::string& path, + const EncodableMap* data, const InternalDocumentOption* option) : type_(type), path_(path), data_(data ? std::optional(*data) : std::nullopt), - option_(option ? std::optional(*option) - : std::nullopt) {} - -const PigeonTransactionType& PigeonTransactionCommand::type() const { + option_(option ? std::make_unique(*option) + : nullptr) {} + +InternalTransactionCommand::InternalTransactionCommand( + const InternalTransactionCommand& other) + : type_(other.type_), + path_(other.path_), + data_(other.data_ ? std::optional(*other.data_) + : std::nullopt), + option_(other.option_ + ? std::make_unique(*other.option_) + : nullptr) {} + +InternalTransactionCommand& InternalTransactionCommand::operator=( + const InternalTransactionCommand& other) { + type_ = other.type_; + path_ = other.path_; + data_ = other.data_; + option_ = other.option_ + ? std::make_unique(*other.option_) + : nullptr; + return *this; +} + +const InternalTransactionType& InternalTransactionCommand::type() const { return type_; } -void PigeonTransactionCommand::set_type( - const PigeonTransactionType& value_arg) { +void InternalTransactionCommand::set_type( + const InternalTransactionType& value_arg) { type_ = value_arg; } -const std::string& PigeonTransactionCommand::path() const { return path_; } +const std::string& InternalTransactionCommand::path() const { return path_; } -void PigeonTransactionCommand::set_path(std::string_view value_arg) { +void InternalTransactionCommand::set_path(std::string_view value_arg) { path_ = value_arg; } -const EncodableMap* PigeonTransactionCommand::data() const { +const EncodableMap* InternalTransactionCommand::data() const { return data_ ? &(*data_) : nullptr; } -void PigeonTransactionCommand::set_data(const EncodableMap* value_arg) { +void InternalTransactionCommand::set_data(const EncodableMap* value_arg) { data_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonTransactionCommand::set_data(const EncodableMap& value_arg) { +void InternalTransactionCommand::set_data(const EncodableMap& value_arg) { data_ = value_arg; } -const PigeonDocumentOption* PigeonTransactionCommand::option() const { - return option_ ? &(*option_) : nullptr; +const InternalDocumentOption* InternalTransactionCommand::option() const { + return option_.get(); } -void PigeonTransactionCommand::set_option( - const PigeonDocumentOption* value_arg) { - option_ = value_arg ? std::optional(*value_arg) - : std::nullopt; +void InternalTransactionCommand::set_option( + const InternalDocumentOption* value_arg) { + option_ = value_arg ? std::make_unique(*value_arg) + : nullptr; } -void PigeonTransactionCommand::set_option( - const PigeonDocumentOption& value_arg) { - option_ = value_arg; +void InternalTransactionCommand::set_option( + const InternalDocumentOption& value_arg) { + option_ = std::make_unique(value_arg); } -EncodableList PigeonTransactionCommand::ToEncodableList() const { +EncodableList InternalTransactionCommand::ToEncodableList() const { EncodableList list; list.reserve(4); - list.push_back(EncodableValue((int)type_)); + list.push_back(CustomEncodableValue(type_)); list.push_back(EncodableValue(path_)); list.push_back(data_ ? EncodableValue(*data_) : EncodableValue()); - list.push_back(option_ ? EncodableValue(option_->ToEncodableList()) - : EncodableValue()); + list.push_back(option_ ? CustomEncodableValue(*option_) : EncodableValue()); return list; } -PigeonTransactionCommand PigeonTransactionCommand::FromEncodableList( +InternalTransactionCommand InternalTransactionCommand::FromEncodableList( const EncodableList& list) { - PigeonTransactionCommand decoded( - (PigeonTransactionType)(std::get(list[0])), + InternalTransactionCommand decoded( + std::any_cast( + std::get(list[0])), std::get(list[1])); auto& encodable_data = list[2]; if (!encodable_data.IsNull()) { @@ -571,12 +1250,38 @@ PigeonTransactionCommand PigeonTransactionCommand::FromEncodableList( } auto& encodable_option = list[3]; if (!encodable_option.IsNull()) { - decoded.set_option(PigeonDocumentOption::FromEncodableList( - std::get(encodable_option))); + decoded.set_option(std::any_cast( + std::get(encodable_option))); } return decoded; } +bool InternalTransactionCommand::operator==( + const InternalTransactionCommand& other) const { + return PigeonInternalDeepEquals(type_, other.type_) && + PigeonInternalDeepEquals(path_, other.path_) && + PigeonInternalDeepEquals(data_, other.data_) && + PigeonInternalDeepEquals(option_, other.option_); +} + +bool InternalTransactionCommand::operator!=( + const InternalTransactionCommand& other) const { + return !(*this == other); +} + +size_t InternalTransactionCommand::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(type_); + result = result * 31 + PigeonInternalDeepHash(path_); + result = result * 31 + PigeonInternalDeepHash(data_); + result = result * 31 + PigeonInternalDeepHash(option_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalTransactionCommand& v) { + return v.Hash(); +} + // DocumentReferenceRequest DocumentReferenceRequest::DocumentReferenceRequest(const std::string& path) @@ -584,18 +1289,45 @@ DocumentReferenceRequest::DocumentReferenceRequest(const std::string& path) DocumentReferenceRequest::DocumentReferenceRequest( const std::string& path, const EncodableMap* data, - const PigeonDocumentOption* option, const Source* source, + const InternalDocumentOption* option, const Source* source, const ServerTimestampBehavior* server_timestamp_behavior) : path_(path), data_(data ? std::optional(*data) : std::nullopt), - option_(option ? std::optional(*option) - : std::nullopt), + option_(option ? std::make_unique(*option) + : nullptr), source_(source ? std::optional(*source) : std::nullopt), server_timestamp_behavior_(server_timestamp_behavior ? std::optional( *server_timestamp_behavior) : std::nullopt) {} +DocumentReferenceRequest::DocumentReferenceRequest( + const DocumentReferenceRequest& other) + : path_(other.path_), + data_(other.data_ ? std::optional(*other.data_) + : std::nullopt), + option_(other.option_ + ? std::make_unique(*other.option_) + : nullptr), + source_(other.source_ ? std::optional(*other.source_) + : std::nullopt), + server_timestamp_behavior_(other.server_timestamp_behavior_ + ? std::optional( + *other.server_timestamp_behavior_) + : std::nullopt) {} + +DocumentReferenceRequest& DocumentReferenceRequest::operator=( + const DocumentReferenceRequest& other) { + path_ = other.path_; + data_ = other.data_; + option_ = other.option_ + ? std::make_unique(*other.option_) + : nullptr; + source_ = other.source_; + server_timestamp_behavior_ = other.server_timestamp_behavior_; + return *this; +} + const std::string& DocumentReferenceRequest::path() const { return path_; } void DocumentReferenceRequest::set_path(std::string_view value_arg) { @@ -614,19 +1346,19 @@ void DocumentReferenceRequest::set_data(const EncodableMap& value_arg) { data_ = value_arg; } -const PigeonDocumentOption* DocumentReferenceRequest::option() const { - return option_ ? &(*option_) : nullptr; +const InternalDocumentOption* DocumentReferenceRequest::option() const { + return option_.get(); } void DocumentReferenceRequest::set_option( - const PigeonDocumentOption* value_arg) { - option_ = value_arg ? std::optional(*value_arg) - : std::nullopt; + const InternalDocumentOption* value_arg) { + option_ = value_arg ? std::make_unique(*value_arg) + : nullptr; } void DocumentReferenceRequest::set_option( - const PigeonDocumentOption& value_arg) { - option_ = value_arg; + const InternalDocumentOption& value_arg) { + option_ = std::make_unique(value_arg); } const Source* DocumentReferenceRequest::source() const { @@ -663,11 +1395,10 @@ EncodableList DocumentReferenceRequest::ToEncodableList() const { list.reserve(5); list.push_back(EncodableValue(path_)); list.push_back(data_ ? EncodableValue(*data_) : EncodableValue()); - list.push_back(option_ ? EncodableValue(option_->ToEncodableList()) - : EncodableValue()); - list.push_back(source_ ? EncodableValue((int)(*source_)) : EncodableValue()); + list.push_back(option_ ? CustomEncodableValue(*option_) : EncodableValue()); + list.push_back(source_ ? CustomEncodableValue(*source_) : EncodableValue()); list.push_back(server_timestamp_behavior_ - ? EncodableValue((int)(*server_timestamp_behavior_)) + ? CustomEncodableValue(*server_timestamp_behavior_) : EncodableValue()); return list; } @@ -681,27 +1412,58 @@ DocumentReferenceRequest DocumentReferenceRequest::FromEncodableList( } auto& encodable_option = list[2]; if (!encodable_option.IsNull()) { - decoded.set_option(PigeonDocumentOption::FromEncodableList( - std::get(encodable_option))); + decoded.set_option(std::any_cast( + std::get(encodable_option))); } auto& encodable_source = list[3]; if (!encodable_source.IsNull()) { - decoded.set_source((Source)(std::get(encodable_source))); + decoded.set_source(std::any_cast( + std::get(encodable_source))); } auto& encodable_server_timestamp_behavior = list[4]; if (!encodable_server_timestamp_behavior.IsNull()) { decoded.set_server_timestamp_behavior( - (ServerTimestampBehavior)(std::get( - encodable_server_timestamp_behavior))); + std::any_cast( + std::get( + encodable_server_timestamp_behavior))); } return decoded; } -// PigeonQueryParameters +bool DocumentReferenceRequest::operator==( + const DocumentReferenceRequest& other) const { + return PigeonInternalDeepEquals(path_, other.path_) && + PigeonInternalDeepEquals(data_, other.data_) && + PigeonInternalDeepEquals(option_, other.option_) && + PigeonInternalDeepEquals(source_, other.source_) && + PigeonInternalDeepEquals(server_timestamp_behavior_, + other.server_timestamp_behavior_); +} + +bool DocumentReferenceRequest::operator!=( + const DocumentReferenceRequest& other) const { + return !(*this == other); +} + +size_t DocumentReferenceRequest::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(path_); + result = result * 31 + PigeonInternalDeepHash(data_); + result = result * 31 + PigeonInternalDeepHash(option_); + result = result * 31 + PigeonInternalDeepHash(source_); + result = result * 31 + PigeonInternalDeepHash(server_timestamp_behavior_); + return result; +} + +size_t PigeonInternalDeepHash(const DocumentReferenceRequest& v) { + return v.Hash(); +} -PigeonQueryParameters::PigeonQueryParameters() {} +// InternalQueryParameters -PigeonQueryParameters::PigeonQueryParameters( +InternalQueryParameters::InternalQueryParameters() {} + +InternalQueryParameters::InternalQueryParameters( const EncodableList* where, const EncodableList* order_by, const int64_t* limit, const int64_t* limit_to_last, const EncodableList* start_at, const EncodableList* start_after, @@ -723,118 +1485,120 @@ PigeonQueryParameters::PigeonQueryParameters( filters_(filters ? std::optional(*filters) : std::nullopt) { } -const EncodableList* PigeonQueryParameters::where() const { +const EncodableList* InternalQueryParameters::where() const { return where_ ? &(*where_) : nullptr; } -void PigeonQueryParameters::set_where(const EncodableList* value_arg) { +void InternalQueryParameters::set_where(const EncodableList* value_arg) { where_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_where(const EncodableList& value_arg) { +void InternalQueryParameters::set_where(const EncodableList& value_arg) { where_ = value_arg; } -const EncodableList* PigeonQueryParameters::order_by() const { +const EncodableList* InternalQueryParameters::order_by() const { return order_by_ ? &(*order_by_) : nullptr; } -void PigeonQueryParameters::set_order_by(const EncodableList* value_arg) { +void InternalQueryParameters::set_order_by(const EncodableList* value_arg) { order_by_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_order_by(const EncodableList& value_arg) { +void InternalQueryParameters::set_order_by(const EncodableList& value_arg) { order_by_ = value_arg; } -const int64_t* PigeonQueryParameters::limit() const { +const int64_t* InternalQueryParameters::limit() const { return limit_ ? &(*limit_) : nullptr; } -void PigeonQueryParameters::set_limit(const int64_t* value_arg) { +void InternalQueryParameters::set_limit(const int64_t* value_arg) { limit_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_limit(int64_t value_arg) { limit_ = value_arg; } +void InternalQueryParameters::set_limit(int64_t value_arg) { + limit_ = value_arg; +} -const int64_t* PigeonQueryParameters::limit_to_last() const { +const int64_t* InternalQueryParameters::limit_to_last() const { return limit_to_last_ ? &(*limit_to_last_) : nullptr; } -void PigeonQueryParameters::set_limit_to_last(const int64_t* value_arg) { +void InternalQueryParameters::set_limit_to_last(const int64_t* value_arg) { limit_to_last_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_limit_to_last(int64_t value_arg) { +void InternalQueryParameters::set_limit_to_last(int64_t value_arg) { limit_to_last_ = value_arg; } -const EncodableList* PigeonQueryParameters::start_at() const { +const EncodableList* InternalQueryParameters::start_at() const { return start_at_ ? &(*start_at_) : nullptr; } -void PigeonQueryParameters::set_start_at(const EncodableList* value_arg) { +void InternalQueryParameters::set_start_at(const EncodableList* value_arg) { start_at_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_start_at(const EncodableList& value_arg) { +void InternalQueryParameters::set_start_at(const EncodableList& value_arg) { start_at_ = value_arg; } -const EncodableList* PigeonQueryParameters::start_after() const { +const EncodableList* InternalQueryParameters::start_after() const { return start_after_ ? &(*start_after_) : nullptr; } -void PigeonQueryParameters::set_start_after(const EncodableList* value_arg) { +void InternalQueryParameters::set_start_after(const EncodableList* value_arg) { start_after_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_start_after(const EncodableList& value_arg) { +void InternalQueryParameters::set_start_after(const EncodableList& value_arg) { start_after_ = value_arg; } -const EncodableList* PigeonQueryParameters::end_at() const { +const EncodableList* InternalQueryParameters::end_at() const { return end_at_ ? &(*end_at_) : nullptr; } -void PigeonQueryParameters::set_end_at(const EncodableList* value_arg) { +void InternalQueryParameters::set_end_at(const EncodableList* value_arg) { end_at_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_end_at(const EncodableList& value_arg) { +void InternalQueryParameters::set_end_at(const EncodableList& value_arg) { end_at_ = value_arg; } -const EncodableList* PigeonQueryParameters::end_before() const { +const EncodableList* InternalQueryParameters::end_before() const { return end_before_ ? &(*end_before_) : nullptr; } -void PigeonQueryParameters::set_end_before(const EncodableList* value_arg) { +void InternalQueryParameters::set_end_before(const EncodableList* value_arg) { end_before_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_end_before(const EncodableList& value_arg) { +void InternalQueryParameters::set_end_before(const EncodableList& value_arg) { end_before_ = value_arg; } -const EncodableMap* PigeonQueryParameters::filters() const { +const EncodableMap* InternalQueryParameters::filters() const { return filters_ ? &(*filters_) : nullptr; } -void PigeonQueryParameters::set_filters(const EncodableMap* value_arg) { +void InternalQueryParameters::set_filters(const EncodableMap* value_arg) { filters_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonQueryParameters::set_filters(const EncodableMap& value_arg) { +void InternalQueryParameters::set_filters(const EncodableMap& value_arg) { filters_ = value_arg; } -EncodableList PigeonQueryParameters::ToEncodableList() const { +EncodableList InternalQueryParameters::ToEncodableList() const { EncodableList list; list.reserve(9); list.push_back(where_ ? EncodableValue(*where_) : EncodableValue()); @@ -851,9 +1615,9 @@ EncodableList PigeonQueryParameters::ToEncodableList() const { return list; } -PigeonQueryParameters PigeonQueryParameters::FromEncodableList( +InternalQueryParameters InternalQueryParameters::FromEncodableList( const EncodableList& list) { - PigeonQueryParameters decoded; + InternalQueryParameters decoded; auto& encodable_where = list[0]; if (!encodable_where.IsNull()) { decoded.set_where(std::get(encodable_where)); @@ -864,11 +1628,11 @@ PigeonQueryParameters PigeonQueryParameters::FromEncodableList( } auto& encodable_limit = list[2]; if (!encodable_limit.IsNull()) { - decoded.set_limit(encodable_limit.LongValue()); + decoded.set_limit(std::get(encodable_limit)); } auto& encodable_limit_to_last = list[3]; if (!encodable_limit_to_last.IsNull()) { - decoded.set_limit_to_last(encodable_limit_to_last.LongValue()); + decoded.set_limit_to_last(std::get(encodable_limit_to_last)); } auto& encodable_start_at = list[4]; if (!encodable_start_at.IsNull()) { @@ -893,6 +1657,42 @@ PigeonQueryParameters PigeonQueryParameters::FromEncodableList( return decoded; } +bool InternalQueryParameters::operator==( + const InternalQueryParameters& other) const { + return PigeonInternalDeepEquals(where_, other.where_) && + PigeonInternalDeepEquals(order_by_, other.order_by_) && + PigeonInternalDeepEquals(limit_, other.limit_) && + PigeonInternalDeepEquals(limit_to_last_, other.limit_to_last_) && + PigeonInternalDeepEquals(start_at_, other.start_at_) && + PigeonInternalDeepEquals(start_after_, other.start_after_) && + PigeonInternalDeepEquals(end_at_, other.end_at_) && + PigeonInternalDeepEquals(end_before_, other.end_before_) && + PigeonInternalDeepEquals(filters_, other.filters_); +} + +bool InternalQueryParameters::operator!=( + const InternalQueryParameters& other) const { + return !(*this == other); +} + +size_t InternalQueryParameters::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(where_); + result = result * 31 + PigeonInternalDeepHash(order_by_); + result = result * 31 + PigeonInternalDeepHash(limit_); + result = result * 31 + PigeonInternalDeepHash(limit_to_last_); + result = result * 31 + PigeonInternalDeepHash(start_at_); + result = result * 31 + PigeonInternalDeepHash(start_after_); + result = result * 31 + PigeonInternalDeepHash(end_at_); + result = result * 31 + PigeonInternalDeepHash(end_before_); + result = result * 31 + PigeonInternalDeepHash(filters_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalQueryParameters& v) { + return v.Hash(); +} + // AggregateQuery AggregateQuery::AggregateQuery(const AggregateType& type) : type_(type) {} @@ -923,13 +1723,14 @@ void AggregateQuery::set_field(std::string_view value_arg) { EncodableList AggregateQuery::ToEncodableList() const { EncodableList list; list.reserve(2); - list.push_back(EncodableValue((int)type_)); + list.push_back(CustomEncodableValue(type_)); list.push_back(field_ ? EncodableValue(*field_) : EncodableValue()); return list; } AggregateQuery AggregateQuery::FromEncodableList(const EncodableList& list) { - AggregateQuery decoded((AggregateType)(std::get(list[0]))); + AggregateQuery decoded(std::any_cast( + std::get(list[0]))); auto& encodable_field = list[1]; if (!encodable_field.IsNull()) { decoded.set_field(std::get(encodable_field)); @@ -937,6 +1738,24 @@ AggregateQuery AggregateQuery::FromEncodableList(const EncodableList& list) { return decoded; } +bool AggregateQuery::operator==(const AggregateQuery& other) const { + return PigeonInternalDeepEquals(type_, other.type_) && + PigeonInternalDeepEquals(field_, other.field_); +} + +bool AggregateQuery::operator!=(const AggregateQuery& other) const { + return !(*this == other); +} + +size_t AggregateQuery::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(type_); + result = result * 31 + PigeonInternalDeepHash(field_); + return result; +} + +size_t PigeonInternalDeepHash(const AggregateQuery& v) { return v.Hash(); } + // AggregateQueryResponse AggregateQueryResponse::AggregateQueryResponse(const AggregateType& type) @@ -980,7 +1799,7 @@ void AggregateQueryResponse::set_value(double value_arg) { value_ = value_arg; } EncodableList AggregateQueryResponse::ToEncodableList() const { EncodableList list; list.reserve(3); - list.push_back(EncodableValue((int)type_)); + list.push_back(CustomEncodableValue(type_)); list.push_back(field_ ? EncodableValue(*field_) : EncodableValue()); list.push_back(value_ ? EncodableValue(*value_) : EncodableValue()); return list; @@ -988,7 +1807,8 @@ EncodableList AggregateQueryResponse::ToEncodableList() const { AggregateQueryResponse AggregateQueryResponse::FromEncodableList( const EncodableList& list) { - AggregateQueryResponse decoded((AggregateType)(std::get(list[0]))); + AggregateQueryResponse decoded(std::any_cast( + std::get(list[0]))); auto& encodable_field = list[1]; if (!encodable_field.IsNull()) { decoded.set_field(std::get(encodable_field)); @@ -1000,189 +1820,407 @@ AggregateQueryResponse AggregateQueryResponse::FromEncodableList( return decoded; } +bool AggregateQueryResponse::operator==( + const AggregateQueryResponse& other) const { + return PigeonInternalDeepEquals(type_, other.type_) && + PigeonInternalDeepEquals(field_, other.field_) && + PigeonInternalDeepEquals(value_, other.value_); +} + +bool AggregateQueryResponse::operator!=( + const AggregateQueryResponse& other) const { + return !(*this == other); +} + +size_t AggregateQueryResponse::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(type_); + result = result * 31 + PigeonInternalDeepHash(field_); + result = result * 31 + PigeonInternalDeepHash(value_); + return result; +} + +size_t PigeonInternalDeepHash(const AggregateQueryResponse& v) { + return v.Hash(); +} + FirebaseFirestoreHostApiCodecSerializer:: FirebaseFirestoreHostApiCodecSerializer() {} EncodableValue FirebaseFirestoreHostApiCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { - case 128: - return CustomEncodableValue(AggregateQuery::FromEncodableList( + case 129: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 130: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue(static_cast(enum_arg_value)); + } + case 131: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 132: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 133: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 134: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast( + enum_arg_value)); + } + case 135: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 136: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 137: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 138: { + return CustomEncodableValue(InternalFirebaseSettings::FromEncodableList( std::get(ReadValue(stream)))); - case 129: - return CustomEncodableValue(AggregateQueryResponse::FromEncodableList( + } + case 139: { + return CustomEncodableValue(FirestorePigeonFirebaseApp::FromEncodableList( std::get(ReadValue(stream)))); - case 130: - return CustomEncodableValue(DocumentReferenceRequest::FromEncodableList( + } + case 140: { + return CustomEncodableValue(InternalSnapshotMetadata::FromEncodableList( std::get(ReadValue(stream)))); - case 131: - return CustomEncodableValue(FirestorePigeonFirebaseApp::FromEncodableList( + } + case 141: { + return CustomEncodableValue(InternalDocumentSnapshot::FromEncodableList( std::get(ReadValue(stream)))); - case 132: - return CustomEncodableValue(PigeonDocumentChange::FromEncodableList( + } + case 142: { + return CustomEncodableValue(InternalDocumentChange::FromEncodableList( std::get(ReadValue(stream)))); - case 133: - return CustomEncodableValue(PigeonDocumentOption::FromEncodableList( + } + case 143: { + return CustomEncodableValue(InternalQuerySnapshot::FromEncodableList( std::get(ReadValue(stream)))); - case 134: - return CustomEncodableValue(PigeonDocumentSnapshot::FromEncodableList( + } + case 144: { + return CustomEncodableValue(InternalPipelineResult::FromEncodableList( std::get(ReadValue(stream)))); - case 135: - return CustomEncodableValue(PigeonFirebaseSettings::FromEncodableList( + } + case 145: { + return CustomEncodableValue(InternalPipelineSnapshot::FromEncodableList( std::get(ReadValue(stream)))); - case 136: - return CustomEncodableValue(PigeonGetOptions::FromEncodableList( + } + case 146: { + return CustomEncodableValue(InternalGetOptions::FromEncodableList( std::get(ReadValue(stream)))); - case 137: - return CustomEncodableValue(PigeonQueryParameters::FromEncodableList( + } + case 147: { + return CustomEncodableValue(InternalDocumentOption::FromEncodableList( std::get(ReadValue(stream)))); - case 138: - return CustomEncodableValue(PigeonQuerySnapshot::FromEncodableList( + } + case 148: { + return CustomEncodableValue(InternalTransactionCommand::FromEncodableList( std::get(ReadValue(stream)))); - case 139: - return CustomEncodableValue(PigeonSnapshotMetadata::FromEncodableList( + } + case 149: { + return CustomEncodableValue(DocumentReferenceRequest::FromEncodableList( std::get(ReadValue(stream)))); - case 140: - return CustomEncodableValue(PigeonTransactionCommand::FromEncodableList( + } + case 150: { + return CustomEncodableValue(InternalQueryParameters::FromEncodableList( + std::get(ReadValue(stream)))); + } + case 151: { + return CustomEncodableValue(AggregateQuery::FromEncodableList( + std::get(ReadValue(stream)))); + } + case 152: { + return CustomEncodableValue(AggregateQueryResponse::FromEncodableList( std::get(ReadValue(stream)))); + } default: - return cloud_firestore_windows::FirestoreCodec::ReadValueOfType(type, - stream); + return ::cloud_firestore_windows::FirestoreCodec::ReadValueOfType(type, + stream); } } void FirebaseFirestoreHostApiCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { - if (custom_value->type() == typeid(AggregateQuery)) { - stream->WriteByte(128); - WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(AggregateQueryResponse)) { + if (custom_value->type() == typeid(DocumentChangeType)) { stream->WriteByte(129); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); return; } - if (custom_value->type() == typeid(DocumentReferenceRequest)) { + if (custom_value->type() == typeid(Source)) { stream->WriteByte(130); + WriteValue(EncodableValue( + static_cast(std::any_cast(*custom_value))), + stream); + return; + } + if (custom_value->type() == typeid(ListenSource)) { + stream->WriteByte(131); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); + return; + } + if (custom_value->type() == typeid(ServerTimestampBehavior)) { + stream->WriteByte(132); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); + return; + } + if (custom_value->type() == typeid(AggregateSource)) { + stream->WriteByte(133); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); + return; + } + if (custom_value->type() == + typeid(PersistenceCacheIndexManagerRequestEnum)) { + stream->WriteByte(134); + WriteValue(EncodableValue(static_cast( + std::any_cast( + *custom_value))), + stream); + return; + } + if (custom_value->type() == typeid(InternalTransactionResult)) { + stream->WriteByte(135); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); + return; + } + if (custom_value->type() == typeid(InternalTransactionType)) { + stream->WriteByte(136); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); + return; + } + if (custom_value->type() == typeid(AggregateType)) { + stream->WriteByte(137); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); + return; + } + if (custom_value->type() == typeid(InternalFirebaseSettings)) { + stream->WriteByte(138); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } if (custom_value->type() == typeid(FirestorePigeonFirebaseApp)) { - stream->WriteByte(131); + stream->WriteByte(139); WriteValue(EncodableValue( std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonDocumentChange)) { - stream->WriteByte(132); + if (custom_value->type() == typeid(InternalSnapshotMetadata)) { + stream->WriteByte(140); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonDocumentOption)) { - stream->WriteByte(133); + if (custom_value->type() == typeid(InternalDocumentSnapshot)) { + stream->WriteByte(141); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonDocumentSnapshot)) { - stream->WriteByte(134); + if (custom_value->type() == typeid(InternalDocumentChange)) { + stream->WriteByte(142); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonFirebaseSettings)) { - stream->WriteByte(135); + if (custom_value->type() == typeid(InternalQuerySnapshot)) { + stream->WriteByte(143); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonGetOptions)) { - stream->WriteByte(136); + if (custom_value->type() == typeid(InternalPipelineResult)) { + stream->WriteByte(144); WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonQueryParameters)) { - stream->WriteByte(137); + if (custom_value->type() == typeid(InternalPipelineSnapshot)) { + stream->WriteByte(145); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonQuerySnapshot)) { - stream->WriteByte(138); + if (custom_value->type() == typeid(InternalGetOptions)) { + stream->WriteByte(146); + WriteValue(EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(InternalDocumentOption)) { + stream->WriteByte(147); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonSnapshotMetadata)) { - stream->WriteByte(139); + if (custom_value->type() == typeid(InternalTransactionCommand)) { + stream->WriteByte(148); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(DocumentReferenceRequest)) { + stream->WriteByte(149); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonTransactionCommand)) { - stream->WriteByte(140); + if (custom_value->type() == typeid(InternalQueryParameters)) { + stream->WriteByte(150); + WriteValue( + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(AggregateQuery)) { + stream->WriteByte(151); + WriteValue( + EncodableValue( + std::any_cast(*custom_value).ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(AggregateQueryResponse)) { + stream->WriteByte(152); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } } - cloud_firestore_windows::FirestoreCodec::WriteValue(value, stream); + ::cloud_firestore_windows::FirestoreCodec::WriteValue(value, stream); } /// The codec used by FirebaseFirestoreHostApi. -const flutter::StandardMessageCodec& FirebaseFirestoreHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebaseFirestoreHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &FirebaseFirestoreHostApiCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseFirestoreHostApi` to handle messages through // the `binary_messenger`. -void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseFirestoreHostApi* api) { +void FirebaseFirestoreHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseFirestoreHostApi* api) { + FirebaseFirestoreHostApi::SetUp(binary_messenger, api, ""); +} + +void FirebaseFirestoreHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, FirebaseFirestoreHostApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = + message_channel_suffix.length() > 0 + ? std::string(".") + message_channel_suffix + : ""; { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.loadBundle", + "FirebaseFirestoreHostApi.loadBundle" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1216,19 +2254,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.namedQueryGet", + "FirebaseFirestoreHostApi.namedQueryGet" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1250,11 +2289,12 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, reply(WrapError("options_arg unexpectedly null.")); return; } - const auto& options_arg = std::any_cast( - std::get(encodable_options_arg)); + const auto& options_arg = + std::any_cast( + std::get(encodable_options_arg)); api->NamedQueryGet( app_arg, name_arg, options_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -1269,19 +2309,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.clearPersistence", + "FirebaseFirestoreHostApi.clearPersistence" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1307,19 +2348,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.disableNetwork", + "FirebaseFirestoreHostApi.disableNetwork" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1345,19 +2387,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.enableNetwork", + "FirebaseFirestoreHostApi.enableNetwork" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1383,19 +2426,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.terminate", + "FirebaseFirestoreHostApi.terminate" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1421,19 +2465,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.waitForPendingWrites", + "FirebaseFirestoreHostApi.waitForPendingWrites" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1459,19 +2504,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.setIndexConfiguration", + "FirebaseFirestoreHostApi.setIndexConfiguration" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1505,19 +2551,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.setLoggingEnabled", + "FirebaseFirestoreHostApi.setLoggingEnabled" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_logging_enabled_arg = args.at(0); @@ -1543,19 +2590,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.snapshotsInSyncSetup", + "FirebaseFirestoreHostApi.snapshotsInSyncSetup" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1582,19 +2630,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.transactionCreate", + "FirebaseFirestoreHostApi.transactionCreate" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1635,19 +2684,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.transactionStoreResult", + "FirebaseFirestoreHostApi.transactionStoreResult" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_transaction_id_arg = args.at(0); @@ -1662,9 +2712,10 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, reply(WrapError("result_type_arg unexpectedly null.")); return; } - const PigeonTransactionResult& result_type_arg = - (PigeonTransactionResult) - encodable_result_type_arg.LongValue(); + const auto& result_type_arg = + std::any_cast( + std::get( + encodable_result_type_arg)); const auto& encodable_commands_arg = args.at(2); const auto* commands_arg = std::get_if(&encodable_commands_arg); @@ -1684,19 +2735,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.transactionGet", + "FirebaseFirestoreHostApi.transactionGet" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1722,7 +2774,7 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& path_arg = std::get(encodable_path_arg); api->TransactionGet( app_arg, transaction_id_arg, path_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -1737,19 +2789,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.documentReferenceSet", + "FirebaseFirestoreHostApi.documentReferenceSet" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1784,19 +2837,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.documentReferenceUpdate", + "FirebaseFirestoreHostApi.documentReferenceUpdate" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1831,19 +2885,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.documentReferenceGet", + "FirebaseFirestoreHostApi.documentReferenceGet" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1864,7 +2919,7 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_request_arg)); api->DocumentReferenceGet( app_arg, request_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -1879,19 +2934,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.documentReferenceDelete", + "FirebaseFirestoreHostApi.documentReferenceDelete" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1926,19 +2982,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.queryGet", + "FirebaseFirestoreHostApi.queryGet" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1968,45 +3025,47 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& parameters_arg = - std::any_cast( + std::any_cast( std::get(encodable_parameters_arg)); const auto& encodable_options_arg = args.at(4); if (encodable_options_arg.IsNull()) { reply(WrapError("options_arg unexpectedly null.")); return; } - const auto& options_arg = std::any_cast( - std::get(encodable_options_arg)); - api->QueryGet( - app_arg, path_arg, is_collection_group_arg, parameters_arg, - options_arg, [reply](ErrorOr&& output) { - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - EncodableList wrapped; - wrapped.push_back( - CustomEncodableValue(std::move(output).TakeValue())); - reply(EncodableValue(std::move(wrapped))); - }); + const auto& options_arg = + std::any_cast( + std::get(encodable_options_arg)); + api->QueryGet(app_arg, path_arg, is_collection_group_arg, + parameters_arg, options_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(CustomEncodableValue( + std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); } catch (const std::exception& exception) { reply(WrapError(exception.what())); } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.aggregateQuery", + "FirebaseFirestoreHostApi.aggregateQuery" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2029,15 +3088,15 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& parameters_arg = - std::any_cast( + std::any_cast( std::get(encodable_parameters_arg)); const auto& encodable_source_arg = args.at(3); if (encodable_source_arg.IsNull()) { reply(WrapError("source_arg unexpectedly null.")); return; } - const AggregateSource& source_arg = - (AggregateSource)encodable_source_arg.LongValue(); + const auto& source_arg = std::any_cast( + std::get(encodable_source_arg)); const auto& encodable_queries_arg = args.at(4); if (encodable_queries_arg.IsNull()) { reply(WrapError("queries_arg unexpectedly null.")); @@ -2069,19 +3128,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.writeBatchCommit", + "FirebaseFirestoreHostApi.writeBatchCommit" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2115,19 +3175,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.querySnapshot", + "FirebaseFirestoreHostApi.querySnapshot" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2157,15 +3218,16 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& parameters_arg = - std::any_cast( + std::any_cast( std::get(encodable_parameters_arg)); const auto& encodable_options_arg = args.at(4); if (encodable_options_arg.IsNull()) { reply(WrapError("options_arg unexpectedly null.")); return; } - const auto& options_arg = std::any_cast( - std::get(encodable_options_arg)); + const auto& options_arg = + std::any_cast( + std::get(encodable_options_arg)); const auto& encodable_include_metadata_changes_arg = args.at(5); if (encodable_include_metadata_changes_arg.IsNull()) { reply(WrapError( @@ -2179,8 +3241,8 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, reply(WrapError("source_arg unexpectedly null.")); return; } - const ListenSource& source_arg = - (ListenSource)encodable_source_arg.LongValue(); + const auto& source_arg = std::any_cast( + std::get(encodable_source_arg)); api->QuerySnapshot( app_arg, path_arg, is_collection_group_arg, parameters_arg, options_arg, include_metadata_changes_arg, source_arg, @@ -2199,19 +3261,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.documentReferenceSnapshot", + "FirebaseFirestoreHostApi.documentReferenceSnapshot" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2243,8 +3306,8 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, reply(WrapError("source_arg unexpectedly null.")); return; } - const ListenSource& source_arg = - (ListenSource)encodable_source_arg.LongValue(); + const auto& source_arg = std::any_cast( + std::get(encodable_source_arg)); api->DocumentReferenceSnapshot( app_arg, parameters_arg, include_metadata_changes_arg, source_arg, [reply](ErrorOr&& output) { @@ -2262,19 +3325,20 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.cloud_firestore_platform_interface." - "FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest", + "FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2290,9 +3354,9 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, reply(WrapError("request_arg unexpectedly null.")); return; } - const PersistenceCacheIndexManagerRequestEnum& request_arg = - (PersistenceCacheIndexManagerRequestEnum) - encodable_request_arg.LongValue(); + const auto& request_arg = + std::any_cast( + std::get(encodable_request_arg)); api->PersistenceCacheIndexManagerRequest( app_arg, request_arg, [reply](std::optional&& output) { @@ -2309,7 +3373,58 @@ void FirebaseFirestoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.cloud_firestore_platform_interface." + "FirebaseFirestoreHostApi.executePipeline" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_arg = args.at(0); + if (encodable_app_arg.IsNull()) { + reply(WrapError("app_arg unexpectedly null.")); + return; + } + const auto& app_arg = + std::any_cast( + std::get(encodable_app_arg)); + const auto& encodable_stages_arg = args.at(1); + if (encodable_stages_arg.IsNull()) { + reply(WrapError("stages_arg unexpectedly null.")); + return; + } + const auto& stages_arg = + std::get(encodable_stages_arg); + const auto& encodable_options_arg = args.at(2); + const auto* options_arg = + std::get_if(&encodable_options_arg); + api->ExecutePipeline( + app_arg, stages_arg, options_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); } } } diff --git a/packages/cloud_firestore/cloud_firestore/windows/messages.g.h b/packages/cloud_firestore/cloud_firestore/windows/messages.g.h index 8aecb887facc..fcc6a5e80a11 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/messages.g.h +++ b/packages/cloud_firestore/cloud_firestore/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -27,17 +27,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -64,12 +64,12 @@ class ErrorOr { enum class DocumentChangeType { // Indicates a new document was added to the set of documents matching the // query. - added = 0, + kAdded = 0, // Indicates a document within the query was modified. - modified = 1, + kModified = 1, // Indicates a document within the query was removed (either deleted or no // longer matches the query. - removed = 2 + kRemoved = 2 }; // An enumeration of firestore source types. @@ -77,7 +77,7 @@ enum class Source { // Causes Firestore to try to retrieve an up-to-date (server-retrieved) // snapshot, but fall back to // returning cached data if the server can't be reached. - serverAndCache = 0, + kServerAndCache = 0, // Causes Firestore to avoid the cache, generating an error if the server // cannot be reached. Note // that the cache will still be updated if the server request succeeds. Also @@ -85,7 +85,7 @@ enum class Source { // latency-compensation still takes effect, so any pending write operations // will be visible in the // returned data (merged into the server-provided data). - server = 1, + kServer = 1, // Causes Firestore to immediately return a value from the cache, ignoring the // server completely // (implying that the returned value may be stale with respect to the value on @@ -93,7 +93,7 @@ enum class Source { // there is no data in the cache to satisfy the `get` call, // [DocumentReference.get] will throw a [FirebaseException] and // [Query.get] will return an empty [QuerySnapshotPlatform] with no documents. - cache = 2 + kCache = 2 }; // The listener retrieves data and listens to updates from the local Firestore @@ -108,63 +108,63 @@ enum class ListenSource { // cache and retrieve up-to-date snapshots from the Firestore server. // Snapshot events will be triggered on local mutations and server side // updates. - defaultSource = 0, + kDefaultSource = 0, // The listener retrieves data and listens to updates from the local Firestore // cache only. // If the cache is empty, an empty snapshot will be returned. // Snapshot events will be triggered on cache updates, like local mutations or // load bundles. - cache = 1 + kCache = 1 }; enum class ServerTimestampBehavior { // Return null for [FieldValue.serverTimestamp()] values that have not yet - none = 0, + kNone = 0, // Return local estimates for [FieldValue.serverTimestamp()] values that have // not yet been set to their final value. - estimate = 1, + kEstimate = 1, // Return the previous value for [FieldValue.serverTimestamp()] values that // have not yet been set to their final value. - previous = 2 + kPrevious = 2 }; // [AggregateSource] represents the source of data for an [AggregateQuery]. enum class AggregateSource { // Indicates that the data should be retrieved from the server. - server = 0 + kServer = 0 }; -// [PersistenceCacheIndexManagerRequest] represents the request types for the -// persistence cache index manager. +// [PersistenceCacheIndexManagerRequestEnum] represents the request types for +// the persistence cache index manager. enum class PersistenceCacheIndexManagerRequestEnum { - enableIndexAutoCreation = 0, - disableIndexAutoCreation = 1, - deleteAllIndexes = 2 + kEnableIndexAutoCreation = 0, + kDisableIndexAutoCreation = 1, + kDeleteAllIndexes = 2 }; -enum class PigeonTransactionResult { success = 0, failure = 1 }; +enum class InternalTransactionResult { kSuccess = 0, kFailure = 1 }; -enum class PigeonTransactionType { - get = 0, - update = 1, - set = 2, - deleteType = 3 +enum class InternalTransactionType { + kGet = 0, + kUpdate = 1, + kSet = 2, + kDeleteType = 3 }; -enum class AggregateType { count = 0, sum = 1, average = 2 }; +enum class AggregateType { kCount = 0, kSum = 1, kAverage = 2 }; // Generated class from Pigeon that represents data sent in messages. -class PigeonFirebaseSettings { +class InternalFirebaseSettings { public: // Constructs an object setting all non-nullable fields. - explicit PigeonFirebaseSettings(bool ignore_undefined_properties); + explicit InternalFirebaseSettings(bool ignore_undefined_properties); // Constructs an object setting all fields. - explicit PigeonFirebaseSettings(const bool* persistence_enabled, - const std::string* host, - const bool* ssl_enabled, - const int64_t* cache_size_bytes, - bool ignore_undefined_properties); + explicit InternalFirebaseSettings(const bool* persistence_enabled, + const std::string* host, + const bool* ssl_enabled, + const int64_t* cache_size_bytes, + bool ignore_undefined_properties); const bool* persistence_enabled() const; void set_persistence_enabled(const bool* value_arg); @@ -185,10 +185,22 @@ class PigeonFirebaseSettings { bool ignore_undefined_properties() const; void set_ignore_undefined_properties(bool value_arg); + bool operator==(const InternalFirebaseSettings& other) const; + bool operator!=(const InternalFirebaseSettings& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalFirebaseSettings FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static PigeonFirebaseSettings FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirestorePigeonFirebaseApp; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; @@ -204,34 +216,54 @@ class FirestorePigeonFirebaseApp { public: // Constructs an object setting all fields. explicit FirestorePigeonFirebaseApp(const std::string& app_name, - const PigeonFirebaseSettings& settings, + const InternalFirebaseSettings& settings, const std::string& database_u_r_l); + ~FirestorePigeonFirebaseApp() = default; + FirestorePigeonFirebaseApp(const FirestorePigeonFirebaseApp& other); + FirestorePigeonFirebaseApp& operator=( + const FirestorePigeonFirebaseApp& other); + FirestorePigeonFirebaseApp(FirestorePigeonFirebaseApp&& other) = default; + FirestorePigeonFirebaseApp& operator=( + FirestorePigeonFirebaseApp&& other) noexcept = default; const std::string& app_name() const; void set_app_name(std::string_view value_arg); - const PigeonFirebaseSettings& settings() const; - void set_settings(const PigeonFirebaseSettings& value_arg); + const InternalFirebaseSettings& settings() const; + void set_settings(const InternalFirebaseSettings& value_arg); const std::string& database_u_r_l() const; void set_database_u_r_l(std::string_view value_arg); + bool operator==(const FirestorePigeonFirebaseApp& other) const; + bool operator!=(const FirestorePigeonFirebaseApp& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static FirestorePigeonFirebaseApp FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::string app_name_; - PigeonFirebaseSettings settings_; + std::unique_ptr settings_; std::string database_u_r_l_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonSnapshotMetadata { +class InternalSnapshotMetadata { public: // Constructs an object setting all fields. - explicit PigeonSnapshotMetadata(bool has_pending_writes, bool is_from_cache); + explicit InternalSnapshotMetadata(bool has_pending_writes, + bool is_from_cache); bool has_pending_writes() const; void set_has_pending_writes(bool value_arg); @@ -239,13 +271,24 @@ class PigeonSnapshotMetadata { bool is_from_cache() const; void set_is_from_cache(bool value_arg); - static PigeonSnapshotMetadata FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + bool operator==(const InternalSnapshotMetadata& other) const; + bool operator!=(const InternalSnapshotMetadata& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + static InternalSnapshotMetadata FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: private: - friend class PigeonDocumentSnapshot; - friend class PigeonQuerySnapshot; + friend class InternalDocumentSnapshot; + friend class InternalQuerySnapshot; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; bool has_pending_writes_; @@ -253,53 +296,76 @@ class PigeonSnapshotMetadata { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonDocumentSnapshot { +class InternalDocumentSnapshot { public: // Constructs an object setting all non-nullable fields. - explicit PigeonDocumentSnapshot(const std::string& path, - const PigeonSnapshotMetadata& metadata); + explicit InternalDocumentSnapshot(const std::string& path, + const InternalSnapshotMetadata& metadata); // Constructs an object setting all fields. - explicit PigeonDocumentSnapshot(const std::string& path, - const flutter::EncodableMap* data, - const PigeonSnapshotMetadata& metadata); - + explicit InternalDocumentSnapshot(const std::string& path, + const ::flutter::EncodableMap* data, + const InternalSnapshotMetadata& metadata); + + ~InternalDocumentSnapshot() = default; + InternalDocumentSnapshot(const InternalDocumentSnapshot& other); + InternalDocumentSnapshot& operator=(const InternalDocumentSnapshot& other); + InternalDocumentSnapshot(InternalDocumentSnapshot&& other) = default; + InternalDocumentSnapshot& operator=( + InternalDocumentSnapshot&& other) noexcept = default; const std::string& path() const; void set_path(std::string_view value_arg); - const flutter::EncodableMap* data() const; - void set_data(const flutter::EncodableMap* value_arg); - void set_data(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* data() const; + void set_data(const ::flutter::EncodableMap* value_arg); + void set_data(const ::flutter::EncodableMap& value_arg); + + const InternalSnapshotMetadata& metadata() const; + void set_metadata(const InternalSnapshotMetadata& value_arg); - const PigeonSnapshotMetadata& metadata() const; - void set_metadata(const PigeonSnapshotMetadata& value_arg); + bool operator==(const InternalDocumentSnapshot& other) const; + bool operator!=(const InternalDocumentSnapshot& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; - static PigeonDocumentSnapshot FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + static InternalDocumentSnapshot FromEncodableList( + const ::flutter::EncodableList& list); + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - friend class PigeonDocumentChange; + private: + friend class InternalDocumentChange; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::string path_; - std::optional data_; - PigeonSnapshotMetadata metadata_; + std::optional<::flutter::EncodableMap> data_; + std::unique_ptr metadata_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonDocumentChange { +class InternalDocumentChange { public: // Constructs an object setting all fields. - explicit PigeonDocumentChange(const DocumentChangeType& type, - const PigeonDocumentSnapshot& document, - int64_t old_index, int64_t new_index); - + explicit InternalDocumentChange(const DocumentChangeType& type, + const InternalDocumentSnapshot& document, + int64_t old_index, int64_t new_index); + + ~InternalDocumentChange() = default; + InternalDocumentChange(const InternalDocumentChange& other); + InternalDocumentChange& operator=(const InternalDocumentChange& other); + InternalDocumentChange(InternalDocumentChange&& other) = default; + InternalDocumentChange& operator=(InternalDocumentChange&& other) noexcept = + default; const DocumentChangeType& type() const; void set_type(const DocumentChangeType& value_arg); - const PigeonDocumentSnapshot& document() const; - void set_document(const PigeonDocumentSnapshot& value_arg); + const InternalDocumentSnapshot& document() const; + void set_document(const InternalDocumentSnapshot& value_arg); int64_t old_index() const; void set_old_index(int64_t value_arg); @@ -307,52 +373,170 @@ class PigeonDocumentChange { int64_t new_index() const; void set_new_index(int64_t value_arg); - static PigeonDocumentChange FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + bool operator==(const InternalDocumentChange& other) const; + bool operator!=(const InternalDocumentChange& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + static InternalDocumentChange FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + private: + private: private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; DocumentChangeType type_; - PigeonDocumentSnapshot document_; + std::unique_ptr document_; int64_t old_index_; int64_t new_index_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonQuerySnapshot { +class InternalQuerySnapshot { public: // Constructs an object setting all fields. - explicit PigeonQuerySnapshot(const flutter::EncodableList& documents, - const flutter::EncodableList& document_changes, - const PigeonSnapshotMetadata& metadata); + explicit InternalQuerySnapshot( + const ::flutter::EncodableList& documents, + const ::flutter::EncodableList& document_changes, + const InternalSnapshotMetadata& metadata); + + ~InternalQuerySnapshot() = default; + InternalQuerySnapshot(const InternalQuerySnapshot& other); + InternalQuerySnapshot& operator=(const InternalQuerySnapshot& other); + InternalQuerySnapshot(InternalQuerySnapshot&& other) = default; + InternalQuerySnapshot& operator=(InternalQuerySnapshot&& other) noexcept = + default; + const ::flutter::EncodableList& documents() const; + void set_documents(const ::flutter::EncodableList& value_arg); + + const ::flutter::EncodableList& document_changes() const; + void set_document_changes(const ::flutter::EncodableList& value_arg); + + const InternalSnapshotMetadata& metadata() const; + void set_metadata(const InternalSnapshotMetadata& value_arg); + + bool operator==(const InternalQuerySnapshot& other) const; + bool operator!=(const InternalQuerySnapshot& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalQuerySnapshot FromEncodableList( + const ::flutter::EncodableList& list); - const flutter::EncodableList& documents() const; - void set_documents(const flutter::EncodableList& value_arg); + public: + public: + ::flutter::EncodableList ToEncodableList() const; - const flutter::EncodableList& document_changes() const; - void set_document_changes(const flutter::EncodableList& value_arg); + private: + private: + friend class FirebaseFirestoreHostApi; + friend class FirebaseFirestoreHostApiCodecSerializer; + ::flutter::EncodableList documents_; + ::flutter::EncodableList document_changes_; + std::unique_ptr metadata_; +}; - const PigeonSnapshotMetadata& metadata() const; - void set_metadata(const PigeonSnapshotMetadata& value_arg); +// Generated class from Pigeon that represents data sent in messages. +class InternalPipelineResult { + public: + // Constructs an object setting all non-nullable fields. + InternalPipelineResult(); + + // Constructs an object setting all fields. + explicit InternalPipelineResult(const std::string* document_path, + const int64_t* create_time, + const int64_t* update_time, + const ::flutter::EncodableMap* data); + + const std::string* document_path() const; + void set_document_path(const std::string_view* value_arg); + void set_document_path(std::string_view value_arg); + + const int64_t* create_time() const; + void set_create_time(const int64_t* value_arg); + void set_create_time(int64_t value_arg); + + const int64_t* update_time() const; + void set_update_time(const int64_t* value_arg); + void set_update_time(int64_t value_arg); + + // All fields in the result (from PipelineResult.data() on Android). + const ::flutter::EncodableMap* data() const; + void set_data(const ::flutter::EncodableMap* value_arg); + void set_data(const ::flutter::EncodableMap& value_arg); + + bool operator==(const InternalPipelineResult& other) const; + bool operator!=(const InternalPipelineResult& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalPipelineResult FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; private: - static PigeonQuerySnapshot FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; - flutter::EncodableList documents_; - flutter::EncodableList document_changes_; - PigeonSnapshotMetadata metadata_; + std::optional document_path_; + std::optional create_time_; + std::optional update_time_; + std::optional<::flutter::EncodableMap> data_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonGetOptions { +class InternalPipelineSnapshot { public: // Constructs an object setting all fields. - explicit PigeonGetOptions( + explicit InternalPipelineSnapshot(const ::flutter::EncodableList& results, + int64_t execution_time); + + const ::flutter::EncodableList& results() const; + void set_results(const ::flutter::EncodableList& value_arg); + + int64_t execution_time() const; + void set_execution_time(int64_t value_arg); + + bool operator==(const InternalPipelineSnapshot& other) const; + bool operator!=(const InternalPipelineSnapshot& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalPipelineSnapshot FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: + friend class FirebaseFirestoreHostApi; + friend class FirebaseFirestoreHostApiCodecSerializer; + ::flutter::EncodableList results_; + int64_t execution_time_; +}; + +// Generated class from Pigeon that represents data sent in messages. +class InternalGetOptions { + public: + // Constructs an object setting all fields. + explicit InternalGetOptions( const Source& source, const ServerTimestampBehavior& server_timestamp_behavior); @@ -362,9 +546,22 @@ class PigeonGetOptions { const ServerTimestampBehavior& server_timestamp_behavior() const; void set_server_timestamp_behavior(const ServerTimestampBehavior& value_arg); + bool operator==(const InternalGetOptions& other) const; + bool operator!=(const InternalGetOptions& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalGetOptions FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static PigeonGetOptions FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; Source source_; @@ -372,72 +569,103 @@ class PigeonGetOptions { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonDocumentOption { +class InternalDocumentOption { public: // Constructs an object setting all non-nullable fields. - PigeonDocumentOption(); + InternalDocumentOption(); // Constructs an object setting all fields. - explicit PigeonDocumentOption(const bool* merge, - const flutter::EncodableList* merge_fields); + explicit InternalDocumentOption(const bool* merge, + const ::flutter::EncodableList* merge_fields); const bool* merge() const; void set_merge(const bool* value_arg); void set_merge(bool value_arg); - const flutter::EncodableList* merge_fields() const; - void set_merge_fields(const flutter::EncodableList* value_arg); - void set_merge_fields(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList* merge_fields() const; + void set_merge_fields(const ::flutter::EncodableList* value_arg); + void set_merge_fields(const ::flutter::EncodableList& value_arg); + + bool operator==(const InternalDocumentOption& other) const; + bool operator!=(const InternalDocumentOption& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalDocumentOption FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; private: - static PigeonDocumentOption FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; - friend class PigeonTransactionCommand; + private: + friend class InternalTransactionCommand; friend class DocumentReferenceRequest; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::optional merge_; - std::optional merge_fields_; + std::optional<::flutter::EncodableList> merge_fields_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonTransactionCommand { +class InternalTransactionCommand { public: // Constructs an object setting all non-nullable fields. - explicit PigeonTransactionCommand(const PigeonTransactionType& type, - const std::string& path); + explicit InternalTransactionCommand(const InternalTransactionType& type, + const std::string& path); // Constructs an object setting all fields. - explicit PigeonTransactionCommand(const PigeonTransactionType& type, - const std::string& path, - const flutter::EncodableMap* data, - const PigeonDocumentOption* option); - - const PigeonTransactionType& type() const; - void set_type(const PigeonTransactionType& value_arg); + explicit InternalTransactionCommand(const InternalTransactionType& type, + const std::string& path, + const ::flutter::EncodableMap* data, + const InternalDocumentOption* option); + + ~InternalTransactionCommand() = default; + InternalTransactionCommand(const InternalTransactionCommand& other); + InternalTransactionCommand& operator=( + const InternalTransactionCommand& other); + InternalTransactionCommand(InternalTransactionCommand&& other) = default; + InternalTransactionCommand& operator=( + InternalTransactionCommand&& other) noexcept = default; + const InternalTransactionType& type() const; + void set_type(const InternalTransactionType& value_arg); const std::string& path() const; void set_path(std::string_view value_arg); - const flutter::EncodableMap* data() const; - void set_data(const flutter::EncodableMap* value_arg); - void set_data(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* data() const; + void set_data(const ::flutter::EncodableMap* value_arg); + void set_data(const ::flutter::EncodableMap& value_arg); + + const InternalDocumentOption* option() const; + void set_option(const InternalDocumentOption* value_arg); + void set_option(const InternalDocumentOption& value_arg); - const PigeonDocumentOption* option() const; - void set_option(const PigeonDocumentOption* value_arg); - void set_option(const PigeonDocumentOption& value_arg); + bool operator==(const InternalTransactionCommand& other) const; + bool operator!=(const InternalTransactionCommand& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalTransactionCommand FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; private: - static PigeonTransactionCommand FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; - PigeonTransactionType type_; + InternalTransactionType type_; std::string path_; - std::optional data_; - std::optional option_; + std::optional<::flutter::EncodableMap> data_; + std::unique_ptr option_; }; // Generated class from Pigeon that represents data sent in messages. @@ -448,20 +676,26 @@ class DocumentReferenceRequest { // Constructs an object setting all fields. explicit DocumentReferenceRequest( - const std::string& path, const flutter::EncodableMap* data, - const PigeonDocumentOption* option, const Source* source, + const std::string& path, const ::flutter::EncodableMap* data, + const InternalDocumentOption* option, const Source* source, const ServerTimestampBehavior* server_timestamp_behavior); + ~DocumentReferenceRequest() = default; + DocumentReferenceRequest(const DocumentReferenceRequest& other); + DocumentReferenceRequest& operator=(const DocumentReferenceRequest& other); + DocumentReferenceRequest(DocumentReferenceRequest&& other) = default; + DocumentReferenceRequest& operator=( + DocumentReferenceRequest&& other) noexcept = default; const std::string& path() const; void set_path(std::string_view value_arg); - const flutter::EncodableMap* data() const; - void set_data(const flutter::EncodableMap* value_arg); - void set_data(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* data() const; + void set_data(const ::flutter::EncodableMap* value_arg); + void set_data(const ::flutter::EncodableMap& value_arg); - const PigeonDocumentOption* option() const; - void set_option(const PigeonDocumentOption* value_arg); - void set_option(const PigeonDocumentOption& value_arg); + const InternalDocumentOption* option() const; + void set_option(const InternalDocumentOption* value_arg); + void set_option(const InternalDocumentOption& value_arg); const Source* source() const; void set_source(const Source* value_arg); @@ -471,43 +705,55 @@ class DocumentReferenceRequest { void set_server_timestamp_behavior(const ServerTimestampBehavior* value_arg); void set_server_timestamp_behavior(const ServerTimestampBehavior& value_arg); + bool operator==(const DocumentReferenceRequest& other) const; + bool operator!=(const DocumentReferenceRequest& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static DocumentReferenceRequest FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; std::string path_; - std::optional data_; - std::optional option_; + std::optional<::flutter::EncodableMap> data_; + std::unique_ptr option_; std::optional source_; std::optional server_timestamp_behavior_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonQueryParameters { +class InternalQueryParameters { public: // Constructs an object setting all non-nullable fields. - PigeonQueryParameters(); + InternalQueryParameters(); // Constructs an object setting all fields. - explicit PigeonQueryParameters(const flutter::EncodableList* where, - const flutter::EncodableList* order_by, - const int64_t* limit, - const int64_t* limit_to_last, - const flutter::EncodableList* start_at, - const flutter::EncodableList* start_after, - const flutter::EncodableList* end_at, - const flutter::EncodableList* end_before, - const flutter::EncodableMap* filters); - - const flutter::EncodableList* where() const; - void set_where(const flutter::EncodableList* value_arg); - void set_where(const flutter::EncodableList& value_arg); - - const flutter::EncodableList* order_by() const; - void set_order_by(const flutter::EncodableList* value_arg); - void set_order_by(const flutter::EncodableList& value_arg); + explicit InternalQueryParameters(const ::flutter::EncodableList* where, + const ::flutter::EncodableList* order_by, + const int64_t* limit, + const int64_t* limit_to_last, + const ::flutter::EncodableList* start_at, + const ::flutter::EncodableList* start_after, + const ::flutter::EncodableList* end_at, + const ::flutter::EncodableList* end_before, + const ::flutter::EncodableMap* filters); + + const ::flutter::EncodableList* where() const; + void set_where(const ::flutter::EncodableList* value_arg); + void set_where(const ::flutter::EncodableList& value_arg); + + const ::flutter::EncodableList* order_by() const; + void set_order_by(const ::flutter::EncodableList* value_arg); + void set_order_by(const ::flutter::EncodableList& value_arg); const int64_t* limit() const; void set_limit(const int64_t* value_arg); @@ -517,41 +763,53 @@ class PigeonQueryParameters { void set_limit_to_last(const int64_t* value_arg); void set_limit_to_last(int64_t value_arg); - const flutter::EncodableList* start_at() const; - void set_start_at(const flutter::EncodableList* value_arg); - void set_start_at(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList* start_at() const; + void set_start_at(const ::flutter::EncodableList* value_arg); + void set_start_at(const ::flutter::EncodableList& value_arg); - const flutter::EncodableList* start_after() const; - void set_start_after(const flutter::EncodableList* value_arg); - void set_start_after(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList* start_after() const; + void set_start_after(const ::flutter::EncodableList* value_arg); + void set_start_after(const ::flutter::EncodableList& value_arg); - const flutter::EncodableList* end_at() const; - void set_end_at(const flutter::EncodableList* value_arg); - void set_end_at(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList* end_at() const; + void set_end_at(const ::flutter::EncodableList* value_arg); + void set_end_at(const ::flutter::EncodableList& value_arg); - const flutter::EncodableList* end_before() const; - void set_end_before(const flutter::EncodableList* value_arg); - void set_end_before(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList* end_before() const; + void set_end_before(const ::flutter::EncodableList* value_arg); + void set_end_before(const ::flutter::EncodableList& value_arg); - const flutter::EncodableMap* filters() const; - void set_filters(const flutter::EncodableMap* value_arg); - void set_filters(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* filters() const; + void set_filters(const ::flutter::EncodableMap* value_arg); + void set_filters(const ::flutter::EncodableMap& value_arg); + bool operator==(const InternalQueryParameters& other) const; + bool operator!=(const InternalQueryParameters& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalQueryParameters FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static PigeonQueryParameters FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; - std::optional where_; - std::optional order_by_; + std::optional<::flutter::EncodableList> where_; + std::optional<::flutter::EncodableList> order_by_; std::optional limit_; std::optional limit_to_last_; - std::optional start_at_; - std::optional start_after_; - std::optional end_at_; - std::optional end_before_; - std::optional filters_; + std::optional<::flutter::EncodableList> start_at_; + std::optional<::flutter::EncodableList> start_after_; + std::optional<::flutter::EncodableList> end_at_; + std::optional<::flutter::EncodableList> end_before_; + std::optional<::flutter::EncodableMap> filters_; }; // Generated class from Pigeon that represents data sent in messages. @@ -570,9 +828,21 @@ class AggregateQuery { void set_field(const std::string_view* value_arg); void set_field(std::string_view value_arg); + bool operator==(const AggregateQuery& other) const; + bool operator!=(const AggregateQuery& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static AggregateQuery FromEncodableList(const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static AggregateQuery FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; AggregateType type_; @@ -601,10 +871,22 @@ class AggregateQueryResponse { void set_value(const double* value_arg); void set_value(double value_arg); + bool operator==(const AggregateQueryResponse& other) const; + bool operator!=(const AggregateQueryResponse& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static AggregateQueryResponse FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseFirestoreHostApi; friend class FirebaseFirestoreHostApiCodecSerializer; AggregateType type_; @@ -621,12 +903,12 @@ class FirebaseFirestoreHostApiCodecSerializer return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -641,8 +923,8 @@ class FirebaseFirestoreHostApi { std::function reply)> result) = 0; virtual void NamedQueryGet( const FirestorePigeonFirebaseApp& app, const std::string& name, - const PigeonGetOptions& options, - std::function reply)> result) = 0; + const InternalGetOptions& options, + std::function reply)> result) = 0; virtual void ClearPersistence( const FirestorePigeonFirebaseApp& app, std::function reply)> result) = 0; @@ -674,13 +956,13 @@ class FirebaseFirestoreHostApi { std::function reply)> result) = 0; virtual void TransactionStoreResult( const std::string& transaction_id, - const PigeonTransactionResult& result_type, - const flutter::EncodableList* commands, + const InternalTransactionResult& result_type, + const ::flutter::EncodableList* commands, std::function reply)> result) = 0; virtual void TransactionGet( const FirestorePigeonFirebaseApp& app, const std::string& transaction_id, const std::string& path, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void DocumentReferenceSet( const FirestorePigeonFirebaseApp& app, const DocumentReferenceRequest& request, @@ -692,29 +974,29 @@ class FirebaseFirestoreHostApi { virtual void DocumentReferenceGet( const FirestorePigeonFirebaseApp& app, const DocumentReferenceRequest& request, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void DocumentReferenceDelete( const FirestorePigeonFirebaseApp& app, const DocumentReferenceRequest& request, std::function reply)> result) = 0; virtual void QueryGet( const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, - std::function reply)> result) = 0; + bool is_collection_group, const InternalQueryParameters& parameters, + const InternalGetOptions& options, + std::function reply)> result) = 0; virtual void AggregateQuery( const FirestorePigeonFirebaseApp& app, const std::string& path, - const PigeonQueryParameters& parameters, const AggregateSource& source, - const flutter::EncodableList& queries, bool is_collection_group, - std::function reply)> result) = 0; + const InternalQueryParameters& parameters, const AggregateSource& source, + const ::flutter::EncodableList& queries, bool is_collection_group, + std::function reply)> result) = 0; virtual void WriteBatchCommit( const FirestorePigeonFirebaseApp& app, - const flutter::EncodableList& writes, + const ::flutter::EncodableList& writes, std::function reply)> result) = 0; virtual void QuerySnapshot( const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, bool include_metadata_changes, + bool is_collection_group, const InternalQueryParameters& parameters, + const InternalGetOptions& options, bool include_metadata_changes, const ListenSource& source, std::function reply)> result) = 0; virtual void DocumentReferenceSnapshot( @@ -726,15 +1008,23 @@ class FirebaseFirestoreHostApi { const FirestorePigeonFirebaseApp& app, const PersistenceCacheIndexManagerRequestEnum& request, std::function reply)> result) = 0; + virtual void ExecutePipeline( + const FirestorePigeonFirebaseApp& app, + const ::flutter::EncodableList& stages, + const ::flutter::EncodableMap* options, + std::function reply)> result) = 0; // The codec used by FirebaseFirestoreHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseFirestoreHostApi` to handle messages // through the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseFirestoreHostApi* api); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static void SetUp(::flutter::BinaryMessenger* binary_messenger, + FirebaseFirestoreHostApi* api, + const std::string& message_channel_suffix); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseFirestoreHostApi() = default; diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/CHANGELOG.md b/packages/cloud_firestore/cloud_firestore_platform_interface/CHANGELOG.md index 2ea9e08a6f60..04eb8b9e92c8 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/CHANGELOG.md +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/CHANGELOG.md @@ -1,3 +1,26 @@ +## 8.0.3 + + - Update a dependency to the latest release. + +## 8.0.2 + + - Update a dependency to the latest release. + +## 8.0.1 + + - Update a dependency to the latest release. + +## 8.0.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 7.2.0 + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + ## 7.1.0 - **REFACTOR**: fix formatting and analysis issues across the repo ([#18124](https://github.com/firebase/flutterfire/issues/18124)). ([ab79fd93](https://github.com/firebase/flutterfire/commit/ab79fd93ee4ccfeb478687623134b1cf8ab71c74)) diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart index 9f9b8e7866e0..2e7e97bec179 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart @@ -29,6 +29,8 @@ export 'src/platform_interface/platform_interface_index_definitions.dart'; export 'src/platform_interface/platform_interface_load_bundle_task.dart'; export 'src/platform_interface/platform_interface_load_bundle_task_snapshot.dart'; export 'src/platform_interface/platform_interface_persistent_cache_index_manager.dart'; +export 'src/platform_interface/platform_interface_pipeline.dart'; +export 'src/platform_interface/platform_interface_pipeline_snapshot.dart'; export 'src/platform_interface/platform_interface_query.dart'; export 'src/platform_interface/platform_interface_query_snapshot.dart'; export 'src/platform_interface/platform_interface_transaction.dart'; diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_aggregate_query.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_aggregate_query.dart index ad54b1f6c018..ac3193875c3d 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_aggregate_query.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_aggregate_query.dart @@ -18,7 +18,7 @@ class MethodChannelAggregateQuery extends AggregateQueryPlatform { final FirestorePigeonFirebaseApp _pigeonApp; final String _path; - final PigeonQueryParameters _pigeonParameters; + final InternalQueryParameters _pigeonParameters; final bool _isCollectionGroupQuery; final List _aggregateQueries; diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_change.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_change.dart index 3d03a9d3edfa..0469941a7a35 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_change.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_change.dart @@ -9,8 +9,8 @@ import 'package:cloud_firestore_platform_interface/cloud_firestore_platform_inte /// communicate with Firebase plugins. class MethodChannelDocumentChange extends DocumentChangePlatform { /// Creates a [MethodChannelDocumentChange] from the given [data] - MethodChannelDocumentChange( - FirebaseFirestorePlatform firestore, PigeonDocumentChange documentChange) + MethodChannelDocumentChange(FirebaseFirestorePlatform firestore, + InternalDocumentChange documentChange) : super( documentChange.type, documentChange.oldIndex, diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_reference.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_reference.dart index 4927de5c72e6..e957d4c63fbb 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_reference.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_document_reference.dart @@ -37,7 +37,7 @@ class MethodChannelDocumentReference extends DocumentReferencePlatform { DocumentReferenceRequest( path: _pointer.path, data: data, - option: PigeonDocumentOption( + option: InternalDocumentOption( merge: options?.merge, mergeFields: options?.mergeFields?.map((e) => e.components).toList(), @@ -137,8 +137,10 @@ class MethodChannelDocumentReference extends DocumentReferencePlatform { ) .listen( (snapshot) { - final PigeonDocumentSnapshot result = - PigeonDocumentSnapshot.decode(snapshot); + // With Pigeon 26, the native side emits the generated Pigeon class + // directly through the Pigeon-aware codec, so we receive a fully + // decoded `InternalDocumentSnapshot` here (no manual decode required). + final result = snapshot as InternalDocumentSnapshot; controller.add( DocumentSnapshotPlatform( firestore, diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_firestore.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_firestore.dart index 573e5a3c91b9..33aefcfb42c0 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_firestore.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_firestore.dart @@ -15,11 +15,12 @@ import 'package:flutter/services.dart'; import 'method_channel_collection_reference.dart'; import 'method_channel_document_reference.dart'; +import 'method_channel_pipeline.dart'; +import 'method_channel_pipeline_snapshot.dart'; import 'method_channel_query.dart'; import 'method_channel_transaction.dart'; import 'method_channel_write_batch.dart'; import 'utils/exception.dart'; -import 'utils/firestore_message_codec.dart'; /// The entry point for accessing a Firestore. /// @@ -37,7 +38,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { static EventChannel querySnapshotChannel(String id) { return EventChannel( 'plugins.flutter.io/firebase_firestore/query/$id', - const StandardMethodCodec(FirestoreMessageCodec()), + const StandardMethodCodec(PigeonCodec()), ); } @@ -45,7 +46,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { static EventChannel documentSnapshotChannel(String id) { return EventChannel( 'plugins.flutter.io/firebase_firestore/document/$id', - const StandardMethodCodec(FirestoreMessageCodec()), + const StandardMethodCodec(PigeonCodec()), ); } @@ -53,7 +54,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { static EventChannel snapshotsInSyncChannel(String id) { return EventChannel( 'plugins.flutter.io/firebase_firestore/snapshotsInSync/$id', - const StandardMethodCodec(FirestoreMessageCodec()), + const StandardMethodCodec(PigeonCodec()), ); } @@ -61,7 +62,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { static EventChannel loadBundleChannel(String id) { return EventChannel( 'plugins.flutter.io/firebase_firestore/loadBundle/$id', - const StandardMethodCodec(FirestoreMessageCodec()), + const StandardMethodCodec(PigeonCodec()), ); } @@ -70,7 +71,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { late final FirestorePigeonFirebaseApp pigeonApp = FirestorePigeonFirebaseApp( appName: appInstance!.name, databaseURL: databaseId, - settings: PigeonFirebaseSettings( + settings: InternalFirebaseSettings( persistenceEnabled: settings.persistenceEnabled, host: settings.host, sslEnabled: settings.sslEnabled, @@ -105,7 +106,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { final data = await pigeonChannel.namedQueryGet( pigeonApp, name, - PigeonGetOptions( + InternalGetOptions( source: options.source, serverTimestampBehavior: options.serverTimestampBehavior, ), @@ -231,7 +232,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { final eventChannel = EventChannel( 'plugins.flutter.io/firebase_firestore/transaction/$transactionId', - const StandardMethodCodec(FirestoreMessageCodec()), + const StandardMethodCodec(PigeonCodec()), ); final snapshotStreamSubscription = @@ -274,7 +275,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { // transaction await pigeonChannel.transactionStoreResult( transactionId, - PigeonTransactionResult.failure, + InternalTransactionResult.failure, null, ); @@ -288,7 +289,7 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { // Send the transaction commands to Dart. await pigeonChannel.transactionStoreResult( transactionId, - PigeonTransactionResult.success, + InternalTransactionResult.success, transaction.commands, ); }, @@ -350,4 +351,38 @@ class MethodChannelFirebaseFirestore extends FirebaseFirestorePlatform { convertPlatformException(e, stack); } } + + @override + PipelinePlatform pipeline(List> initialStages) { + return MethodChannelPipeline(this, pigeonApp, stages: initialStages); + } + + @override + Future executePipeline( + List> stages, { + Map? options, + }) async { + try { + // Convert stages to Pigeon format (List?>) + final List?> pigeonStages = stages.map((stage) { + return stage.map(MapEntry.new); + }).toList(); + + // Convert options to Pigeon format (Map?) + final Map? pigeonOptions = options?.map( + MapEntry.new, + ); + + final InternalPipelineSnapshot result = + await pigeonChannel.executePipeline( + pigeonApp, + pigeonStages, + pigeonOptions, + ); + + return MethodChannelPipelineSnapshot(this, pigeonApp, result); + } catch (e, stack) { + convertPlatformException(e, stack); + } + } } diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_pipeline.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_pipeline.dart new file mode 100644 index 000000000000..7e0b65e43c7d --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_pipeline.dart @@ -0,0 +1,51 @@ +// ignore_for_file: require_trailing_commas, unnecessary_lambdas +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; +import 'package:cloud_firestore_platform_interface/src/platform_interface/platform_interface_pipeline.dart' + as pipeline; + +/// An implementation of [PipelinePlatform] that uses [MethodChannel] to +/// communicate with Firebase plugins. +class MethodChannelPipeline extends pipeline.PipelinePlatform { + /// Create a [MethodChannelPipeline] from [stages] + MethodChannelPipeline( + FirebaseFirestorePlatform _firestore, + this.pigeonApp, { + List>? stages, + }) : super(_firestore, stages); + + final FirestorePigeonFirebaseApp pigeonApp; + + /// Creates a new instance of [MethodChannelPipeline], however overrides + /// any existing [stages]. + /// + /// This is in place to ensure that changes to a pipeline don't mutate + /// other pipelines. + MethodChannelPipeline _copyWithStages(List> newStages) { + return MethodChannelPipeline( + firestore, + pigeonApp, + stages: List.unmodifiable([ + ...stages, + ...newStages, + ]), + ); + } + + @override + pipeline.PipelinePlatform addStage(Map serializedStage) { + return _copyWithStages([serializedStage]); + } + + @override + Future execute({ + Map? options, + }) async { + return firestore.executePipeline(stages, options: options); + } +} diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_pipeline_snapshot.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_pipeline_snapshot.dart new file mode 100644 index 000000000000..a0b3a73d5bb3 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_pipeline_snapshot.dart @@ -0,0 +1,83 @@ +// ignore_for_file: require_trailing_commas +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; +import 'package:cloud_firestore_platform_interface/src/method_channel/method_channel_document_reference.dart'; + +/// An implementation of [PipelineSnapshotPlatform] that uses [MethodChannel] to +/// communicate with Firebase plugins. +class MethodChannelPipelineSnapshot extends PipelineSnapshotPlatform { + final List _results; + final DateTime _executionTime; + + /// Creates a [MethodChannelPipelineSnapshot] from the given [pigeonSnapshot] + MethodChannelPipelineSnapshot( + FirebaseFirestorePlatform firestore, + FirestorePigeonFirebaseApp pigeonApp, + InternalPipelineSnapshot pigeonSnapshot, + ) : _results = pigeonSnapshot.results + .whereType() + .map((result) => MethodChannelPipelineResult( + firestore, + pigeonApp, + result.documentPath, + result.createTime != null + ? DateTime.fromMillisecondsSinceEpoch(result.createTime!) + : null, + result.updateTime != null + ? DateTime.fromMillisecondsSinceEpoch(result.updateTime!) + : null, + result.data?.cast(), + )) + .toList(), + _executionTime = DateTime.fromMillisecondsSinceEpoch( + pigeonSnapshot.executionTime, + ), + super(); + + @override + List get results => _results; + + @override + DateTime get executionTime => _executionTime; +} + +/// An implementation of [PipelineResultPlatform] that uses [MethodChannel] to +/// communicate with Firebase plugins. +class MethodChannelPipelineResult extends PipelineResultPlatform { + final DocumentReferencePlatform? _document; + final DateTime? _createTime; + final DateTime? _updateTime; + final Map? _data; + + MethodChannelPipelineResult( + FirebaseFirestorePlatform firestore, + FirestorePigeonFirebaseApp pigeonApp, + String? documentPath, + this._createTime, + this._updateTime, + Map? data, + ) : _document = (documentPath != null && documentPath.isNotEmpty) + ? MethodChannelDocumentReference( + firestore, + documentPath, + pigeonApp, + ) + : null, + _data = data, + super(); + + @override + DocumentReferencePlatform? get document => _document; + + @override + DateTime? get createTime => _createTime; + + @override + DateTime? get updateTime => _updateTime; + + @override + Map? get data => _data; +} diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart index 6ae2ec548688..12604b472d7c 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query.dart @@ -43,8 +43,8 @@ class MethodChannelQuery extends QueryPlatform { return _pointer.path; } - PigeonQueryParameters get _pigeonParameters { - return PigeonQueryParameters( + InternalQueryParameters get _pigeonParameters { + return InternalQueryParameters( where: parameters['where'], orderBy: parameters['orderBy'], limit: parameters['limit'], @@ -114,13 +114,13 @@ class MethodChannelQuery extends QueryPlatform { Future get( [GetOptions options = const GetOptions()]) async { try { - final PigeonQuerySnapshot result = + final InternalQuerySnapshot result = await MethodChannelFirebaseFirestore.pigeonChannel.queryGet( pigeonApp, _pointer.path, isCollectionGroupQuery, _pigeonParameters, - PigeonGetOptions( + InternalGetOptions( source: options.source, serverTimestampBehavior: options.serverTimestampBehavior, ), @@ -170,7 +170,7 @@ class MethodChannelQuery extends QueryPlatform { _pointer.path, isCollectionGroupQuery, _pigeonParameters, - PigeonGetOptions( + InternalGetOptions( source: Source.serverAndCache, serverTimestampBehavior: serverTimestampBehavior, ), @@ -185,21 +185,10 @@ class MethodChannelQuery extends QueryPlatform { ) .listen( (snapshot) { - final snapshotList = snapshot as List; - // We force the types here of list because they are not automatically - // decoded by the pigeon generated code. - final List documents = - (snapshotList[0]! as List) - .map((e) => PigeonDocumentSnapshot.decode(e)) - .toList() - .cast(); - final List changes = - (snapshotList[1]! as List) - .map((e) => PigeonDocumentChange.decode(e)) - .toList() - .cast(); - final PigeonQuerySnapshot result = PigeonQuerySnapshot.decode( - [documents, changes, snapshotList[2]]); + // With Pigeon 26, the native side emits the generated Pigeon class + // directly through the Pigeon-aware codec, so we receive a fully + // decoded `InternalQuerySnapshot` here (no manual decode required). + final result = snapshot as InternalQuerySnapshot; controller.add(MethodChannelQuerySnapshot(firestore, result)); }, onError: controller.addError, diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query_snapshot.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query_snapshot.dart index 204915caa160..72c61bd9e0db 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query_snapshot.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_query_snapshot.dart @@ -12,7 +12,7 @@ import 'method_channel_document_change.dart'; class MethodChannelQuerySnapshot extends QuerySnapshotPlatform { /// Creates a [MethodChannelQuerySnapshot] from the given [data] MethodChannelQuerySnapshot( - FirebaseFirestorePlatform firestore, PigeonQuerySnapshot data) + FirebaseFirestorePlatform firestore, InternalQuerySnapshot data) : super( data.documents .map((document) { diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_transaction.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_transaction.dart index 19b85bb93474..a4e75ad3d377 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_transaction.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_transaction.dart @@ -31,11 +31,11 @@ class MethodChannelTransaction extends TransactionPlatform { app: Firebase.app(appName), databaseId: databaseId); } - List _commands = []; + List _commands = []; /// Returns all transaction commands for the current instance. @override - List get commands { + List get commands { return _commands; } @@ -63,8 +63,8 @@ class MethodChannelTransaction extends TransactionPlatform { @override MethodChannelTransaction delete(String documentPath) { - _commands.add(PigeonTransactionCommand( - type: PigeonTransactionType.deleteType, + _commands.add(InternalTransactionCommand( + type: InternalTransactionType.deleteType, path: documentPath, )); @@ -76,8 +76,8 @@ class MethodChannelTransaction extends TransactionPlatform { String documentPath, Map data, ) { - _commands.add(PigeonTransactionCommand( - type: PigeonTransactionType.update, + _commands.add(InternalTransactionCommand( + type: InternalTransactionType.update, path: documentPath, data: data, )); @@ -88,11 +88,11 @@ class MethodChannelTransaction extends TransactionPlatform { @override MethodChannelTransaction set(String documentPath, Map data, [SetOptions? options]) { - _commands.add(PigeonTransactionCommand( - type: PigeonTransactionType.set, + _commands.add(InternalTransactionCommand( + type: InternalTransactionType.set, path: documentPath, data: data, - option: PigeonDocumentOption( + option: InternalDocumentOption( merge: options?.merge, mergeFields: options?.mergeFields?.map((e) => e.components).toList(), ))); diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_write_batch.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_write_batch.dart index bbdcbf3e678c..efd1de89fe6e 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_write_batch.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/method_channel_write_batch.dart @@ -24,7 +24,7 @@ class MethodChannelWriteBatch extends WriteBatchPlatform { final FirestorePigeonFirebaseApp pigeonApp; /// Keeps track of all batch writes in order. - List _writes = []; + List _writes = []; /// The committed state of this batch. /// @@ -52,9 +52,9 @@ class MethodChannelWriteBatch extends WriteBatchPlatform { @override void delete(String documentPath) { _assertNotCommitted(); - _writes.add(PigeonTransactionCommand( + _writes.add(InternalTransactionCommand( path: documentPath, - type: PigeonTransactionType.deleteType, + type: InternalTransactionType.deleteType, )); } @@ -62,11 +62,11 @@ class MethodChannelWriteBatch extends WriteBatchPlatform { void set(String documentPath, Map data, [SetOptions? options]) { _assertNotCommitted(); - _writes.add(PigeonTransactionCommand( + _writes.add(InternalTransactionCommand( path: documentPath, - type: PigeonTransactionType.set, + type: InternalTransactionType.set, data: data, - option: PigeonDocumentOption( + option: InternalDocumentOption( merge: options?.merge, mergeFields: options?.mergeFields?.map((e) => e.components).toList(), ), @@ -79,9 +79,9 @@ class MethodChannelWriteBatch extends WriteBatchPlatform { Map data, ) { _assertNotCommitted(); - _writes.add(PigeonTransactionCommand( + _writes.add(InternalTransactionCommand( path: documentPath, - type: PigeonTransactionType.update, + type: InternalTransactionType.update, data: data, )); } diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart index 2420b2cfc4b4..352fbf28fbed 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,16 +1,115 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.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: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; import 'package:cloud_firestore_platform_interface/src/method_channel/utils/firestore_message_codec.dart'; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; +} + +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + return a == b; +} + +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} /// An enumeration of document change types. enum DocumentChangeType { @@ -86,12 +185,12 @@ enum PersistenceCacheIndexManagerRequest { deleteAllIndexes, } -enum PigeonTransactionResult { +enum InternalTransactionResult { success, failure, } -enum PigeonTransactionType { +enum InternalTransactionType { get, update, set, @@ -104,8 +203,8 @@ enum AggregateType { average, } -class PigeonFirebaseSettings { - PigeonFirebaseSettings({ +class InternalFirebaseSettings { + InternalFirebaseSettings({ this.persistenceEnabled, this.host, this.sslEnabled, @@ -123,7 +222,7 @@ class PigeonFirebaseSettings { bool ignoreUndefinedProperties; - Object encode() { + List _toList() { return [ persistenceEnabled, host, @@ -133,9 +232,13 @@ class PigeonFirebaseSettings { ]; } - static PigeonFirebaseSettings decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalFirebaseSettings decode(Object result) { result as List; - return PigeonFirebaseSettings( + return InternalFirebaseSettings( persistenceEnabled: result[0] as bool?, host: result[1] as String?, sslEnabled: result[2] as bool?, @@ -143,6 +246,27 @@ class PigeonFirebaseSettings { ignoreUndefinedProperties: result[4]! as bool, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalFirebaseSettings || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(persistenceEnabled, other.persistenceEnabled) && + _deepEquals(host, other.host) && + _deepEquals(sslEnabled, other.sslEnabled) && + _deepEquals(cacheSizeBytes, other.cacheSizeBytes) && + _deepEquals(ignoreUndefinedProperties, other.ignoreUndefinedProperties); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class FirestorePigeonFirebaseApp { @@ -154,30 +278,53 @@ class FirestorePigeonFirebaseApp { String appName; - PigeonFirebaseSettings settings; + InternalFirebaseSettings settings; String databaseURL; - Object encode() { + List _toList() { return [ appName, - settings.encode(), + settings, databaseURL, ]; } + Object encode() { + return _toList(); + } + static FirestorePigeonFirebaseApp decode(Object result) { result as List; return FirestorePigeonFirebaseApp( appName: result[0]! as String, - settings: PigeonFirebaseSettings.decode(result[1]! as List), + settings: result[1]! as InternalFirebaseSettings, databaseURL: result[2]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! FirestorePigeonFirebaseApp || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(appName, other.appName) && + _deepEquals(settings, other.settings) && + _deepEquals(databaseURL, other.databaseURL); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonSnapshotMetadata { - PigeonSnapshotMetadata({ +class InternalSnapshotMetadata { + InternalSnapshotMetadata({ required this.hasPendingWrites, required this.isFromCache, }); @@ -186,24 +333,46 @@ class PigeonSnapshotMetadata { bool isFromCache; - Object encode() { + List _toList() { return [ hasPendingWrites, isFromCache, ]; } - static PigeonSnapshotMetadata decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalSnapshotMetadata decode(Object result) { result as List; - return PigeonSnapshotMetadata( + return InternalSnapshotMetadata( hasPendingWrites: result[0]! as bool, isFromCache: result[1]! as bool, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalSnapshotMetadata || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(hasPendingWrites, other.hasPendingWrites) && + _deepEquals(isFromCache, other.isFromCache); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonDocumentSnapshot { - PigeonDocumentSnapshot({ +class InternalDocumentSnapshot { + InternalDocumentSnapshot({ required this.path, this.data, required this.metadata, @@ -213,28 +382,51 @@ class PigeonDocumentSnapshot { Map? data; - PigeonSnapshotMetadata metadata; + InternalSnapshotMetadata metadata; - Object encode() { + List _toList() { return [ path, data, - metadata.encode(), + metadata, ]; } - static PigeonDocumentSnapshot decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalDocumentSnapshot decode(Object result) { result as List; - return PigeonDocumentSnapshot( + return InternalDocumentSnapshot( path: result[0]! as String, data: (result[1] as Map?)?.cast(), - metadata: PigeonSnapshotMetadata.decode(result[2]! as List), + metadata: result[2]! as InternalSnapshotMetadata, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalDocumentSnapshot || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(path, other.path) && + _deepEquals(data, other.data) && + _deepEquals(metadata, other.metadata); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonDocumentChange { - PigeonDocumentChange({ +class InternalDocumentChange { + InternalDocumentChange({ required this.type, required this.document, required this.oldIndex, @@ -243,66 +435,220 @@ class PigeonDocumentChange { DocumentChangeType type; - PigeonDocumentSnapshot document; + InternalDocumentSnapshot document; int oldIndex; int newIndex; - Object encode() { + List _toList() { return [ - type.index, - document.encode(), + type, + document, oldIndex, newIndex, ]; } - static PigeonDocumentChange decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalDocumentChange decode(Object result) { result as List; - return PigeonDocumentChange( - type: DocumentChangeType.values[result[0]! as int], - document: PigeonDocumentSnapshot.decode(result[1]! as List), + return InternalDocumentChange( + type: result[0]! as DocumentChangeType, + document: result[1]! as InternalDocumentSnapshot, oldIndex: result[2]! as int, newIndex: result[3]! as int, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalDocumentChange || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(type, other.type) && + _deepEquals(document, other.document) && + _deepEquals(oldIndex, other.oldIndex) && + _deepEquals(newIndex, other.newIndex); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonQuerySnapshot { - PigeonQuerySnapshot({ +class InternalQuerySnapshot { + InternalQuerySnapshot({ required this.documents, required this.documentChanges, required this.metadata, }); - List documents; + List documents; - List documentChanges; + List documentChanges; - PigeonSnapshotMetadata metadata; + InternalSnapshotMetadata metadata; - Object encode() { + List _toList() { return [ documents, documentChanges, - metadata.encode(), + metadata, ]; } - static PigeonQuerySnapshot decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalQuerySnapshot decode(Object result) { result as List; - return PigeonQuerySnapshot( - documents: (result[0] as List?)!.cast(), + return InternalQuerySnapshot( + documents: + (result[0]! as List).cast(), documentChanges: - (result[1] as List?)!.cast(), - metadata: PigeonSnapshotMetadata.decode(result[2]! as List), + (result[1]! as List).cast(), + metadata: result[2]! as InternalSnapshotMetadata, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalQuerySnapshot || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(documents, other.documents) && + _deepEquals(documentChanges, other.documentChanges) && + _deepEquals(metadata, other.metadata); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); +} + +class InternalPipelineResult { + InternalPipelineResult({ + this.documentPath, + this.createTime, + this.updateTime, + this.data, + }); + + String? documentPath; + + int? createTime; + + int? updateTime; + + /// All fields in the result (from PipelineResult.data() on Android). + Map? data; + + List _toList() { + return [ + documentPath, + createTime, + updateTime, + data, + ]; + } + + Object encode() { + return _toList(); + } + + static InternalPipelineResult decode(Object result) { + result as List; + return InternalPipelineResult( + documentPath: result[0] as String?, + createTime: result[1] as int?, + updateTime: result[2] as int?, + data: (result[3] as Map?)?.cast(), + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalPipelineResult || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(documentPath, other.documentPath) && + _deepEquals(createTime, other.createTime) && + _deepEquals(updateTime, other.updateTime) && + _deepEquals(data, other.data); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); +} + +class InternalPipelineSnapshot { + InternalPipelineSnapshot({ + required this.results, + required this.executionTime, + }); + + List results; + + int executionTime; + + List _toList() { + return [ + results, + executionTime, + ]; + } + + Object encode() { + return _toList(); + } + + static InternalPipelineSnapshot decode(Object result) { + result as List; + return InternalPipelineSnapshot( + results: (result[0]! as List).cast(), + executionTime: result[1]! as int, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalPipelineSnapshot || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(results, other.results) && + _deepEquals(executionTime, other.executionTime); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonGetOptions { - PigeonGetOptions({ +class InternalGetOptions { + InternalGetOptions({ required this.source, required this.serverTimestampBehavior, }); @@ -311,25 +657,45 @@ class PigeonGetOptions { ServerTimestampBehavior serverTimestampBehavior; - Object encode() { + List _toList() { return [ - source.index, - serverTimestampBehavior.index, + source, + serverTimestampBehavior, ]; } - static PigeonGetOptions decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalGetOptions decode(Object result) { result as List; - return PigeonGetOptions( - source: Source.values[result[0]! as int], - serverTimestampBehavior: - ServerTimestampBehavior.values[result[1]! as int], + return InternalGetOptions( + source: result[0]! as Source, + serverTimestampBehavior: result[1]! as ServerTimestampBehavior, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalGetOptions || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(source, other.source) && + _deepEquals(serverTimestampBehavior, other.serverTimestampBehavior); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonDocumentOption { - PigeonDocumentOption({ +class InternalDocumentOption { + InternalDocumentOption({ this.merge, this.mergeFields, }); @@ -338,58 +704,101 @@ class PigeonDocumentOption { List?>? mergeFields; - Object encode() { + List _toList() { return [ merge, mergeFields, ]; } - static PigeonDocumentOption decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalDocumentOption decode(Object result) { result as List; - return PigeonDocumentOption( + return InternalDocumentOption( merge: result[0] as bool?, mergeFields: (result[1] as List?)?.cast?>(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalDocumentOption || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(merge, other.merge) && + _deepEquals(mergeFields, other.mergeFields); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonTransactionCommand { - PigeonTransactionCommand({ +class InternalTransactionCommand { + InternalTransactionCommand({ required this.type, required this.path, this.data, this.option, }); - PigeonTransactionType type; + InternalTransactionType type; String path; Map? data; - PigeonDocumentOption? option; + InternalDocumentOption? option; - Object encode() { + List _toList() { return [ - type.index, + type, path, data, - option?.encode(), + option, ]; } - static PigeonTransactionCommand decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalTransactionCommand decode(Object result) { result as List; - return PigeonTransactionCommand( - type: PigeonTransactionType.values[result[0]! as int], + return InternalTransactionCommand( + type: result[0]! as InternalTransactionType, path: result[1]! as String, - data: (result[2] as Map?)?.cast(), - option: result[3] != null - ? PigeonDocumentOption.decode(result[3]! as List) - : null, + data: result[2] as Map?, + option: result[3] as InternalDocumentOption?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalTransactionCommand || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(type, other.type) && + _deepEquals(path, other.path) && + _deepEquals(data, other.data) && + _deepEquals(option, other.option); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class DocumentReferenceRequest { @@ -405,40 +814,61 @@ class DocumentReferenceRequest { Map? data; - PigeonDocumentOption? option; + InternalDocumentOption? option; Source? source; ServerTimestampBehavior? serverTimestampBehavior; - Object encode() { + List _toList() { return [ path, data, - option?.encode(), - source?.index, - serverTimestampBehavior?.index, + option, + source, + serverTimestampBehavior, ]; } + Object encode() { + return _toList(); + } + static DocumentReferenceRequest decode(Object result) { result as List; return DocumentReferenceRequest( path: result[0]! as String, - data: (result[1] as Map?)?.cast(), - option: result[2] != null - ? PigeonDocumentOption.decode(result[2]! as List) - : null, - source: result[3] != null ? Source.values[result[3]! as int] : null, - serverTimestampBehavior: result[4] != null - ? ServerTimestampBehavior.values[result[4]! as int] - : null, + data: result[1] as Map?, + option: result[2] as InternalDocumentOption?, + source: result[3] as Source?, + serverTimestampBehavior: result[4] as ServerTimestampBehavior?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! DocumentReferenceRequest || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(path, other.path) && + _deepEquals(data, other.data) && + _deepEquals(option, other.option) && + _deepEquals(source, other.source) && + _deepEquals(serverTimestampBehavior, other.serverTimestampBehavior); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonQueryParameters { - PigeonQueryParameters({ +class InternalQueryParameters { + InternalQueryParameters({ this.where, this.orderBy, this.limit, @@ -468,7 +898,7 @@ class PigeonQueryParameters { Map? filters; - Object encode() { + List _toList() { return [ where, orderBy, @@ -482,20 +912,48 @@ class PigeonQueryParameters { ]; } - static PigeonQueryParameters decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalQueryParameters decode(Object result) { result as List; - return PigeonQueryParameters( + return InternalQueryParameters( where: (result[0] as List?)?.cast?>(), orderBy: (result[1] as List?)?.cast?>(), limit: result[2] as int?, limitToLast: result[3] as int?, - startAt: (result[4] as List?)?.cast(), - startAfter: (result[5] as List?)?.cast(), - endAt: (result[6] as List?)?.cast(), - endBefore: (result[7] as List?)?.cast(), + startAt: result[4] as List?, + startAfter: result[5] as List?, + endAt: result[6] as List?, + endBefore: result[7] as List?, filters: (result[8] as Map?)?.cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalQueryParameters || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(where, other.where) && + _deepEquals(orderBy, other.orderBy) && + _deepEquals(limit, other.limit) && + _deepEquals(limitToLast, other.limitToLast) && + _deepEquals(startAt, other.startAt) && + _deepEquals(startAfter, other.startAfter) && + _deepEquals(endAt, other.endAt) && + _deepEquals(endBefore, other.endBefore) && + _deepEquals(filters, other.filters); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class AggregateQuery { @@ -508,20 +966,40 @@ class AggregateQuery { String? field; - Object encode() { + List _toList() { return [ - type.index, + type, field, ]; } + Object encode() { + return _toList(); + } + static AggregateQuery decode(Object result) { result as List; return AggregateQuery( - type: AggregateType.values[result[0]! as int], + type: result[0]! as AggregateType, field: result[1] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! AggregateQuery || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(type, other.type) && _deepEquals(field, other.field); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class AggregateQueryResponse { @@ -537,67 +1015,125 @@ class AggregateQueryResponse { double? value; - Object encode() { + List _toList() { return [ - type.index, + type, field, value, ]; } + Object encode() { + return _toList(); + } + static AggregateQueryResponse decode(Object result) { result as List; return AggregateQueryResponse( - type: AggregateType.values[result[0]! as int], + type: result[0]! as AggregateType, field: result[1] as String?, value: result[2] as double?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! AggregateQueryResponse || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(type, other.type) && + _deepEquals(field, other.field) && + _deepEquals(value, other.value); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class _FirebaseFirestoreHostApiCodec extends FirestoreMessageCodec { - const _FirebaseFirestoreHostApiCodec(); +class PigeonCodec extends FirestoreMessageCodec { + const PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is AggregateQuery) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is AggregateQueryResponse) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is DocumentChangeType) { buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is DocumentReferenceRequest) { + writeValue(buffer, value.index); + } else if (value is Source) { buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is FirestorePigeonFirebaseApp) { + writeValue(buffer, value.index); + } else if (value is ListenSource) { buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is PigeonDocumentChange) { + writeValue(buffer, value.index); + } else if (value is ServerTimestampBehavior) { buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is PigeonDocumentOption) { + writeValue(buffer, value.index); + } else if (value is AggregateSource) { buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else if (value is PigeonDocumentSnapshot) { + writeValue(buffer, value.index); + } else if (value is PersistenceCacheIndexManagerRequest) { buffer.putUint8(134); - writeValue(buffer, value.encode()); - } else if (value is PigeonFirebaseSettings) { + writeValue(buffer, value.index); + } else if (value is InternalTransactionResult) { buffer.putUint8(135); - writeValue(buffer, value.encode()); - } else if (value is PigeonGetOptions) { + writeValue(buffer, value.index); + } else if (value is InternalTransactionType) { buffer.putUint8(136); - writeValue(buffer, value.encode()); - } else if (value is PigeonQueryParameters) { + writeValue(buffer, value.index); + } else if (value is AggregateType) { buffer.putUint8(137); - writeValue(buffer, value.encode()); - } else if (value is PigeonQuerySnapshot) { + writeValue(buffer, value.index); + } else if (value is InternalFirebaseSettings) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PigeonSnapshotMetadata) { + } else if (value is FirestorePigeonFirebaseApp) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PigeonTransactionCommand) { + } else if (value is InternalSnapshotMetadata) { buffer.putUint8(140); writeValue(buffer, value.encode()); + } else if (value is InternalDocumentSnapshot) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else if (value is InternalDocumentChange) { + buffer.putUint8(142); + writeValue(buffer, value.encode()); + } else if (value is InternalQuerySnapshot) { + buffer.putUint8(143); + writeValue(buffer, value.encode()); + } else if (value is InternalPipelineResult) { + buffer.putUint8(144); + writeValue(buffer, value.encode()); + } else if (value is InternalPipelineSnapshot) { + buffer.putUint8(145); + writeValue(buffer, value.encode()); + } else if (value is InternalGetOptions) { + buffer.putUint8(146); + writeValue(buffer, value.encode()); + } else if (value is InternalDocumentOption) { + buffer.putUint8(147); + writeValue(buffer, value.encode()); + } else if (value is InternalTransactionCommand) { + buffer.putUint8(148); + writeValue(buffer, value.encode()); + } else if (value is DocumentReferenceRequest) { + buffer.putUint8(149); + writeValue(buffer, value.encode()); + } else if (value is InternalQueryParameters) { + buffer.putUint8(150); + writeValue(buffer, value.encode()); + } else if (value is AggregateQuery) { + buffer.putUint8(151); + writeValue(buffer, value.encode()); + } else if (value is AggregateQueryResponse) { + buffer.putUint8(152); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -606,32 +1142,65 @@ class _FirebaseFirestoreHostApiCodec extends FirestoreMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: - return AggregateQuery.decode(readValue(buffer)!); case 129: - return AggregateQueryResponse.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : DocumentChangeType.values[value]; case 130: - return DocumentReferenceRequest.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : Source.values[value]; case 131: - return FirestorePigeonFirebaseApp.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : ListenSource.values[value]; case 132: - return PigeonDocumentChange.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : ServerTimestampBehavior.values[value]; case 133: - return PigeonDocumentOption.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : AggregateSource.values[value]; case 134: - return PigeonDocumentSnapshot.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null + ? null + : PersistenceCacheIndexManagerRequest.values[value]; case 135: - return PigeonFirebaseSettings.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : InternalTransactionResult.values[value]; case 136: - return PigeonGetOptions.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : InternalTransactionType.values[value]; case 137: - return PigeonQueryParameters.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : AggregateType.values[value]; case 138: - return PigeonQuerySnapshot.decode(readValue(buffer)!); + return InternalFirebaseSettings.decode(readValue(buffer)!); case 139: - return PigeonSnapshotMetadata.decode(readValue(buffer)!); + return FirestorePigeonFirebaseApp.decode(readValue(buffer)!); case 140: - return PigeonTransactionCommand.decode(readValue(buffer)!); + return InternalSnapshotMetadata.decode(readValue(buffer)!); + case 141: + return InternalDocumentSnapshot.decode(readValue(buffer)!); + case 142: + return InternalDocumentChange.decode(readValue(buffer)!); + case 143: + return InternalQuerySnapshot.decode(readValue(buffer)!); + case 144: + return InternalPipelineResult.decode(readValue(buffer)!); + case 145: + return InternalPipelineSnapshot.decode(readValue(buffer)!); + case 146: + return InternalGetOptions.decode(readValue(buffer)!); + case 147: + return InternalDocumentOption.decode(readValue(buffer)!); + case 148: + return InternalTransactionCommand.decode(readValue(buffer)!); + case 149: + return DocumentReferenceRequest.decode(readValue(buffer)!); + case 150: + return InternalQueryParameters.decode(readValue(buffer)!); + case 151: + return AggregateQuery.decode(readValue(buffer)!); + case 152: + return AggregateQueryResponse.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -642,704 +1211,530 @@ class FirebaseFirestoreHostApi { /// Constructor for [FirebaseFirestoreHostApi]. 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. - FirebaseFirestoreHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + FirebaseFirestoreHostApi( + {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 = _FirebaseFirestoreHostApiCodec(); + final String pigeonVar_messageChannelSuffix; Future loadBundle( - FirestorePigeonFirebaseApp arg_app, - Uint8List arg_bundle, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_bundle]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + FirestorePigeonFirebaseApp app, Uint8List bundle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, bundle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } - Future namedQueryGet( - FirestorePigeonFirebaseApp arg_app, - String arg_name, - PigeonGetOptions arg_options, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel - .send([arg_app, arg_name, arg_options]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonQuerySnapshot?)!; - } + Future namedQueryGet(FirestorePigeonFirebaseApp app, + String name, InternalGetOptions options) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, name, options]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalQuerySnapshot; } - Future clearPersistence(FirestorePigeonFirebaseApp arg_app) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future clearPersistence(FirestorePigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future disableNetwork(FirestorePigeonFirebaseApp arg_app) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future disableNetwork(FirestorePigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future enableNetwork(FirestorePigeonFirebaseApp arg_app) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future enableNetwork(FirestorePigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future terminate(FirestorePigeonFirebaseApp arg_app) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future terminate(FirestorePigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future waitForPendingWrites(FirestorePigeonFirebaseApp arg_app) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future waitForPendingWrites(FirestorePigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setIndexConfiguration( - FirestorePigeonFirebaseApp arg_app, - String arg_indexConfiguration, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel - .send([arg_app, arg_indexConfiguration]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + FirestorePigeonFirebaseApp app, String indexConfiguration) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, indexConfiguration]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future setLoggingEnabled(bool arg_loggingEnabled) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_loggingEnabled]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future setLoggingEnabled(bool loggingEnabled) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([loggingEnabled]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future snapshotsInSyncSetup( - FirestorePigeonFirebaseApp arg_app, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + Future snapshotsInSyncSetup(FirestorePigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future transactionCreate( - FirestorePigeonFirebaseApp arg_app, - int arg_timeout, - int arg_maxAttempts, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_timeout, arg_maxAttempts]) - as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + FirestorePigeonFirebaseApp app, int timeout, int maxAttempts) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, timeout, maxAttempts]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future transactionStoreResult( - String arg_transactionId, - PigeonTransactionResult arg_resultType, - List? arg_commands, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel.send( - [arg_transactionId, arg_resultType.index, arg_commands], - ) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + String transactionId, + InternalTransactionResult resultType, + List? commands) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([transactionId, resultType, commands]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future transactionGet( - FirestorePigeonFirebaseApp arg_app, - String arg_transactionId, - String arg_path, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_transactionId, arg_path]) - as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonDocumentSnapshot?)!; - } + Future transactionGet( + FirestorePigeonFirebaseApp app, String transactionId, String path) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, transactionId, path]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalDocumentSnapshot; } Future documentReferenceSet( - FirestorePigeonFirebaseApp arg_app, - DocumentReferenceRequest arg_request, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future documentReferenceUpdate( - FirestorePigeonFirebaseApp arg_app, - DocumentReferenceRequest arg_request, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future documentReferenceGet( - FirestorePigeonFirebaseApp arg_app, - DocumentReferenceRequest arg_request, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonDocumentSnapshot?)!; - } + Future documentReferenceGet( + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalDocumentSnapshot; } Future documentReferenceDelete( - FirestorePigeonFirebaseApp arg_app, - DocumentReferenceRequest arg_request, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + FirestorePigeonFirebaseApp app, DocumentReferenceRequest request) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future queryGet( - FirestorePigeonFirebaseApp arg_app, - String arg_path, - bool arg_isCollectionGroup, - PigeonQueryParameters arg_parameters, - PigeonGetOptions arg_options, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel.send([ - arg_app, - arg_path, - arg_isCollectionGroup, - arg_parameters, - arg_options, - ]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonQuerySnapshot?)!; - } + Future queryGet( + FirestorePigeonFirebaseApp app, + String path, + bool isCollectionGroup, + InternalQueryParameters parameters, + InternalGetOptions options) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([app, path, isCollectionGroup, parameters, options]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalQuerySnapshot; } Future> aggregateQuery( - FirestorePigeonFirebaseApp arg_app, - String arg_path, - PigeonQueryParameters arg_parameters, - AggregateSource arg_source, - List arg_queries, - bool arg_isCollectionGroup, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel.send([ - arg_app, - arg_path, - arg_parameters, - arg_source.index, - arg_queries, - arg_isCollectionGroup, - ]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as List?)!.cast(); - } + FirestorePigeonFirebaseApp app, + String path, + InternalQueryParameters parameters, + AggregateSource source, + List queries, + bool isCollectionGroup) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [app, path, parameters, source, queries, isCollectionGroup]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as List) + .cast(); } - Future writeBatchCommit( - FirestorePigeonFirebaseApp arg_app, - List arg_writes, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = - await channel.send([arg_app, arg_writes]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future writeBatchCommit(FirestorePigeonFirebaseApp app, + List writes) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, writes]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future querySnapshot( - FirestorePigeonFirebaseApp arg_app, - String arg_path, - bool arg_isCollectionGroup, - PigeonQueryParameters arg_parameters, - PigeonGetOptions arg_options, - bool arg_includeMetadataChanges, - ListenSource arg_source, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel.send([ - arg_app, - arg_path, - arg_isCollectionGroup, - arg_parameters, - arg_options, - arg_includeMetadataChanges, - arg_source.index, - ]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + FirestorePigeonFirebaseApp app, + String path, + bool isCollectionGroup, + InternalQueryParameters parameters, + InternalGetOptions options, + bool includeMetadataChanges, + ListenSource source) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([ + app, + path, + isCollectionGroup, + parameters, + options, + includeMetadataChanges, + source + ]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future documentReferenceSnapshot( - FirestorePigeonFirebaseApp arg_app, - DocumentReferenceRequest arg_parameters, - bool arg_includeMetadataChanges, - ListenSource arg_source, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel.send([ - arg_app, - arg_parameters, - arg_includeMetadataChanges, - arg_source.index, - ]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + FirestorePigeonFirebaseApp app, + DocumentReferenceRequest parameters, + bool includeMetadataChanges, + ListenSource source) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([app, parameters, includeMetadataChanges, source]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future persistenceCacheIndexManagerRequest( - FirestorePigeonFirebaseApp arg_app, - PersistenceCacheIndexManagerRequest arg_request, - ) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest', - codec, - binaryMessenger: _binaryMessenger, - ); - final List? replyList = await channel - .send([arg_app, arg_request.index]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + FirestorePigeonFirebaseApp app, + PersistenceCacheIndexManagerRequest request) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + } + + Future executePipeline( + FirestorePigeonFirebaseApp app, + List?> stages, + Map? options) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, stages, options]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalPipelineSnapshot; } } diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_document_snapshot.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_document_snapshot.dart index 05f7ada727e8..3fce79a77f10 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_document_snapshot.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_document_snapshot.dart @@ -38,7 +38,7 @@ class DocumentSnapshotPlatform extends PlatformInterface { final Map? _data; - final PigeonSnapshotMetadata _metadata; + final InternalSnapshotMetadata _metadata; /// The database ID of the snapshot's document. String get id => _pointer.id; diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_firestore.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_firestore.dart index 3faffc0a3778..ee9cd3a32478 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_firestore.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_firestore.dart @@ -243,6 +243,21 @@ abstract class FirebaseFirestorePlatform extends PlatformInterface { throw UnimplementedError('setLoggingEnabled() is not implemented'); } + /// Creates a pipeline platform instance with initial stages. + PipelinePlatform pipeline(List> initialStages) { + throw UnimplementedError('pipeline() is not implemented'); + } + + /// Executes a pipeline and returns the results. + /// + /// The [stages] parameter contains the serialized pipeline stages. + Future executePipeline( + List> stages, { + Map? options, + }) { + throw UnimplementedError('executePipeline() is not implemented'); + } + @override //ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) => diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_pipeline.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_pipeline.dart new file mode 100644 index 000000000000..2d0449ee4aef --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_pipeline.dart @@ -0,0 +1,54 @@ +// ignore_for_file: require_trailing_commas +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; +import 'package:meta/meta.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +/// Represents a pipeline for querying and transforming Firestore data. +@immutable +abstract class PipelinePlatform extends PlatformInterface { + /// Create a [PipelinePlatform] instance + PipelinePlatform(this.firestore, List>? stages) + : _stages = stages ?? [], + super(token: _token); + + static final Object _token = Object(); + + /// Throws an [AssertionError] if [instance] does not extend + /// [PipelinePlatform]. + /// + /// This is used by the app-facing [Pipeline] to ensure that + /// the object in which it's going to delegate calls has been + /// constructed properly. + static void verify(PipelinePlatform instance) { + PlatformInterface.verify(instance, _token); + } + + /// The [FirebaseFirestorePlatform] interface for this current pipeline. + final FirebaseFirestorePlatform firestore; + + /// Stores the pipeline stages. + final List> _stages; + + /// Exposes the [stages] on the pipeline delegate. + /// + /// This should only be used for testing to ensure that all + /// pipeline stages are correctly set on the underlying delegate + /// when being tested from a different package. + List> get stages { + return List.unmodifiable(_stages); + } + + /// Adds a serialized stage to the pipeline + PipelinePlatform addStage(Map serializedStage); + + /// Executes the pipeline and returns a snapshot of the results + Future execute({ + Map? options, + }); +} diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_pipeline_snapshot.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_pipeline_snapshot.dart new file mode 100644 index 000000000000..1946ce740712 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_pipeline_snapshot.dart @@ -0,0 +1,44 @@ +// ignore_for_file: require_trailing_commas +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import '../../cloud_firestore_platform_interface.dart'; +import 'platform_interface_document_reference.dart'; + +/// Platform interface for [PipelineSnapshot]. +abstract class PipelineSnapshotPlatform extends PlatformInterface { + /// Create an instance of [PipelineSnapshotPlatform]. + PipelineSnapshotPlatform() : super(token: _token); + + static final Object _token = Object(); + + /// The results of the pipeline execution + List get results; + + /// The execution time of the pipeline + DateTime get executionTime; +} + +/// Platform interface for [PipelineResult]. +abstract class PipelineResultPlatform extends PlatformInterface { + /// Create an instance of [PipelineResultPlatform]. + PipelineResultPlatform() : super(token: _token); + + static final Object _token = Object(); + + /// The document reference. Null for aggregate-only results (no document row). + DocumentReferencePlatform? get document; + + /// The creation time of the document + DateTime? get createTime; + + /// The update time of the document + DateTime? get updateTime; + + /// All fields in the result (from PipelineResult.data() on the native SDK). + /// Returns null if the result has no data. + Map? get data; +} diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_transaction.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_transaction.dart index e6648db2aea2..64ae9fef837e 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_transaction.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/platform_interface/platform_interface_transaction.dart @@ -31,7 +31,7 @@ abstract class TransactionPlatform extends PlatformInterface { } /// Returns all transaction commands for the current instance. - List get commands { + List get commands { throw UnimplementedError('commands is not implemented'); } diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh index f6f0a2d4aef5..0bf9dabad9d7 100755 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/generate_pigeon.sh @@ -1,4 +1,5 @@ # Objective of this script is to fix some files generated by Pigeon because Pigeon does not support Custom Codecs. +# These transformations are tuned for Pigeon 26.x. If you bump Pigeon, re-verify every sed/perl below. echo "Generate Pigeon Files." (cd .. && dart run pigeon --input ./pigeons/messages.dart) @@ -9,26 +10,36 @@ echo "Formatting complete." # # Fix Java files FILE_NAME="../../cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java" +# Expose toList() so plugin code can serialize generated classes outside of the pigeon codec. sed -i '' 's/ArrayList toList() {/public ArrayList toList() {/' "$FILE_NAME" -sed -i '' 's/private static class FirebaseFirestoreHostApiCodec extends StandardMessageCodec {/private static class FirebaseFirestoreHostApiCodec extends FlutterFirebaseFirestoreMessageCodec {/' "$FILE_NAME" +# Pigeon 26 emits a single `PigeonCodec` per file (was `FirebaseFirestoreHostApiCodec` in older versions). +# Swap its base class to the custom Firestore codec so Firestore types are encoded/decoded, and +# expose it publicly so the plugin can reuse it on EventChannel/MethodChannel instances (those +# must serialize Pigeon-generated types like `InternalDocumentSnapshot` emitted via stream handlers). +sed -i '' 's/private static class PigeonCodec extends StandardMessageCodec {/public static class PigeonCodec extends FlutterFirebaseFirestoreMessageCodec {/' "$FILE_NAME" echo "Android modification complete." # Fix iOS files -FILE_NAME="../../cloud_firestore/ios/Classes/FirestoreMessages.g.m" +FILE_NAME="../../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FirestoreMessages.g.m" sed -i '' '/#import "FirestoreMessages.g.h"/a\ #import "FLTFirebaseFirestoreReader.h"\ #import "FLTFirebaseFirestoreWriter.h" ' $FILE_NAME -sed -i '' 's/pigeonResult.newIndex = newIndex;/pigeonResult.index = newIndex;/' $FILE_NAME -sed -i '' -e 's/pigeonResult.newIndex = GetNullableObjectAtIndex(list, 3);/pigeonResult.index = GetNullableObjectAtIndex(list, 3);/' -e 's/NSAssert(pigeonResult.newIndex != nil, @"");/NSAssert(pigeonResult.index != nil, @"");/' $FILE_NAME -sed -i '' 's/(self\.newIndex \?: \[NSNull null\]),/(self.index ?: [NSNull null]),/' $FILE_NAME +# Pigeon 26 generates ObjC codec classes with a `nullPigeonCodec*` prefix when no +# ObjcOptions prefix is configured. Rename them to stable, readable names first. +sed -i '' 's/nullFirestoreMessagesPigeonCodecReaderWriter/FirebaseFirestoreHostApiCodecReaderWriter/g' $FILE_NAME +sed -i '' 's/nullFirestoreMessagesPigeonCodecReader/FirebaseFirestoreHostApiCodecReader/g' $FILE_NAME +sed -i '' 's/nullFirestoreMessagesPigeonCodecWriter/FirebaseFirestoreHostApiCodecWriter/g' $FILE_NAME +# Rename the public codec getter from `nullGetFirestoreMessagesCodec` so the plugin can reuse +# it on EventChannels without an awkward `null` prefix. +sed -i '' 's/nullGetFirestoreMessagesCodec/GetFirebaseFirestoreHostApiCodec/g' $FILE_NAME +sed -i '' 's/nullGetFirestoreMessagesCodec/GetFirebaseFirestoreHostApiCodec/g' ../../cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Public/FirestoreMessages.g.h +# Reparent the reader/writer onto our custom Firestore reader/writer so Firestore-specific +# types (Timestamp, GeoPoint, FieldValue, DocumentReference, FieldPath, ...) round-trip. sed -i '' 's/@interface FirebaseFirestoreHostApiCodecReader : FlutterStandardReader/@interface FirebaseFirestoreHostApiCodecReader : FLTFirebaseFirestoreReader/' $FILE_NAME sed -i '' 's/@interface FirebaseFirestoreHostApiCodecWriter : FlutterStandardWriter/@interface FirebaseFirestoreHostApiCodecWriter : FLTFirebaseFirestoreWriter/' $FILE_NAME -FILE_NAME="../../cloud_firestore/ios/Classes/Public/FirestoreMessages.g.h" -sed -i '' 's/@property(nonatomic, strong) NSNumber \*newIndex;/@property(nonatomic, strong) NSNumber \*index;/' $FILE_NAME - echo "iOS modification complete." # Fix Windows files @@ -36,25 +47,49 @@ FILE_NAME="../../cloud_firestore/windows/messages.g.h" sed -i '' '/#include /a\ #include "firestore_codec.h" ' $FILE_NAME -perl -i -0777 -pe 's|private:\n(\s+static PigeonSnapshotMetadata FromEncodableList\(\n\s+const flutter::EncodableList& list\);\n\s+flutter::EncodableList ToEncodableList\(\) const;)|\1\n\n private:|gs' $FILE_NAME -perl -0777 -i -pe 's/private:\n(\x20{2}static PigeonDocumentSnapshot FromEncodableList\(\n\x20{6}const flutter::EncodableList& list\);\n\x20{2}flutter::EncodableList ToEncodableList\(\) const;)/\1\n\n private:/gs' $FILE_NAME -perl -0777 -i -pe 's/private:\n(\x20{2}static PigeonDocumentChange FromEncodableList\(\n\x20{6}const flutter::EncodableList& list\);\n\x20{2}flutter::EncodableList ToEncodableList\(\) const;)/\1\n\n private:/gs' $FILE_NAME +# Make FromEncodableList / ToEncodableList accessible from plugin code (they're used directly +# to serialize stream events). Pigeon 26 qualifies EncodableList with a leading `::`, hence the +# `::flutter::EncodableList` pattern below. +perl -i -0777 -pe 's|private:\n(\s+static InternalSnapshotMetadata FromEncodableList\(\n\s+const ::flutter::EncodableList& list\);\n\s+::flutter::EncodableList ToEncodableList\(\) const;)|\1\n\n private:|gs' $FILE_NAME +perl -0777 -i -pe 's/private:\n(\x20{2}static InternalDocumentSnapshot FromEncodableList\(\n\x20{6}const ::flutter::EncodableList& list\);\n\x20{2}::flutter::EncodableList ToEncodableList\(\) const;)/\1\n\n private:/gs' $FILE_NAME +perl -0777 -i -pe 's/private:\n(\x20{2}static InternalDocumentChange FromEncodableList\(\n\x20{6}const ::flutter::EncodableList& list\);\n\x20{2}::flutter::EncodableList ToEncodableList\(\) const;)/\1\n\n private:/gs' $FILE_NAME -sed -i '' 's/: public flutter::StandardCodecSerializer {/: public cloud_firestore_windows::FirestoreCodec {/' $FILE_NAME +# Pigeon 26 renamed the generated C++ codec class to `PigeonInternalCodecSerializer`. The Windows +# plugin code historically references `FirebaseFirestoreHostApiCodecSerializer`, so keep that +# stable name and reparent the class onto our custom Firestore codec. +sed -i '' 's/PigeonInternalCodecSerializer/FirebaseFirestoreHostApiCodecSerializer/g' $FILE_NAME +sed -i '' 's|: public ::flutter::StandardCodecSerializer {|: public cloud_firestore_windows::FirestoreCodec {|' $FILE_NAME FILE_NAME="../../cloud_firestore/windows/messages.g.cpp" -sed -i '' 's/flutter::StandardCodecSerializer::WriteValue(value, stream);/cloud_firestore_windows::FirestoreCodec::WriteValue(value, stream);/' $FILE_NAME -sed -i '' 's/return flutter::StandardCodecSerializer::ReadValueOfType(type, stream);/return cloud_firestore_windows::FirestoreCodec::ReadValueOfType(type, stream);/' $FILE_NAME +sed -i '' 's/PigeonInternalCodecSerializer/FirebaseFirestoreHostApiCodecSerializer/g' $FILE_NAME +sed -i '' 's|::flutter::StandardCodecSerializer::WriteValue(value, stream)|::cloud_firestore_windows::FirestoreCodec::WriteValue(value, stream)|' $FILE_NAME +sed -i '' 's|::flutter::StandardCodecSerializer::ReadValueOfType(type, stream)|::cloud_firestore_windows::FirestoreCodec::ReadValueOfType(type, stream)|' $FILE_NAME + +# Pigeon 26 no longer auto-renames a C++ enum whose identifier collides with a method in the same +# scope. The `PersistenceCacheIndexManagerRequest` enum collides with the host API method of the +# same name, which fails to compile on MSVC. Rename every *usage* of the enum type (but keep the +# method name intact) by first renaming the identifier globally and then restoring call/method +# sites that are followed by `(`. +for FILE_NAME in "../../cloud_firestore/windows/messages.g.h" "../../cloud_firestore/windows/messages.g.cpp"; do + sed -i '' 's/PersistenceCacheIndexManagerRequest/PersistenceCacheIndexManagerRequestEnum/g' $FILE_NAME + sed -i '' 's/PersistenceCacheIndexManagerRequestEnum(/PersistenceCacheIndexManagerRequest(/g' $FILE_NAME +done echo "Windows modification complete." # Fix Dart files FILE_NAME="../lib/src/pigeon/messages.pigeon.dart" -sed -i '' '/import '\''package:flutter\/foundation\.dart'\'' show ReadBuffer, WriteBuffer;/i\ +# Pigeon 26 no longer imports flutter/foundation.dart, so anchor on flutter/services.dart instead. +sed -i '' '/import '\''package:flutter\/services\.dart'\'';/i\ import '\''package:cloud_firestore_platform_interface/src/method_channel/utils/firestore_message_codec.dart'\''; ' $FILE_NAME -sed -i '' 's/class _FirebaseFirestoreHostApiCodec extends StandardMessageCodec {/class _FirebaseFirestoreHostApiCodec extends FirestoreMessageCodec {/' $FILE_NAME -(cd .. && dart fix --apply > /dev/null) +# Pigeon 26 renamed the private codec to `_PigeonCodec`. Swap its base class to our custom one +# and expose it publicly (as `PigeonCodec`) so the plugin's EventChannels can reuse it to decode +# Pigeon-generated types like `InternalDocumentSnapshot` emitted via stream handlers. +sed -i '' 's/class _PigeonCodec extends StandardMessageCodec {/class PigeonCodec extends FirestoreMessageCodec {/' $FILE_NAME +sed -i '' 's/const _PigeonCodec();/const PigeonCodec();/' $FILE_NAME +sed -i '' 's/pigeonChannelCodec = _PigeonCodec();/pigeonChannelCodec = PigeonCodec();/' $FILE_NAME +(cd .. && dart fix --apply > /dev/null) FILE_NAME="../test/pigeon/test_api.dart" sed -i '' -E 's/import '\''dart:typed_data'\'' show Float64List, Int32List, Int64List, Uint8List;/import '\''dart:typed_data'\'' show Uint8List;/' $FILE_NAME diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart index 1c1bda0a1177..27a61ef98f0d 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/pigeons/messages.dart @@ -26,8 +26,8 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', ), ) -class PigeonFirebaseSettings { - const PigeonFirebaseSettings({ +class InternalFirebaseSettings { + const InternalFirebaseSettings({ required this.persistenceEnabled, required this.host, required this.sslEnabled, @@ -52,12 +52,12 @@ class FirestorePigeonFirebaseApp { }); final String appName; - final PigeonFirebaseSettings settings; + final InternalFirebaseSettings settings; final String databaseURL; } -class PigeonSnapshotMetadata { - const PigeonSnapshotMetadata({ +class InternalSnapshotMetadata { + const InternalSnapshotMetadata({ required this.hasPendingWrites, required this.isFromCache, }); @@ -66,8 +66,8 @@ class PigeonSnapshotMetadata { final bool isFromCache; } -class PigeonDocumentSnapshot { - const PigeonDocumentSnapshot({ +class InternalDocumentSnapshot { + const InternalDocumentSnapshot({ required this.path, required this.data, required this.metadata, @@ -75,7 +75,7 @@ class PigeonDocumentSnapshot { final String path; final Map? data; - final PigeonSnapshotMetadata metadata; + final InternalSnapshotMetadata metadata; } /// An enumeration of document change types. @@ -92,8 +92,8 @@ enum DocumentChangeType { removed, } -class PigeonDocumentChange { - const PigeonDocumentChange({ +class InternalDocumentChange { + const InternalDocumentChange({ required this.type, required this.document, required this.oldIndex, @@ -101,21 +101,46 @@ class PigeonDocumentChange { }); final DocumentChangeType type; - final PigeonDocumentSnapshot document; + final InternalDocumentSnapshot document; final int oldIndex; final int newIndex; } -class PigeonQuerySnapshot { - const PigeonQuerySnapshot({ +class InternalQuerySnapshot { + const InternalQuerySnapshot({ required this.documents, required this.documentChanges, required this.metadata, }); - final List documents; - final List documentChanges; - final PigeonSnapshotMetadata metadata; + final List documents; + final List documentChanges; + final InternalSnapshotMetadata metadata; +} + +class InternalPipelineResult { + const InternalPipelineResult({ + this.documentPath, + this.createTime, + this.updateTime, + this.data, + }); + + final String? documentPath; + final int? createTime; // Timestamp in milliseconds since epoch + final int? updateTime; // Timestamp in milliseconds since epoch + /// All fields in the result (from PipelineResult.data() on Android). + final Map? data; +} + +class InternalPipelineSnapshot { + const InternalPipelineSnapshot({ + required this.results, + required this.executionTime, + }); + + final List results; + final int executionTime; // Timestamp in milliseconds since epoch } /// An enumeration of firestore source types. @@ -178,8 +203,8 @@ enum PersistenceCacheIndexManagerRequest { deleteAllIndexes } -class PigeonGetOptions { - const PigeonGetOptions({ +class InternalGetOptions { + const InternalGetOptions({ required this.source, required this.serverTimestampBehavior, }); @@ -188,12 +213,12 @@ class PigeonGetOptions { final ServerTimestampBehavior serverTimestampBehavior; } -enum PigeonTransactionResult { +enum InternalTransactionResult { success, failure, } -enum PigeonTransactionType { +enum InternalTransactionType { get, update, set, @@ -201,8 +226,8 @@ enum PigeonTransactionType { deleteType, } -class PigeonDocumentOption { - const PigeonDocumentOption({ +class InternalDocumentOption { + const InternalDocumentOption({ required this.merge, required this.mergeFields, }); @@ -211,18 +236,18 @@ class PigeonDocumentOption { final List?>? mergeFields; } -class PigeonTransactionCommand { - const PigeonTransactionCommand({ +class InternalTransactionCommand { + const InternalTransactionCommand({ required this.type, required this.path, required this.data, this.option, }); - final PigeonTransactionType type; + final InternalTransactionType type; final String path; final Map? data; - final PigeonDocumentOption? option; + final InternalDocumentOption? option; } class DocumentReferenceRequest { @@ -235,13 +260,13 @@ class DocumentReferenceRequest { }); final String path; final Map? data; - final PigeonDocumentOption? option; + final InternalDocumentOption? option; final Source? source; final ServerTimestampBehavior? serverTimestampBehavior; } -class PigeonQueryParameters { - const PigeonQueryParameters({ +class InternalQueryParameters { + const InternalQueryParameters({ this.where, this.orderBy, this.limit, @@ -301,10 +326,10 @@ abstract class FirebaseFirestoreHostApi { ); @async - PigeonQuerySnapshot namedQueryGet( + InternalQuerySnapshot namedQueryGet( FirestorePigeonFirebaseApp app, String name, - PigeonGetOptions options, + InternalGetOptions options, ); @async @@ -358,12 +383,12 @@ abstract class FirebaseFirestoreHostApi { @async void transactionStoreResult( String transactionId, - PigeonTransactionResult resultType, - List? commands, + InternalTransactionResult resultType, + List? commands, ); @async - PigeonDocumentSnapshot transactionGet( + InternalDocumentSnapshot transactionGet( FirestorePigeonFirebaseApp app, String transactionId, String path, @@ -382,7 +407,7 @@ abstract class FirebaseFirestoreHostApi { ); @async - PigeonDocumentSnapshot documentReferenceGet( + InternalDocumentSnapshot documentReferenceGet( FirestorePigeonFirebaseApp app, DocumentReferenceRequest request, ); @@ -394,19 +419,19 @@ abstract class FirebaseFirestoreHostApi { ); @async - PigeonQuerySnapshot queryGet( + InternalQuerySnapshot queryGet( FirestorePigeonFirebaseApp app, String path, bool isCollectionGroup, - PigeonQueryParameters parameters, - PigeonGetOptions options, + InternalQueryParameters parameters, + InternalGetOptions options, ); @async List aggregateQuery( FirestorePigeonFirebaseApp app, String path, - PigeonQueryParameters parameters, + InternalQueryParameters parameters, AggregateSource source, List queries, bool isCollectionGroup, @@ -415,7 +440,7 @@ abstract class FirebaseFirestoreHostApi { @async void writeBatchCommit( FirestorePigeonFirebaseApp app, - List writes, + List writes, ); @async @@ -423,8 +448,8 @@ abstract class FirebaseFirestoreHostApi { FirestorePigeonFirebaseApp app, String path, bool isCollectionGroup, - PigeonQueryParameters parameters, - PigeonGetOptions options, + InternalQueryParameters parameters, + InternalGetOptions options, bool includeMetadataChanges, ListenSource source, ); @@ -442,4 +467,11 @@ abstract class FirebaseFirestoreHostApi { FirestorePigeonFirebaseApp app, PersistenceCacheIndexManagerRequest request, ); + + @async + InternalPipelineSnapshot executePipeline( + FirestorePigeonFirebaseApp app, + List?> stages, + Map? options, + ); } diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/pubspec.yaml b/packages/cloud_firestore/cloud_firestore_platform_interface/pubspec.yaml index 3a812504557f..9a4ba100e7b3 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/pubspec.yaml +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/pubspec.yaml @@ -1,29 +1,28 @@ name: cloud_firestore_platform_interface description: A common platform interface for the cloud_firestore plugin. -version: 7.1.0 +version: 8.0.3 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/cloud_firestore/cloud_firestore_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/cloud_firestore/cloud_firestore_platform_interface environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 + _flutterfire_internals: ^1.3.73 collection: ^1.15.0 - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 - pigeon: 11.0.1 + pigeon: 26.3.4 watcher: ^1.1.0 -dependency_overrides: - watcher: ^1.1.0 diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart index 32d57ebdcb48..726d510a1f69 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/test/pigeon/test_api.dart @@ -1,61 +1,97 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Uint8List; - -import 'package:cloud_firestore_platform_interface/src/pigeon/messages.pigeon.dart'; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -class _TestFirebaseFirestoreHostApiCodec extends StandardMessageCodec { - const _TestFirebaseFirestoreHostApiCodec(); +import 'package:cloud_firestore_platform_interface/src/pigeon/messages.pigeon.dart'; + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is AggregateQuery) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is AggregateQueryResponse) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is DocumentChangeType) { buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is DocumentReferenceRequest) { + writeValue(buffer, value.index); + } else if (value is Source) { buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is FirestorePigeonFirebaseApp) { + writeValue(buffer, value.index); + } else if (value is ListenSource) { buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is PigeonDocumentChange) { + writeValue(buffer, value.index); + } else if (value is ServerTimestampBehavior) { buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is PigeonDocumentOption) { + writeValue(buffer, value.index); + } else if (value is AggregateSource) { buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else if (value is PigeonDocumentSnapshot) { + writeValue(buffer, value.index); + } else if (value is PersistenceCacheIndexManagerRequest) { buffer.putUint8(134); - writeValue(buffer, value.encode()); - } else if (value is PigeonFirebaseSettings) { + writeValue(buffer, value.index); + } else if (value is InternalTransactionResult) { buffer.putUint8(135); - writeValue(buffer, value.encode()); - } else if (value is PigeonGetOptions) { + writeValue(buffer, value.index); + } else if (value is InternalTransactionType) { buffer.putUint8(136); - writeValue(buffer, value.encode()); - } else if (value is PigeonQueryParameters) { + writeValue(buffer, value.index); + } else if (value is AggregateType) { buffer.putUint8(137); - writeValue(buffer, value.encode()); - } else if (value is PigeonQuerySnapshot) { + writeValue(buffer, value.index); + } else if (value is InternalFirebaseSettings) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PigeonSnapshotMetadata) { + } else if (value is FirestorePigeonFirebaseApp) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PigeonTransactionCommand) { + } else if (value is InternalSnapshotMetadata) { buffer.putUint8(140); writeValue(buffer, value.encode()); + } else if (value is InternalDocumentSnapshot) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else if (value is InternalDocumentChange) { + buffer.putUint8(142); + writeValue(buffer, value.encode()); + } else if (value is InternalQuerySnapshot) { + buffer.putUint8(143); + writeValue(buffer, value.encode()); + } else if (value is InternalPipelineResult) { + buffer.putUint8(144); + writeValue(buffer, value.encode()); + } else if (value is InternalPipelineSnapshot) { + buffer.putUint8(145); + writeValue(buffer, value.encode()); + } else if (value is InternalGetOptions) { + buffer.putUint8(146); + writeValue(buffer, value.encode()); + } else if (value is InternalDocumentOption) { + buffer.putUint8(147); + writeValue(buffer, value.encode()); + } else if (value is InternalTransactionCommand) { + buffer.putUint8(148); + writeValue(buffer, value.encode()); + } else if (value is DocumentReferenceRequest) { + buffer.putUint8(149); + writeValue(buffer, value.encode()); + } else if (value is InternalQueryParameters) { + buffer.putUint8(150); + writeValue(buffer, value.encode()); + } else if (value is AggregateQuery) { + buffer.putUint8(151); + writeValue(buffer, value.encode()); + } else if (value is AggregateQueryResponse) { + buffer.putUint8(152); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -63,37 +99,83 @@ class _TestFirebaseFirestoreHostApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { - return switch (type) { - 128 => AggregateQuery.decode(readValue(buffer)!), - 129 => AggregateQueryResponse.decode(readValue(buffer)!), - 130 => DocumentReferenceRequest.decode(readValue(buffer)!), - 131 => FirestorePigeonFirebaseApp.decode(readValue(buffer)!), - 132 => PigeonDocumentChange.decode(readValue(buffer)!), - 133 => PigeonDocumentOption.decode(readValue(buffer)!), - 134 => PigeonDocumentSnapshot.decode(readValue(buffer)!), - 135 => PigeonFirebaseSettings.decode(readValue(buffer)!), - 136 => PigeonGetOptions.decode(readValue(buffer)!), - 137 => PigeonQueryParameters.decode(readValue(buffer)!), - 138 => PigeonQuerySnapshot.decode(readValue(buffer)!), - 139 => PigeonSnapshotMetadata.decode(readValue(buffer)!), - 140 => PigeonTransactionCommand.decode(readValue(buffer)!), - _ => super.readValueOfType(type, buffer) - }; + switch (type) { + case 129: + final value = readValue(buffer) as int?; + return value == null ? null : DocumentChangeType.values[value]; + case 130: + final value = readValue(buffer) as int?; + return value == null ? null : Source.values[value]; + case 131: + final value = readValue(buffer) as int?; + return value == null ? null : ListenSource.values[value]; + case 132: + final value = readValue(buffer) as int?; + return value == null ? null : ServerTimestampBehavior.values[value]; + case 133: + final value = readValue(buffer) as int?; + return value == null ? null : AggregateSource.values[value]; + case 134: + final value = readValue(buffer) as int?; + return value == null + ? null + : PersistenceCacheIndexManagerRequest.values[value]; + case 135: + final value = readValue(buffer) as int?; + return value == null ? null : InternalTransactionResult.values[value]; + case 136: + final value = readValue(buffer) as int?; + return value == null ? null : InternalTransactionType.values[value]; + case 137: + final value = readValue(buffer) as int?; + return value == null ? null : AggregateType.values[value]; + case 138: + return InternalFirebaseSettings.decode(readValue(buffer)!); + case 139: + return FirestorePigeonFirebaseApp.decode(readValue(buffer)!); + case 140: + return InternalSnapshotMetadata.decode(readValue(buffer)!); + case 141: + return InternalDocumentSnapshot.decode(readValue(buffer)!); + case 142: + return InternalDocumentChange.decode(readValue(buffer)!); + case 143: + return InternalQuerySnapshot.decode(readValue(buffer)!); + case 144: + return InternalPipelineResult.decode(readValue(buffer)!); + case 145: + return InternalPipelineSnapshot.decode(readValue(buffer)!); + case 146: + return InternalGetOptions.decode(readValue(buffer)!); + case 147: + return InternalDocumentOption.decode(readValue(buffer)!); + case 148: + return InternalTransactionCommand.decode(readValue(buffer)!); + case 149: + return DocumentReferenceRequest.decode(readValue(buffer)!); + case 150: + return InternalQueryParameters.decode(readValue(buffer)!); + case 151: + return AggregateQuery.decode(readValue(buffer)!); + case 152: + return AggregateQueryResponse.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } } } abstract class TestFirebaseFirestoreHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec codec = - _TestFirebaseFirestoreHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); Future loadBundle(FirestorePigeonFirebaseApp app, Uint8List bundle); - Future namedQueryGet( + Future namedQueryGet( FirestorePigeonFirebaseApp app, String name, - PigeonGetOptions options, + InternalGetOptions options, ); Future clearPersistence(FirestorePigeonFirebaseApp app); @@ -123,11 +205,11 @@ abstract class TestFirebaseFirestoreHostApi { Future transactionStoreResult( String transactionId, - PigeonTransactionResult resultType, - List? commands, + InternalTransactionResult resultType, + List? commands, ); - Future transactionGet( + Future transactionGet( FirestorePigeonFirebaseApp app, String transactionId, String path, @@ -143,7 +225,7 @@ abstract class TestFirebaseFirestoreHostApi { DocumentReferenceRequest request, ); - Future documentReferenceGet( + Future documentReferenceGet( FirestorePigeonFirebaseApp app, DocumentReferenceRequest request, ); @@ -153,18 +235,18 @@ abstract class TestFirebaseFirestoreHostApi { DocumentReferenceRequest request, ); - Future queryGet( + Future queryGet( FirestorePigeonFirebaseApp app, String path, bool isCollectionGroup, - PigeonQueryParameters parameters, - PigeonGetOptions options, + InternalQueryParameters parameters, + InternalGetOptions options, ); Future> aggregateQuery( FirestorePigeonFirebaseApp app, String path, - PigeonQueryParameters parameters, + InternalQueryParameters parameters, AggregateSource source, List queries, bool isCollectionGroup, @@ -172,15 +254,15 @@ abstract class TestFirebaseFirestoreHostApi { Future writeBatchCommit( FirestorePigeonFirebaseApp app, - List writes, + List writes, ); Future querySnapshot( FirestorePigeonFirebaseApp app, String path, bool isCollectionGroup, - PigeonQueryParameters parameters, - PigeonGetOptions options, + InternalQueryParameters parameters, + InternalGetOptions options, bool includeMetadataChanges, ListenSource source, ); @@ -197,894 +279,800 @@ abstract class TestFirebaseFirestoreHostApi { PersistenceCacheIndexManagerRequest request, ); - static void setup( + Future executePipeline( + FirestorePigeonFirebaseApp app, + List?> stages, + Map? options, + ); + + static void setUp( TestFirebaseFirestoreHostApi? api, { BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final Uint8List arg_bundle = args[1]! as Uint8List; + try { + final String output = await api.loadBundle(arg_app, arg_bundle); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final Uint8List? arg_bundle = (args[1] as Uint8List?); - assert( - arg_bundle != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.loadBundle was null, expected non-null Uint8List.', - ); - final String output = await api.loadBundle(arg_app!, arg_bundle!); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final String arg_name = args[1]! as String; + final InternalGetOptions arg_options = args[2]! as InternalGetOptions; + try { + final InternalQuerySnapshot output = + await api.namedQueryGet(arg_app, arg_name, arg_options); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final String? arg_name = (args[1] as String?); - assert( - arg_name != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet was null, expected non-null String.', - ); - final PigeonGetOptions? arg_options = (args[2] as PigeonGetOptions?); - assert( - arg_options != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.namedQueryGet was null, expected non-null PigeonGetOptions.', - ); - final PigeonQuerySnapshot output = - await api.namedQueryGet(arg_app!, arg_name!, arg_options!); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + try { + await api.clearPersistence(arg_app); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.clearPersistence was null, expected non-null FirestorePigeonFirebaseApp.', - ); - await api.clearPersistence(arg_app!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + try { + await api.disableNetwork(arg_app); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.disableNetwork was null, expected non-null FirestorePigeonFirebaseApp.', - ); - await api.disableNetwork(arg_app!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + try { + await api.enableNetwork(arg_app); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.enableNetwork was null, expected non-null FirestorePigeonFirebaseApp.', - ); - await api.enableNetwork(arg_app!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + try { + await api.terminate(arg_app); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.terminate was null, expected non-null FirestorePigeonFirebaseApp.', - ); - await api.terminate(arg_app!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + try { + await api.waitForPendingWrites(arg_app); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.waitForPendingWrites was null, expected non-null FirestorePigeonFirebaseApp.', - ); - await api.waitForPendingWrites(arg_app!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final String arg_indexConfiguration = args[1]! as String; + try { + await api.setIndexConfiguration(arg_app, arg_indexConfiguration); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final String? arg_indexConfiguration = (args[1] as String?); - assert( - arg_indexConfiguration != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setIndexConfiguration was null, expected non-null String.', - ); - await api.setIndexConfiguration(arg_app!, arg_indexConfiguration!); - return []; + final List args = message! as List; + final bool arg_loggingEnabled = args[0]! as bool; + try { + await api.setLoggingEnabled(arg_loggingEnabled); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled was null.', - ); - final List args = (message as List?)!; - final bool? arg_loggingEnabled = (args[0] as bool?); - assert( - arg_loggingEnabled != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.setLoggingEnabled was null, expected non-null bool.', - ); - await api.setLoggingEnabled(arg_loggingEnabled!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + try { + final String output = await api.snapshotsInSyncSetup(arg_app); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.snapshotsInSyncSetup was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final String output = await api.snapshotsInSyncSetup(arg_app!); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final int arg_timeout = args[1]! as int; + final int arg_maxAttempts = args[2]! as int; + try { + final String output = await api.transactionCreate( + arg_app, + arg_timeout, + arg_maxAttempts, + ); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final int? arg_timeout = (args[1] as int?); - assert( - arg_timeout != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate was null, expected non-null int.', - ); - final int? arg_maxAttempts = (args[2] as int?); - assert( - arg_maxAttempts != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionCreate was null, expected non-null int.', - ); - final String output = await api.transactionCreate( - arg_app!, - arg_timeout!, - arg_maxAttempts!, - ); - return [output]; + final List args = message! as List; + final String arg_transactionId = args[0]! as String; + final InternalTransactionResult arg_resultType = + args[1]! as InternalTransactionResult; + final List? arg_commands = + (args[2] as List?)?.cast(); + try { + await api.transactionStoreResult( + arg_transactionId, + arg_resultType, + arg_commands, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult was null.', - ); - final List args = (message as List?)!; - final String? arg_transactionId = (args[0] as String?); - assert( - arg_transactionId != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult was null, expected non-null String.', - ); - final PigeonTransactionResult? arg_resultType = args[1] == null - ? null - : PigeonTransactionResult.values[args[1]! as int]; - assert( - arg_resultType != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionStoreResult was null, expected non-null PigeonTransactionResult.', - ); - final List? arg_commands = - (args[2] as List?)?.cast(); - await api.transactionStoreResult( - arg_transactionId!, - arg_resultType!, - arg_commands, - ); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final String arg_transactionId = args[1]! as String; + final String arg_path = args[2]! as String; + try { + final InternalDocumentSnapshot output = + await api.transactionGet(arg_app, arg_transactionId, arg_path); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final String? arg_transactionId = (args[1] as String?); - assert( - arg_transactionId != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet was null, expected non-null String.', - ); - final String? arg_path = (args[2] as String?); - assert( - arg_path != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.transactionGet was null, expected non-null String.', - ); - final PigeonDocumentSnapshot output = - await api.transactionGet(arg_app!, arg_transactionId!, arg_path!); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final DocumentReferenceRequest arg_request = + args[1]! as DocumentReferenceRequest; + try { + await api.documentReferenceSet(arg_app, arg_request); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final DocumentReferenceRequest? arg_request = - (args[1] as DocumentReferenceRequest?); - assert( - arg_request != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSet was null, expected non-null DocumentReferenceRequest.', - ); - await api.documentReferenceSet(arg_app!, arg_request!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final DocumentReferenceRequest arg_request = + args[1]! as DocumentReferenceRequest; + try { + await api.documentReferenceUpdate(arg_app, arg_request); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final DocumentReferenceRequest? arg_request = - (args[1] as DocumentReferenceRequest?); - assert( - arg_request != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceUpdate was null, expected non-null DocumentReferenceRequest.', - ); - await api.documentReferenceUpdate(arg_app!, arg_request!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final DocumentReferenceRequest arg_request = + args[1]! as DocumentReferenceRequest; + try { + final InternalDocumentSnapshot output = + await api.documentReferenceGet(arg_app, arg_request); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final DocumentReferenceRequest? arg_request = - (args[1] as DocumentReferenceRequest?); - assert( - arg_request != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceGet was null, expected non-null DocumentReferenceRequest.', - ); - final PigeonDocumentSnapshot output = - await api.documentReferenceGet(arg_app!, arg_request!); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final DocumentReferenceRequest arg_request = + args[1]! as DocumentReferenceRequest; + try { + await api.documentReferenceDelete(arg_app, arg_request); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final DocumentReferenceRequest? arg_request = - (args[1] as DocumentReferenceRequest?); - assert( - arg_request != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceDelete was null, expected non-null DocumentReferenceRequest.', - ); - await api.documentReferenceDelete(arg_app!, arg_request!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final String arg_path = args[1]! as String; + final bool arg_isCollectionGroup = args[2]! as bool; + final InternalQueryParameters arg_parameters = + args[3]! as InternalQueryParameters; + final InternalGetOptions arg_options = args[4]! as InternalGetOptions; + try { + final InternalQuerySnapshot output = await api.queryGet( + arg_app, + arg_path, + arg_isCollectionGroup, + arg_parameters, + arg_options, + ); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final String? arg_path = (args[1] as String?); - assert( - arg_path != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet was null, expected non-null String.', - ); - final bool? arg_isCollectionGroup = (args[2] as bool?); - assert( - arg_isCollectionGroup != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet was null, expected non-null bool.', - ); - final PigeonQueryParameters? arg_parameters = - (args[3] as PigeonQueryParameters?); - assert( - arg_parameters != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet was null, expected non-null PigeonQueryParameters.', - ); - final PigeonGetOptions? arg_options = (args[4] as PigeonGetOptions?); - assert( - arg_options != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.queryGet was null, expected non-null PigeonGetOptions.', - ); - final PigeonQuerySnapshot output = await api.queryGet( - arg_app!, - arg_path!, - arg_isCollectionGroup!, - arg_parameters!, - arg_options!, - ); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final String arg_path = args[1]! as String; + final InternalQueryParameters arg_parameters = + args[2]! as InternalQueryParameters; + final AggregateSource arg_source = args[3]! as AggregateSource; + final List arg_queries = + (args[4]! as List).cast(); + final bool arg_isCollectionGroup = args[5]! as bool; + try { + final List output = + await api.aggregateQuery( + arg_app, + arg_path, + arg_parameters, + arg_source, + arg_queries, + arg_isCollectionGroup, + ); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final String? arg_path = (args[1] as String?); - assert( - arg_path != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery was null, expected non-null String.', - ); - final PigeonQueryParameters? arg_parameters = - (args[2] as PigeonQueryParameters?); - assert( - arg_parameters != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery was null, expected non-null PigeonQueryParameters.', - ); - final AggregateSource? arg_source = - args[3] == null ? null : AggregateSource.values[args[3]! as int]; - assert( - arg_source != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery was null, expected non-null AggregateSource.', - ); - final List? arg_queries = - (args[4] as List?)?.cast(); - assert( - arg_queries != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery was null, expected non-null List.', - ); - final bool? arg_isCollectionGroup = (args[5] as bool?); - assert( - arg_isCollectionGroup != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.aggregateQuery was null, expected non-null bool.', - ); - final List output = await api.aggregateQuery( - arg_app!, - arg_path!, - arg_parameters!, - arg_source!, - arg_queries!, - arg_isCollectionGroup!, - ); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final List arg_writes = + (args[1]! as List).cast(); + try { + await api.writeBatchCommit(arg_app, arg_writes); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final List? arg_writes = - (args[1] as List?)?.cast(); - assert( - arg_writes != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.writeBatchCommit was null, expected non-null List.', - ); - await api.writeBatchCommit(arg_app!, arg_writes!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final String arg_path = args[1]! as String; + final bool arg_isCollectionGroup = args[2]! as bool; + final InternalQueryParameters arg_parameters = + args[3]! as InternalQueryParameters; + final InternalGetOptions arg_options = args[4]! as InternalGetOptions; + final bool arg_includeMetadataChanges = args[5]! as bool; + final ListenSource arg_source = args[6]! as ListenSource; + try { + final String output = await api.querySnapshot( + arg_app, + arg_path, + arg_isCollectionGroup, + arg_parameters, + arg_options, + arg_includeMetadataChanges, + arg_source, + ); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final String? arg_path = (args[1] as String?); - assert( - arg_path != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null, expected non-null String.', - ); - final bool? arg_isCollectionGroup = (args[2] as bool?); - assert( - arg_isCollectionGroup != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null, expected non-null bool.', - ); - final PigeonQueryParameters? arg_parameters = - (args[3] as PigeonQueryParameters?); - assert( - arg_parameters != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null, expected non-null PigeonQueryParameters.', - ); - final PigeonGetOptions? arg_options = (args[4] as PigeonGetOptions?); - assert( - arg_options != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null, expected non-null PigeonGetOptions.', - ); - final bool? arg_includeMetadataChanges = (args[5] as bool?); - assert( - arg_includeMetadataChanges != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null, expected non-null bool.', - ); - final ListenSource? arg_source = - args[6] == null ? null : ListenSource.values[args[6]! as int]; - assert( - arg_source != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.querySnapshot was null, expected non-null ListenSource.', - ); - final String output = await api.querySnapshot( - arg_app!, - arg_path!, - arg_isCollectionGroup!, - arg_parameters!, - arg_options!, - arg_includeMetadataChanges!, - arg_source!, - ); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final DocumentReferenceRequest arg_parameters = + args[1]! as DocumentReferenceRequest; + final bool arg_includeMetadataChanges = args[2]! as bool; + final ListenSource arg_source = args[3]! as ListenSource; + try { + final String output = await api.documentReferenceSnapshot( + arg_app, + arg_parameters, + arg_includeMetadataChanges, + arg_source, + ); + 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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final DocumentReferenceRequest? arg_parameters = - (args[1] as DocumentReferenceRequest?); - assert( - arg_parameters != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot was null, expected non-null DocumentReferenceRequest.', - ); - final bool? arg_includeMetadataChanges = (args[2] as bool?); - assert( - arg_includeMetadataChanges != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot was null, expected non-null bool.', - ); - final ListenSource? arg_source = - args[3] == null ? null : ListenSource.values[args[3]! as int]; - assert( - arg_source != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.documentReferenceSnapshot was null, expected non-null ListenSource.', - ); - final String output = await api.documentReferenceSnapshot( - arg_app!, - arg_parameters!, - arg_includeMetadataChanges!, - arg_source!, - ); - return [output]; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final PersistenceCacheIndexManagerRequest arg_request = + args[1]! as PersistenceCacheIndexManagerRequest; + try { + await api.persistenceCacheIndexManagerRequest(arg_app, arg_request); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.executePipeline$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.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest was null.', - ); - final List args = (message as List?)!; - final FirestorePigeonFirebaseApp? arg_app = - (args[0] as FirestorePigeonFirebaseApp?); - assert( - arg_app != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest was null, expected non-null FirestorePigeonFirebaseApp.', - ); - final PersistenceCacheIndexManagerRequest? arg_request = - args[1] == null - ? null - : PersistenceCacheIndexManagerRequest.values[args[1]! as int]; - assert( - arg_request != null, - 'Argument for dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest was null, expected non-null PersistenceCacheIndexManagerRequest.', - ); - await api.persistenceCacheIndexManagerRequest(arg_app!, arg_request!); - return []; + final List args = message! as List; + final FirestorePigeonFirebaseApp arg_app = + args[0]! as FirestorePigeonFirebaseApp; + final List?> arg_stages = + (args[1]! as List).cast?>(); + final Map? arg_options = + (args[2] as Map?)?.cast(); + try { + final InternalPipelineSnapshot output = + await api.executePipeline(arg_app, arg_stages, arg_options); + 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/cloud_firestore/cloud_firestore_platform_interface/test/utils/test_firestore_message_codec.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/test/utils/test_firestore_message_codec.dart index 12b7b5bb1c27..d17c62a8ecf1 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/test/utils/test_firestore_message_codec.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/test/utils/test_firestore_message_codec.dart @@ -73,7 +73,7 @@ class TestFirestoreMessageCodec extends FirestoreMessageCodec { values['path'], FirestorePigeonFirebaseApp( appName: 'test', - settings: PigeonFirebaseSettings( + settings: InternalFirebaseSettings( ignoreUndefinedProperties: false, ), databaseURL: '', diff --git a/packages/cloud_firestore/cloud_firestore_web/CHANGELOG.md b/packages/cloud_firestore/cloud_firestore_web/CHANGELOG.md index b6e35474af23..707fbe40588e 100644 --- a/packages/cloud_firestore/cloud_firestore_web/CHANGELOG.md +++ b/packages/cloud_firestore/cloud_firestore_web/CHANGELOG.md @@ -1,3 +1,25 @@ +## 5.6.0 + + - **FEAT**(firestore): add support for search in firestore pipeline ([#18312](https://github.com/firebase/flutterfire/issues/18312)). ([b3c835f7](https://github.com/firebase/flutterfire/commit/b3c835f7bae8684d4c98167d78a071d9ed88f980)) + +## 5.5.0 + + - **FEAT**(firestore): add support for new array expressions ([#18283](https://github.com/firebase/flutterfire/issues/18283)). ([2293dee0](https://github.com/firebase/flutterfire/commit/2293dee00767cc6eebd57a340eb2b72bdab41d52)) + +## 5.4.1 + + - Update a dependency to the latest release. + +## 5.4.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(firestore,web): update Timestamp handling in jsify and EncodeUtility ([#18264](https://github.com/firebase/flutterfire/issues/18264)). ([9783a448](https://github.com/firebase/flutterfire/commit/9783a448ff532568a5e46ecb927e7b1bc77a164c)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 5.3.0 + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + ## 5.2.0 - **REFACTOR**: fix formatting and analysis issues across the repo ([#18124](https://github.com/firebase/flutterfire/issues/18124)). ([ab79fd93](https://github.com/firebase/flutterfire/commit/ab79fd93ee4ccfeb478687623134b1cf8ab71c74)) diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/cloud_firestore_web.dart b/packages/cloud_firestore/cloud_firestore_web/lib/cloud_firestore_web.dart index ff68541be65a..04d722e3decf 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/cloud_firestore_web.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/cloud_firestore_web.dart @@ -21,6 +21,9 @@ import 'src/collection_reference_web.dart'; import 'src/document_reference_web.dart'; import 'src/field_value_factory_web.dart'; import 'src/interop/firestore.dart' as firestore_interop; +import 'src/interop/firestore_interop.dart' as firestore_interop_js; +import 'src/pipeline_builder_web.dart'; +import 'src/pipeline_web.dart'; import 'src/query_web.dart'; import 'src/transaction_web.dart'; import 'src/write_batch_web.dart'; @@ -271,4 +274,48 @@ class FirebaseFirestoreWeb extends FirebaseFirestorePlatform { } _delegate.setLoggingEnabled(value); } + + @override + PipelinePlatform pipeline(List> initialStages) { + return PipelineWeb(this, _delegate, initialStages); + } + + @override + Future executePipeline( + List> stages, { + Map? options, + }) async { + final jsFirestore = _delegate.jsObject; + firestore_interop_js.PipelineJsImpl jsPipeline; + try { + jsPipeline = buildPipelineFromStages(jsFirestore, stages); + } catch (e, stack) { + // Let our Dart FirebaseException (e.g. unsupported expression) propagate + // so the user sees a clear message; the guard would cast it to JSError. + if (e is FirebaseException) { + Error.throwWithStackTrace(e, stack); + } + // JS or other errors: run through the guard to convert to FirebaseException. + return convertWebExceptions(() async { + Error.throwWithStackTrace(e, stack); + }); + } + + final dartPipeline = firestore_interop.Pipeline.getInstance(jsPipeline); + return convertWebExceptions(() async { + String? executeOptions; + if (options != null) { + executeOptions = options['indexMode'] as String; + } + final snapshot = await dartPipeline.execute(executeOptions); + + final results = snapshot.results + .map((r) => PipelineResultWeb(this, _delegate, r.jsObject)) + .toList(); + + final executionTime = snapshot.executionTime ?? DateTime.now(); + + return PipelineSnapshotWeb(results, executionTime); + }); + } } diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/cloud_firestore_version.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/cloud_firestore_version.dart index 305156baf450..bc5f696e6123 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/cloud_firestore_version.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/cloud_firestore_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '6.2.0'; +const packageVersion = '6.6.0'; diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore.dart index 6703d7738f14..9ed2cf332707 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore.dart @@ -1089,3 +1089,122 @@ class AggregateQuerySnapshot } } } + +// ============================================================================= +// Pipeline (global execute + snapshot/result wrappers) +// ============================================================================= + +/// Single result in a pipeline snapshot (document + data). +class PipelineResult + extends JsObjectWrapper { + static final _expando = Expando(); + + late final DocumentReference? _ref; + late final Map? _data; + late final DateTime? _createTime; + late final DateTime? _updateTime; + + static PipelineResult getInstance( + firestore_interop.PipelineResultJsImpl jsObject) { + return _expando[jsObject] ??= PipelineResult._fromJsObject(jsObject); + } + + PipelineResult._fromJsObject(firestore_interop.PipelineResultJsImpl jsObject) + : _ref = jsObject.ref != null + ? DocumentReference.getInstance(jsObject.ref!) + : null, + _data = _dataFromResult(jsObject), + _createTime = _timestampToDateTime(jsObject.createTime), + _updateTime = _timestampToDateTime(jsObject.updateTime), + super.fromJsObject(jsObject); + + static Map? _dataFromResult( + firestore_interop.PipelineResultJsImpl jsResult) { + final d = jsResult.data(); + if (d == null) return null; + final parsed = dartify(d); + return parsed != null + ? Map.from(parsed as Map) + : null; + } + + static DateTime? _timestampToDateTime(dynamic value) { + if (value == null) return null; + final d = dartify(value); + if (d == null) return null; + if (d is DateTime) return d; + if (d is Timestamp) return d.toDate(); + if (d is int) return DateTime.fromMillisecondsSinceEpoch(d); + return null; + } + + DocumentReference? get ref => _ref; + Map? get data => _data; + DateTime? get createTime => _createTime; + DateTime? get updateTime => _updateTime; +} + +/// Snapshot of pipeline execution results. +class PipelineSnapshot + extends JsObjectWrapper { + static final _expando = Expando(); + + late final List _results; + late final DateTime? _executionTime; + + static PipelineSnapshot getInstance( + firestore_interop.PipelineSnapshotJsImpl jsObject) { + return _expando[jsObject] ??= PipelineSnapshot._fromJsObject(jsObject); + } + + static List _buildResults( + firestore_interop.PipelineSnapshotJsImpl jsObject) { + final rawResults = jsObject.results.toDart; + return rawResults + .cast() + .map(PipelineResult.getInstance) + .toList(); + } + + PipelineSnapshot._fromJsObject( + firestore_interop.PipelineSnapshotJsImpl jsObject) + : _results = _buildResults(jsObject), + _executionTime = _executionTimeFromJs(jsObject.executionTime), + super.fromJsObject(jsObject); + + static DateTime? _executionTimeFromJs(dynamic value) { + if (value == null) return null; + final d = dartify(value); + if (d == null) return null; + if (d is DateTime) return d; + if (d is int) return DateTime.fromMillisecondsSinceEpoch(d); + return null; + } + + List get results => _results; + DateTime? get executionTime => _executionTime; +} + +/// Wraps a JS pipeline; use [execute] to run it via the global execute function. +class Pipeline extends JsObjectWrapper { + static final _expando = Expando(); + + static Pipeline getInstance(firestore_interop.PipelineJsImpl jsObject) { + return _expando[jsObject] ??= Pipeline._fromJsObject(jsObject); + } + + Pipeline._fromJsObject(firestore_interop.PipelineJsImpl jsObject) + : super.fromJsObject(jsObject); + + /// Runs this pipeline using the global JS SDK execute function. + Future execute(String? executeOptions) async { + final executeOptionsJs = firestore_interop.PipelineExecuteOptionsJsImpl(); + if (executeOptions != null) { + executeOptionsJs.indexMode = executeOptions.toJS; + } + executeOptionsJs.pipeline = jsObject as JSAny; + final snapshot = + await firestore_interop.pipelines.execute(executeOptionsJs).toDart; + return PipelineSnapshot.getInstance(snapshot); + } +} diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart index 85cc51fbffde..c271c4ac62aa 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart @@ -333,13 +333,207 @@ external JSObject get and; @staticInterop external WriteBatchJsImpl writeBatch(FirestoreJsImpl firestore); -@JS('Firestore') -@staticInterop -abstract class FirestoreJsImpl {} - -extension FirestoreJsImplExtension on FirestoreJsImpl { +extension type FirestoreJsImpl._(JSObject _) implements JSObject { external AppJsImpl get app; external JSString get type; + + /// Returns the pipeline source for building and executing pipelines. + external JSAny pipeline(); +} + +@JS() +@staticInterop +external PipelinesJsImpl get pipelines; + +/// Pipeline expression API — mirrors the Firebase JS SDK pipelines module. +/// Use these to build expressions for where(), sort(), addFields(), aggregate(), etc. +extension type PipelinesJsImpl._(JSObject _) implements JSObject { + external JSPromise execute( + PipelineExecuteOptionsJsImpl pipeline); + + // --- Expression builders --- + external ExpressionJsImpl field(JSString path); + external ExpressionJsImpl constant(JSAny? value); + + // --- Boolean / comparison --- + external JSAny equal(JSAny left, JSAny right); + external JSAny notEqual(JSAny left, JSAny right); + external JSAny greaterThan(JSAny left, JSAny right); + external JSAny greaterThanOrEqual(JSAny left, JSAny right); + external JSAny lessThan(JSAny left, JSAny right); + external JSAny lessThanOrEqual(JSAny left, JSAny right); + external JSAny and(JSAny a, JSAny b); + external JSAny or(JSAny a, JSAny b); + external JSAny xor(JSAny a, JSAny b); + external JSAny not(JSAny expr); + external JSAny documentMatches(JSString query); + + // --- Existence / type checks --- + external JSAny exists(JSAny expr); + external JSAny isAbsent(JSAny expr); + external JSAny isError(JSAny expr); + + // --- Array --- + external JSAny arrayContains(JSAny array, JSAny element); + external JSAny arrayContainsAny(JSAny array, JSArray values); + external JSAny arrayContainsAll(JSAny array, JSAny valuesOrArray); + + // --- IN / NOT IN (boolean) --- + external JSAny equalAny(JSAny element, JSAny valuesArray); + external JSAny notEqualAny(JSAny element, JSAny valuesArray); + + // --- String / value expressions (global) --- + external ExpressionJsImpl split(JSAny expression, JSAny delimiter); + external ExpressionJsImpl join(JSAny arrayExpression, JSAny delimiter); + external ExpressionJsImpl substring( + JSAny input, JSAny position, JSAny length); + external ExpressionJsImpl stringReplaceAll( + JSAny expression, JSAny find, JSAny replacement); + external ExpressionJsImpl ifAbsent(JSAny expression, JSAny elseExpr); + external ExpressionJsImpl ifError(JSAny expression, JSAny catchExpr); + external ExpressionJsImpl conditional( + JSAny condition, JSAny thenExpr, JSAny elseExpr); + external ExpressionJsImpl documentId(JSAny path); + external ExpressionJsImpl collectionId(JSAny expression); + external ExpressionJsImpl mapGet(JSAny mapExpr, JSString key); + external ExpressionJsImpl mapKeys(JSAny mapExpr); + external ExpressionJsImpl mapValues(JSAny mapExpr); + external ExpressionJsImpl currentTimestamp(); + external ExpressionJsImpl timestampAdd( + JSAny timestamp, JSString unit, JSAny amount); + external ExpressionJsImpl timestampSubtract( + JSAny timestamp, JSString unit, JSAny amount); + external ExpressionJsImpl timestampTruncate(JSAny timestamp, JSString unit, + [JSString? timezone]); + external ExpressionJsImpl abs(JSAny expr); + external ExpressionJsImpl arrayLength(JSAny array); + external ExpressionJsImpl arraySum(JSAny expression); + external ExpressionJsImpl arrayConcat(JSAny first, JSAny second); + external ExpressionJsImpl array(JSArray elements); + external ExpressionJsImpl map(JSObject keyValuePairs); + external ExpressionJsImpl rand(); + + @JS('isType') + external JSAny isTypeExpr(JSAny expression, JSAny typeValue); + + // --- Ordering (for sort stage) --- + external JSAny ascending(JSAny expr); + external JSAny descending(JSAny expr); + + // --- Aggregates --- + external AggregateFunctionJsImpl sum(JSAny expr); + external AggregateFunctionJsImpl average(JSAny expr); + external AggregateFunctionJsImpl count(JSAny expr); + external AggregateFunctionJsImpl countDistinct(JSAny expr); + external AggregateFunctionJsImpl minimum(JSAny expr); + external AggregateFunctionJsImpl maximum(JSAny expr); + external AggregateFunctionJsImpl first(JSAny expr); + external AggregateFunctionJsImpl last(JSAny expr); + external AggregateFunctionJsImpl arrayAgg(JSAny expr); + external AggregateFunctionJsImpl arrayAggDistinct(JSAny expr); + external AggregateFunctionJsImpl countAll(); + + // --- Aliased (for select/addFields/aggregate output names) --- + external JSAny aliased(JSAny expr, JSString alias); +} + +/// Aggregate function (result of sum(), average(), count(), etc. on pipelines). +/// Has .as(alias) to create an aliased aggregate for accumulators. +extension type AggregateFunctionJsImpl._(JSObject _) implements JSObject { + @JS('as') + external JSAny asAlias(JSString alias); +} + +extension type ExpressionJsImpl._(JSObject _) implements JSObject { + @JS('as') + external JSAny asAlias(JSString alias); + + external ExpressionJsImpl add(JSAny right); + external ExpressionJsImpl subtract(JSAny right); + external ExpressionJsImpl multiply(JSAny right); + external ExpressionJsImpl divide(JSAny right); + @JS('mod') + external ExpressionJsImpl modulo(JSAny right); + external ExpressionJsImpl length(); + external ExpressionJsImpl concat(JSAny right); + external ExpressionJsImpl toLower(); + external ExpressionJsImpl toUpper(); + external ExpressionJsImpl trim(); + external ExpressionJsImpl arrayReverse(); + external ExpressionJsImpl asBoolean(); + external ExpressionJsImpl isError(); + external ExpressionJsImpl isAbsent(); + + external ExpressionJsImpl regexFind(JSAny pattern); + external ExpressionJsImpl regexFindAll(JSAny pattern); + external ExpressionJsImpl stringReplaceOne(JSAny find, JSAny replacement); + external ExpressionJsImpl stringIndexOf(JSAny search); + external ExpressionJsImpl stringRepeat(JSAny repetitions); + external ExpressionJsImpl ltrim([JSAny? valueToTrim]); + external ExpressionJsImpl rtrim([JSAny? valueToTrim]); + external ExpressionJsImpl type(); + external ExpressionJsImpl trunc([JSAny? decimals]); + external ExpressionJsImpl arrayFirst(); + external ExpressionJsImpl arrayFirstN(JSAny n); + external ExpressionJsImpl arrayLast(); + external ExpressionJsImpl arrayLastN(JSAny n); + external ExpressionJsImpl arrayMaximum(); + external ExpressionJsImpl arrayMaximumN(JSAny n); + external ExpressionJsImpl arrayMinimum(); + external ExpressionJsImpl arrayMinimumN(JSAny n); + external ExpressionJsImpl arrayIndexOf(JSAny element); + external ExpressionJsImpl arrayLastIndexOf(JSAny element); + external ExpressionJsImpl arrayIndexOfAll(JSAny element); + external ExpressionJsImpl arraySlice(JSAny offset, [JSAny? length]); + external ExpressionJsImpl arrayFilter(JSString alias, JSAny filter); + external ExpressionJsImpl arrayTransform( + JSString elementAlias, JSAny transform); + external ExpressionJsImpl arrayTransformWithIndex( + JSString elementAlias, JSString indexAlias, JSAny transform); + external ExpressionJsImpl mapSet(JSAny key, JSAny value); + external ExpressionJsImpl mapEntries(); +} + +extension type SelectableJsImpl._(JSObject _) implements JSObject { + @JS('as') + external JSAny asAlias(JSString alias); +} + +/// Aliased aggregate for use in aggregate() stage accumulators. +/// Mirrors Firebase JS SDK: constructor(aggregate, alias, _methodName?). +@JS('AliasedAggregate') +@staticInterop +abstract class AliasedAggregateJsImpl { + external factory AliasedAggregateJsImpl( + JSAny aggregate, + JSString alias, [ + JSString? methodName, + ]); +} + +/// Options for the aggregate() pipeline stage. +/// Mirrors Firebase JS SDK AggregateStageOptions: { accumulators, groups? }. +extension type AggregateStageOptionsJsImpl._(JSObject _) implements JSObject { + AggregateStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set accumulators(JSAny value); + // ignore: avoid_setters_without_getters + external set groups(JSAny value); +} + +extension type SelectStageOptionsJsImpl._(JSObject _) implements JSObject { + SelectStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set selections(JSArray value); +} + +extension type AddFieldsOptionsJsImpl._(JSObject _) implements JSObject { + AddFieldsOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set fields(JSAny value); } extension type WriteBatchJsImpl._(JSObject _) implements JSObject { @@ -1014,3 +1208,161 @@ extension type AggregateQuerySnapshotJsImpl._(JSObject _) implements JSObject { @JS() @staticInterop abstract class PersistentCacheIndexManager {} + +/// Entry point for defining the data source of a Firestore Pipeline. +/// Use .collection(), .collectionGroup(), .database(), or .documents(). +extension type PipelineSourceJsImpl._(JSObject _) implements JSObject { + /// Returns all documents from the entire collection (can be nested). + external PipelineJsImpl collection(JSString collectionPath); + + /// Returns all documents from a collection ID regardless of parent. + external PipelineJsImpl collectionGroup(JSString collectionId); + + /// Returns all documents from the entire database. + external PipelineJsImpl database(); + + /// Sets the pipeline source to the given document paths or references. + external PipelineJsImpl documents(JSArray docs); +} + +/// Pipeline returned by PipelineSource methods; chain stages and call execute(). +/// See: https://firebase.google.com/docs/reference/js/firestore_pipelines.pipeline +extension type PipelineJsImpl._(JSObject _) implements JSObject { + external PipelineJsImpl limit(JSNumber limit); + external PipelineJsImpl offset(JSNumber offset); + external PipelineJsImpl where(JSAny condition); + external PipelineJsImpl sort(JSAny orderingOrOptions); + external PipelineJsImpl addFields(JSAny fieldOrOptions); + external PipelineJsImpl select(JSAny selectionOrOptions); + external PipelineJsImpl distinct(JSAny groupOrOptions); + external PipelineJsImpl aggregate(AggregateStageOptionsJsImpl options); + external PipelineJsImpl sample(JSAny documentsOrOptions); + external PipelineJsImpl unnest(JSAny selectableOrOptions); + external PipelineJsImpl removeFields(JSAny fieldOrOptions); + external PipelineJsImpl replaceWith(JSAny fieldNameOrOptions); + external PipelineJsImpl findNearest(JSAny options); + external PipelineJsImpl search(JSAny options); + external PipelineJsImpl union(JSAny otherOrOptions); + external PipelineJsImpl rawStage(JSString name, JSArray params, + [JSAny? options]); +} + +/// Options for pipeline execution (e.g. index mode). +@anonymous +@JS() +@staticInterop +abstract class PipelineExecuteOptions { + external factory PipelineExecuteOptions({JSString? indexMode}); +} + +extension PipelineExecuteOptionsExtension on PipelineExecuteOptions { + external JSString? get indexMode; + external set indexMode(JSString? v); +} + +/// Snapshot of pipeline execution results. +extension type PipelineSnapshotJsImpl._(JSObject _) implements JSObject { + /// Array of [PipelineResultJsImpl]. + external JSArray get results; + + /// Execution time (if provided by SDK). + external JSAny? get executionTime; +} + +/// Single result in a pipeline snapshot (document + data). +extension type PipelineResultJsImpl._(JSObject _) implements JSObject { + external DocumentReferenceJsImpl? get ref; + external JSObject? data(); + external JSAny? get createTime; + external JSAny? get updateTime; +} + +extension type SampleStageOptionsJsImpl._(JSObject _) implements JSObject { + SampleStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set documents(JSAny value); + // ignore: avoid_setters_without_getters + external set percentage(JSAny value); +} + +extension type SortStageOptionsJsImpl._(JSObject _) implements JSObject { + SortStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set orderings(JSAny value); +} + +extension type DistinctStageOptionsJsImpl._(JSObject _) implements JSObject { + DistinctStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set groups(JSAny value); +} + +extension type UnnestStageOptionsJsImpl._(JSObject _) implements JSObject { + UnnestStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set selectable(JSAny value); + // ignore: avoid_setters_without_getters + external set indexField(JSString? value); +} + +extension type RemoveFieldsStageOptionsJsImpl._(JSObject _) + implements JSObject { + RemoveFieldsStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set fields(JSArray value); +} + +extension type ReplaceWithStageOptionsJsImpl._(JSObject _) implements JSObject { + ReplaceWithStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set map(JSAny value); +} + +extension type FindNearestStageOptionsJsImpl._(JSObject _) implements JSObject { + FindNearestStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set field(JSAny value); + // ignore: avoid_setters_without_getters + external set vectorValue(JSAny value); + // ignore: avoid_setters_without_getters + external set distanceMeasure(JSString value); + // ignore: avoid_setters_without_getters + external set limit(JSNumber value); + // ignore: avoid_setters_without_getters + external set distanceField(JSString value); +} + +extension type SearchStageOptionsJsImpl._(JSObject _) implements JSObject { + SearchStageOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set query(JSAny value); + // ignore: avoid_setters_without_getters + external set languageCode(JSString value); + // ignore: avoid_setters_without_getters + external set retrievalDepth(JSNumber value); + // ignore: avoid_setters_without_getters + external set sort(JSAny value); + // ignore: avoid_setters_without_getters + external set offset(JSNumber value); + // ignore: avoid_setters_without_getters + external set limit(JSNumber value); + // ignore: avoid_setters_without_getters + external set addFields(JSAny value); +} + +extension type PipelineExecuteOptionsJsImpl._(JSObject _) implements JSObject { + PipelineExecuteOptionsJsImpl() : this._(JSObject.new()); + + // ignore: avoid_setters_without_getters + external set indexMode(JSString value); + // ignore: avoid_setters_without_getters + external set pipeline(JSAny value); +} diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_builder_web.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_builder_web.dart new file mode 100644 index 000000000000..81e181372491 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_builder_web.dart @@ -0,0 +1,139 @@ +// ignore_for_file: require_trailing_commas +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore_web/src/interop/firestore_interop.dart' + as interop; +import 'package:cloud_firestore_web/src/pipeline_expression_parser_web.dart'; +import 'package:firebase_core/firebase_core.dart'; + +/// Builds a JS Pipeline from serialized [stages] and returns it ready to execute. +/// Keeps [executePipeline] thin: build → execute → convert. +interop.PipelineJsImpl buildPipelineFromStages( + interop.FirestoreJsImpl jsFirestore, + List> stages, +) { + final source = jsFirestore.pipeline(); + final first = stages.first; + final stageName = first['stage'] as String?; + + // Build source stage + interop.PipelineJsImpl pipeline = _applySourceStage( + source as interop.PipelineSourceJsImpl, jsFirestore, stageName, first); + + final converter = PipelineExpressionParserWeb(interop.pipelines, jsFirestore); + + // Apply remaining stages + for (var i = 1; i < stages.length; i++) { + pipeline = _applyStage(pipeline, stages[i], converter, jsFirestore); + } + return pipeline; +} + +interop.PipelineJsImpl _applySourceStage( + interop.PipelineSourceJsImpl source, + interop.FirestoreJsImpl jsFirestore, + String? stageName, + Map first, +) { + final args = first['args']; + return switch (stageName) { + 'collection' => source + .collection(((args as Map)['path']! as String).toJS), + 'collection_group' => source.collectionGroup( + ((args as Map)['path']! as String).toJS), + 'database' => source.database(), + 'documents' => source.documents( + (args as List) + .map((e) => (e as Map)['path']! as String) + .map((p) => interop.doc(jsFirestore as JSAny, p.toJS)) + .toList() + .toJS, + ), + _ => throw UnsupportedError( + 'Pipeline source stage "$stageName" is not supported on web.'), + }; +} + +interop.PipelineJsImpl _applyStage( + interop.PipelineJsImpl pipeline, + Map stage, + PipelineExpressionParserWeb converter, + interop.FirestoreJsImpl jsFirestore, +) { + final name = stage['stage'] as String?; + final args = stage['args']; + final map = args is Map ? args : {}; + + switch (name) { + case 'limit': + final limit = map['limit'] as int; + return pipeline.limit(limit.toJS); + case 'offset': + final offset = map['offset'] as int; + return pipeline.offset(offset.toJS); + case 'where': + final expression = map['expression']; + if (expression == null) return pipeline; + final condition = + converter.toBooleanExpression(expression as Map); + if (condition == null) { + throw UnsupportedError( + 'Pipeline where() on web: could not parse the condition expression.', + ); + } + return pipeline.where(condition); + case 'sort': + final orderings = map['orderings'] as List?; + if (orderings == null || orderings.isEmpty) return pipeline; + return pipeline.sort(converter.toSortOptions(orderings)); + case 'add_fields': + final expressions = map['expressions'] as List?; + if (expressions == null || expressions.isEmpty) return pipeline; + return pipeline.addFields(converter.toAddFieldsOptions(expressions)); + case 'select': + final expressions = map['expressions'] as List?; + if (expressions == null || expressions.isEmpty) return pipeline; + return pipeline.select(converter.toSelectOptions(expressions)); + case 'distinct': + final expressions = map['expressions'] as List?; + if (expressions == null || expressions.isEmpty) return pipeline; + return pipeline.distinct(converter.toDistinctOptions(expressions)); + case 'aggregate': + return pipeline.aggregate(converter.toAggregateOptionsFromFunctions(map)); + case 'aggregate_with_options': + return pipeline + .aggregate(converter.toAggregateOptionsFromStageAndOptions(map)); + case 'sample': + return pipeline.sample(converter.toSampleOptions(args)); + case 'unnest': + return pipeline.unnest(converter.toUnnestOptions(map)); + case 'remove_fields': + final fieldPaths = map['field_paths'] as List?; + if (fieldPaths == null || fieldPaths.isEmpty) return pipeline; + return pipeline.removeFields(converter.toRemoveFieldsOptions(fieldPaths)); + case 'replace_with': + final expression = map['expression']; + if (expression == null) return pipeline; + return pipeline.replaceWith( + converter.toReplaceWithOptions(expression as Map)); + case 'find_nearest': + return pipeline.findNearest(converter.toFindNearestOptions(map)); + case 'search': + return pipeline.search(converter.toSearchOptions(map)); + case 'union': + final pipelineStages = map['pipeline'] as List>; + final otherPipeline = + buildPipelineFromStages(jsFirestore, pipelineStages); + return pipeline.union(otherPipeline); + default: + throw FirebaseException( + plugin: 'cloud_firestore', + code: 'unknown-pipeline-stage', + message: 'Unknown pipeline stage: $name', + ); + } +} diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_expression_parser_web.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_expression_parser_web.dart new file mode 100644 index 000000000000..8fc7ce67d944 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_expression_parser_web.dart @@ -0,0 +1,930 @@ +// ignore_for_file: require_trailing_commas +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:typed_data'; + +import 'package:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart' + show Blob, GeoPoint, Timestamp, VectorValue; +import 'package:cloud_firestore_web/src/interop/firestore_interop.dart' + as interop; +import 'package:cloud_firestore_web/src/interop/utils/utils.dart'; +import 'package:firebase_core/firebase_core.dart'; + +/// Converts Dart serialized pipeline expressions/stage args into JS pipeline +/// types by calling the pipelines interop API (field, constant, equal, and, +/// ascending, etc.) that mirrors the Firebase JS SDK. +class PipelineExpressionParserWeb { + PipelineExpressionParserWeb(this._pipelines, this._jsFirestore); + + final interop.PipelinesJsImpl _pipelines; + final interop.FirestoreJsImpl _jsFirestore; + + static const _kName = 'name'; + static const _kArgs = 'args'; + static const _kLeft = 'left'; + static const _kRight = 'right'; + static const _kExpression = 'expression'; + static const _kField = 'field'; + static const _kAlias = 'alias'; + static const _kValue = 'value'; + + // ── Value expressions ───────────────────────────────────────────────────── + + /// Converts a serialized value expression to a JS Expression. + interop.ExpressionJsImpl toExpression(Map map) { + final name = map[_kName] as String?; + final argsMap = _argsOf(map); + switch (name) { + case 'field': + return _pipelines.field(((argsMap[_kField] as String?) ?? '').toJS); + case 'add': + return _binaryArithmetic(argsMap, (l, r) => l.add(r)); + case 'subtract': + return _binaryArithmetic(argsMap, (l, r) => l.subtract(r)); + case 'multiply': + return _binaryArithmetic(argsMap, (l, r) => l.multiply(r)); + case 'divide': + return _binaryArithmetic(argsMap, (l, r) => l.divide(r)); + case 'modulo': + return _binaryArithmetic(argsMap, (l, r) => l.modulo(r)); + case 'constant': + case 'null': + return _pipelines.constant(_constantValueToJs(argsMap[_kValue])); + case 'concat': + final expressions = argsMap['expressions'] as List?; + if (expressions == null || expressions.isEmpty) { + throw UnsupportedError('concat requires at least one expression'); + } + interop.ExpressionJsImpl result = + toExpression(expressions[0] as Map); + for (var i = 1; i < expressions.length; i++) { + result = result + .concat(toExpression(expressions[i] as Map)); + } + return result; + case 'length': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .length(); + case 'to_lower_case': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .toLower(); + case 'to_upper_case': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .toUpper(); + case 'trim': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .trim(); + case 'as_boolean': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .asBoolean(); + case 'is_error': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .isError(); + case 'is_absent': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .isAbsent(); + case 'substring': + return _pipelines.substring( + _expr(argsMap, _kExpression), + _expr(argsMap, 'start'), + _expr(argsMap, 'end'), + ); + case 'string_replace_all': + return _pipelines.stringReplaceAll( + _expr(argsMap, _kExpression), + _expr(argsMap, 'find'), + _expr(argsMap, 'replacement'), + ); + case 'split': + return _pipelines.split( + _expr(argsMap, _kExpression), + _expr(argsMap, 'delimiter'), + ); + case 'join': + return _pipelines.join( + _expr(argsMap, _kExpression), + _expr(argsMap, 'delimiter'), + ); + case 'if_absent': + return _pipelines.ifAbsent( + _expr(argsMap, _kExpression), + _expr(argsMap, 'else'), + ); + case 'if_error': + return _pipelines.ifError( + _expr(argsMap, _kExpression), + _expr(argsMap, 'catch'), + ); + case 'conditional': + return _pipelines.conditional( + toBooleanExpression(argsMap['condition'] as Map)!, + _expr(argsMap, 'then'), + _expr(argsMap, 'else'), + ); + case 'document_id': + final pathArg = argsMap[_kExpression]; + return _pipelines + .documentId(toExpression(pathArg as Map)); + case 'document_id_from_ref': + final path = argsMap['doc_ref'] as String?; + if (path == null || path.isEmpty) { + throw ArgumentError( + "document_id_from_ref requires a non-empty 'doc_ref' path"); + } + final docRef = interop.doc(_jsFirestore as JSAny, path.toJS); + return _pipelines.documentId(docRef); + case 'collection_id': + return _pipelines.collectionId(_expr(argsMap, _kExpression)); + case 'map_get': + { + final keyExprMap = argsMap['key'] as Map?; + if (keyExprMap == null) { + throw ArgumentError("map_get requires a 'key' argument"); + } + final keyString = _constantStringFromExpression(keyExprMap); + if (keyString == null) { + throw UnsupportedError( + 'mapGet on web only supports a constant string key '); + } + return _pipelines.mapGet( + _expr(argsMap, 'map'), + keyString.toJS, + ); + } + case 'map_keys': + return _pipelines.mapKeys(_expr(argsMap, _kExpression)); + case 'map_values': + return _pipelines.mapValues(_expr(argsMap, _kExpression)); + case 'current_timestamp': + return _pipelines.currentTimestamp(); + case 'timestamp_add': + return _pipelines.timestampAdd( + _expr(argsMap, 'timestamp'), + (argsMap['unit'] as String).toJS, + _expr(argsMap, 'amount'), + ); + case 'timestamp_subtract': + return _pipelines.timestampSubtract( + _expr(argsMap, 'timestamp'), + (argsMap['unit'] as String).toJS, + _expr(argsMap, 'amount'), + ); + case 'timestamp_truncate': + final tz = argsMap['timezone'] as String?; + return _pipelines.timestampTruncate( + _expr(argsMap, 'timestamp'), + (argsMap['unit'] as String).toJS, + tz?.toJS, + ); + case 'abs': + return _pipelines.abs(_expr(argsMap, _kExpression)); + case 'array_length': + return _pipelines.arrayLength(_expr(argsMap, _kExpression)); + case 'array_sum': + return _pipelines.arraySum(_expr(argsMap, _kExpression)); + case 'array_concat': + return _pipelines.arrayConcat( + _expr(argsMap, 'first'), + _expr(argsMap, 'second'), + ); + case 'array_concat_multiple': + final arrays = argsMap['arrays'] as List?; + if (arrays == null || arrays.length < 2) { + throw UnsupportedError( + 'array_concat_multiple requires at least two arrays'); + } + var arrResult = _pipelines.arrayConcat( + toExpression(arrays[0] as Map), + toExpression(arrays[1] as Map), + ); + for (var i = 2; i < arrays.length; i++) { + arrResult = _pipelines.arrayConcat( + arrResult, + toExpression(arrays[i] as Map), + ); + } + return arrResult; + case 'array_reverse': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayReverse(); + case 'array': + final elements = argsMap['elements'] as List?; + if (elements == null) { + throw UnsupportedError('array requires elements'); + } + final jsElements = elements + .map((e) => toExpression(e as Map)) + .toList() + .toJS; + return _pipelines.array(jsElements); + case 'array_contains': + return _jsArrayContains(argsMap) as interop.ExpressionJsImpl; + case 'array_contains_any': + final js = _jsArrayContainsAny(argsMap); + if (js == null) { + throw UnsupportedError('array_contains_any requires values'); + } + return js as interop.ExpressionJsImpl; + case 'array_contains_all': + final js = _jsArrayContainsAll(argsMap); + if (js == null) { + throw UnsupportedError( + 'array_contains_all requires values or array_expression', + ); + } + return js as interop.ExpressionJsImpl; + case 'equal_any': + final js = _jsEqualAny(argsMap); + if (js == null) throw UnsupportedError('equal_any requires values'); + return js as interop.ExpressionJsImpl; + case 'not_equal_any': + final js = _jsNotEqualAny(argsMap); + if (js == null) { + throw UnsupportedError('not_equal_any requires values'); + } + return js as interop.ExpressionJsImpl; + case 'map': + final data = argsMap['data'] as Map?; + if (data == null) { + throw UnsupportedError('map requires data'); + } + final m = {}; + for (final entry in data.entries) { + m[entry.key] = toExpression(entry.value as Map); + } + return _pipelines.map(jsify(m)! as JSObject); + case 'map_set': + final kv = argsMap['key_values'] as List?; + if (kv == null || kv.isEmpty || kv.length.isOdd) { + throw UnsupportedError( + 'map_set requires key_values with even length'); + } + var cur = _expr(argsMap, 'map') as interop.ExpressionJsImpl; + for (var i = 0; i < kv.length; i += 2) { + cur = cur.mapSet( + toExpression(kv[i] as Map), + toExpression(kv[i + 1] as Map), + ); + } + return cur; + case 'map_entries': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .mapEntries(); + case 'regex_find': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .regexFind(_expr(argsMap, 'pattern')); + case 'regex_find_all': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .regexFindAll(_expr(argsMap, 'pattern')); + case 'string_replace_one': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .stringReplaceOne( + _expr(argsMap, 'find'), + _expr(argsMap, 'replacement'), + ); + case 'string_index_of': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .stringIndexOf(_expr(argsMap, 'search')); + case 'string_repeat': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .stringRepeat(_expr(argsMap, 'repetitions')); + case 'ltrim': + { + final expr = _expr(argsMap, _kExpression) as interop.ExpressionJsImpl; + final v = argsMap['value']; + if (v == null) return expr.ltrim(); + return expr.ltrim(toExpression(v as Map)); + } + case 'rtrim': + { + final expr = _expr(argsMap, _kExpression) as interop.ExpressionJsImpl; + final v = argsMap['value']; + if (v == null) return expr.rtrim(); + return expr.rtrim(toExpression(v as Map)); + } + case 'type': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .type(); + case 'trunc': + { + final expr = _expr(argsMap, _kExpression) as interop.ExpressionJsImpl; + final dec = argsMap['decimals']; + if (dec == null) return expr.trunc(); + return expr.trunc(toExpression(dec as Map)); + } + case 'rand': + return _pipelines.rand(); + case 'array_first': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayFirst(); + case 'array_first_n': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayFirstN(_expr(argsMap, 'n')); + case 'array_last': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayLast(); + case 'array_last_n': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayLastN(_expr(argsMap, 'n')); + case 'maximum': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayMaximum(); + case 'maximum_n': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayMaximumN(_expr(argsMap, 'n')); + case 'minimum': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayMinimum(); + case 'minimum_n': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayMinimumN(_expr(argsMap, 'n')); + case 'array_index_of': + { + final base = _expr(argsMap, _kExpression) as interop.ExpressionJsImpl; + final el = _expr(argsMap, 'element'); + final occMap = argsMap['occurrence'] as Map?; + final occ = _constantStringFromExpression(occMap ?? {}); + if (occ == 'last') { + return base.arrayLastIndexOf(el); + } + if (occ == 'first') { + return base.arrayIndexOf(el); + } + throw UnsupportedError( + 'array_index_of requires occurrence constant first or last', + ); + } + case 'array_index_of_all': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayIndexOfAll(_expr(argsMap, 'element')); + case 'array_slice': + { + final base = _expr(argsMap, _kExpression) as interop.ExpressionJsImpl; + final length = argsMap['length']; + if (length == null) { + return base.arraySlice(_expr(argsMap, 'offset')); + } + return base.arraySlice( + _expr(argsMap, 'offset'), + toExpression(length as Map), + ); + } + case 'array_filter': + { + final filter = + toBooleanExpression(argsMap['filter'] as Map); + if (filter == null) { + throw UnsupportedError('array_filter requires a boolean filter'); + } + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayFilter((argsMap['alias'] as String).toJS, filter); + } + case 'array_transform': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayTransform( + (argsMap['element_alias'] as String).toJS, + _expr(argsMap, 'transform'), + ); + case 'array_transform_with_index': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .arrayTransformWithIndex( + (argsMap['element_alias'] as String).toJS, + (argsMap['index_alias'] as String).toJS, + _expr(argsMap, 'transform'), + ); + default: + throw FirebaseException( + plugin: 'cloud_firestore', + code: 'unsupported-expression', + message: + "The pipeline expression '$name' is not supported on the web " + 'platform. The Firebase JS SDK may not expose this expression.', + ); + } + } + + // ── Boolean expressions ─────────────────────────────────────────────────── + + /// Converts a serialized boolean expression to a JS BooleanExpression. + /// + /// Returns null if [map] is not a recognized boolean expression. + JSAny? toBooleanExpression(Map map) { + final name = map[_kName] as String?; + final argsMap = _argsOf(map); + switch (name) { + case 'equal': + return _pipelines.equal( + _expr(argsMap, _kLeft), _expr(argsMap, _kRight)); + case 'not_equal': + return _pipelines.notEqual( + _expr(argsMap, _kLeft), _expr(argsMap, _kRight)); + case 'greater_than': + return _pipelines.greaterThan( + _expr(argsMap, _kLeft), _expr(argsMap, _kRight)); + case 'greater_than_or_equal': + return _pipelines.greaterThanOrEqual( + _expr(argsMap, _kLeft), _expr(argsMap, _kRight)); + case 'less_than': + return _pipelines.lessThan( + _expr(argsMap, _kLeft), _expr(argsMap, _kRight)); + case 'less_than_or_equal': + return _pipelines.lessThanOrEqual( + _expr(argsMap, _kLeft), _expr(argsMap, _kRight)); + case 'and': + case 'or': + case 'xor': + final exprMaps = argsMap['expressions'] as List?; + if (exprMaps == null || exprMaps.isEmpty) return null; + final exprs = exprMaps + .map((e) => toBooleanExpression(e as Map)) + .whereType() + .toList(); + if (exprs.isEmpty) return null; + var result = exprs.first; + for (var i = 1; i < exprs.length; i++) { + if (name == 'and') { + result = _pipelines.and(result, exprs[i]); + } else if (name == 'or') { + result = _pipelines.or(result, exprs[i]); + } else { + result = _pipelines.xor(result, exprs[i]); + } + } + return result; + case 'not': + final expr = argsMap[_kExpression] as Map; + final boolExpr = toBooleanExpression(expr); + if (boolExpr == null) { + throw UnsupportedError('not requires a boolean expression'); + } + return _pipelines.not(boolExpr); + case 'document_matches': + final query = argsMap['query'] as String?; + if (query == null) { + throw UnsupportedError('document_matches requires query'); + } + return _pipelines.documentMatches(query.toJS); + case 'exists': + return _pipelines.exists(_expr(argsMap, _kExpression)); + case 'is_absent': + return (_expr(argsMap, _kExpression) as interop.ExpressionJsImpl) + .isAbsent(); + case 'is_error': + return _pipelines.isError(_expr(argsMap, _kExpression)); + case 'array_contains': + return _jsArrayContains(argsMap); + case 'array_contains_any': + return _jsArrayContainsAny(argsMap); + case 'array_contains_all': + return _jsArrayContainsAll(argsMap); + case 'equal_any': + return _jsEqualAny(argsMap); + case 'not_equal_any': + return _jsNotEqualAny(argsMap); + case 'filter': + return _buildFilterExpression(argsMap); + case 'is_type': + final typeStr = argsMap['type'] as String?; + if (typeStr == null) { + throw UnsupportedError("is_type requires string 'type'"); + } + return _pipelines.isTypeExpr( + _expr(argsMap, _kExpression), + typeStr.toJS, + ); + default: + throw FirebaseException( + plugin: 'cloud_firestore', + code: 'unsupported-boolean-expression', + message: "The boolean expression '$name' is not supported on the web " + 'platform. The Firebase JS SDK may not expose this expression.', + ); + } + } + + // ── Stage options ───────────────────────────────────────────────────────── + + /// Converts orderings list to JS SortStageOptions. + /// + /// Each item shape: `{ expression: Map, order_direction: 'asc' | 'desc' }`. + JSAny toSortOptions(List orderings) { + final list = _toOrderingList(orderings); + if (list.isEmpty) { + throw UnsupportedError( + 'Pipeline sort() on web requires the Firebase JS pipeline expression API ' + '(ascending, descending). Ensure the pipelines module is loaded.', + ); + } + return interop.SortStageOptionsJsImpl()..orderings = list.toJS; + } + + /// Converts a single expression map to a JS Selectable (field or aliased). + JSAny toSelectable(Map map) { + final name = map[_kName] as String?; + final argsMap = _argsOf(map); + if (name == 'field') { + return _pipelines.field(((argsMap[_kField] as String?) ?? '').toJS); + } + if (name == _kAlias) { + final alias = argsMap[_kAlias] as String; + final expression = argsMap[_kExpression]; + return toExpression(expression as Map) + .asAlias(alias.toJS); + } + return toExpression(map); + } + + /// Converts add_fields expressions to JS AddFieldsStageOptions. + JSAny toAddFieldsOptions(List expressions) => + interop.AddFieldsOptionsJsImpl() + ..fields = _toSelectableList(expressions).toJS; + + /// Converts select stage expressions to JS SelectStageOptions. + JSAny toSelectOptions(List expressions) => + interop.SelectStageOptionsJsImpl() + ..selections = _toSelectableList(expressions).toJS; + + /// Converts distinct stage groups to JS DistinctStageOptions. + JSAny toDistinctOptions(List expressions) { + final list = _toSelectableList(expressions); + if (list.isEmpty) { + throw UnsupportedError( + 'Pipeline distinct() on web requires the Firebase JS pipeline expression API.', + ); + } + return interop.DistinctStageOptionsJsImpl()..groups = list.toJS; + } + + // ── Aggregate ───────────────────────────────────────────────────────────── + + /// Converts args for the 'aggregate' stage to JS AggregateStageOptions. + /// + /// Expects [map] to contain an [aggregate_functions] list. + interop.AggregateStageOptionsJsImpl toAggregateOptionsFromFunctions( + Map map) { + final list = map['aggregate_functions'] as List; + return _buildAccumulators(list); + } + + /// Converts args for the 'aggregate_with_options' stage to JS AggregateStageOptions. + /// + /// Expects [map] to contain an [aggregate_stage] map with [accumulators] + /// and optionally [groups]. + interop.AggregateStageOptionsJsImpl toAggregateOptionsFromStageAndOptions( + Map map) { + final stage = map['aggregate_stage'] as Map; + final list = stage['accumulators'] as List; + final groups = stage['groups'] as List?; + return _buildAccumulators(list, groups: groups); + } + + // ── Other stage options ─────────────────────────────────────────────────── + + /// Converts sample stage args to JS (integer count or SampleStageOptions). + /// + /// Dart serializes as `{ type: 'size', value: n }` or a raw number. + JSAny toSampleOptions(Map map) { + final args = map['type'] as String; + if (args == 'size') { + final value = map['value'] as num; + return interop.SampleStageOptionsJsImpl()..documents = value.toInt().toJS; + } else { + final value = map['value'] as num; + return interop.SampleStageOptionsJsImpl() + ..percentage = value.toDouble().toJS; + } + } + + /// Converts unnest stage args to JS UnnestStageOptions. + JSAny toUnnestOptions(Map map) { + final expression = map[_kExpression] as Map; + final indexField = map['index_field'] as String?; + final sel = toSelectable(expression); + return interop.UnnestStageOptionsJsImpl() + ..selectable = sel + ..indexField = indexField?.toJS; + } + + /// Converts remove_fields field paths to JS RemoveFieldsStageOptions. + JSAny toRemoveFieldsOptions(List fieldPaths) { + final paths = []; + for (final e in fieldPaths) { + final s = e is String + ? e + : (e is Map ? e[_kField] ?? e['path'] : null)?.toString(); + if (s != null) paths.add(s.toJS); + } + return interop.RemoveFieldsStageOptionsJsImpl()..fields = paths.toJS; + } + + /// Converts replace_with expression to JS ReplaceWithStageOptions. + JSAny toReplaceWithOptions(Map expression) { + return interop.ReplaceWithStageOptionsJsImpl() + ..map = toExpression(expression); + } + + /// Converts find_nearest args to JS FindNearestStageOptions. + interop.FindNearestStageOptionsJsImpl toFindNearestOptions( + Map map) { + final vectorField = + (map['vector_field'] as String?) ?? (map[_kField] as String?); + final vectorValue = map['vector_value'] as List?; + final distanceMeasure = (map['distance_measure'] as String?) ?? 'cosine'; + final limit = map['limit'] as int?; + final distanceField = map['distance_field'] as String?; + if (vectorField == null || vectorValue == null) { + throw UnsupportedError( + 'Pipeline findNearest() on web requires vector_field and vector_value.', + ); + } + final doubles = vectorValue.map((e) => (e as num).toDouble()).toList(); + final opts = interop.FindNearestStageOptionsJsImpl() + ..field = _pipelines.field(vectorField.toJS) + ..vectorValue = interop.vector(doubles.jsify()! as JSArray) + ..distanceMeasure = distanceMeasure.toJS; + if (limit != null) opts.limit = limit.toJS; + if (distanceField != null) opts.distanceField = distanceField.toJS; + return opts; + } + + /// Converts search stage args to JS SearchStageOptions. + interop.SearchStageOptionsJsImpl toSearchOptions(Map map) { + final queryType = map['query_type'] as String?; + final query = map['query']; + final opts = interop.SearchStageOptionsJsImpl(); + + if (queryType == 'string') { + opts.query = (query as String).toJS; + } else if (queryType == 'expression') { + opts.query = toBooleanExpression(query as Map)!; + } else { + throw UnsupportedError( + "Pipeline search() on web requires query_type 'string' or 'expression'.", + ); + } + + final languageCode = map['language_code'] as String?; + if (languageCode != null) opts.languageCode = languageCode.toJS; + + final retrievalDepth = map['retrieval_depth'] as int?; + if (retrievalDepth != null) opts.retrievalDepth = retrievalDepth.toJS; + + final offset = map['offset'] as int?; + if (offset != null) opts.offset = offset.toJS; + + final limit = map['limit'] as int?; + if (limit != null) opts.limit = limit.toJS; + + final sort = map['sort'] as List?; + if (sort != null && sort.isNotEmpty) { + final orderings = _toOrderingList(sort); + if (orderings.isNotEmpty) opts.sort = orderings.toJS; + } + + final addFields = map['add_fields'] as List?; + if (addFields != null && addFields.isNotEmpty) { + opts.addFields = _toSelectableList(addFields).toJS; + } + + return opts; + } + + // ── Private helpers ─────────────────────────────────────────────────────── + + /// Converts a [Constant] value to the correct JS type for the pipelines API. + /// + /// Each Dart type accepted by [Constant] is mapped to the corresponding + /// Firestore JS SDK interop type so that the JS SDK receives a properly typed + /// value (e.g. a JS `Timestamp`, `GeoPoint`, or `Bytes` object) rather than + /// a plain JS primitive or an unrecognised object. + JSAny? _constantValueToJs(Object? value) { + if (value == null) return null; + if (value is String) return value.toJS; + if (value is bool) return value.toJS; + if (value is int) return value.toJS; + if (value is double) return value.toJS; + if (value is DateTime) { + return interop.TimestampJsImpl.fromMillis( + value.millisecondsSinceEpoch.toJS) as JSAny; + } + + if (value is Timestamp) { + // Use seconds + nanoseconds directly to preserve sub-millisecond precision. + return interop.TimestampJsImpl(value.seconds.toJS, value.nanoseconds.toJS) + as JSAny; + } + if (value is GeoPoint) { + return interop.GeoPointJsImpl(value.latitude.toJS, value.longitude.toJS) + as JSAny; + } + if (value is Blob) { + return interop.BytesJsImpl.fromUint8Array(value.bytes.toJS) as JSAny; + } + if (value is List) { + return interop.BytesJsImpl.fromUint8Array(Uint8List.fromList(value).toJS) + as JSAny; + } + if (value is VectorValue) { + return interop.vector(value.toArray().jsify()! as JSArray) as JSAny; + } + if (value is Map) { + final path = value['path'] as String; + return interop.doc(_jsFirestore as JSAny, path.toJS) as JSAny; + } + return jsify(value); + } + + /// Extracts and safe-casts the 'args' sub-map from an expression map. + static Map _argsOf(Map map) { + final a = map[_kArgs]; + return a is Map ? a : const {}; + } + + /// Returns the string value if [exprMap] is a constant string expression; + /// otherwise null. + String? _constantStringFromExpression(Map exprMap) { + if ((exprMap[_kName] as String?) != 'constant') return null; + final args = _argsOf(exprMap); + final value = args[_kValue]; + return value is String ? value : null; + } + + /// Resolves [key] from [argsMap] as a value expression. + JSAny _expr(Map argsMap, String key) => + toExpression(argsMap[key] as Map); + + JSAny _jsArrayContains(Map argsMap) => + _pipelines.arrayContains( + _expr(argsMap, 'array'), + _expr(argsMap, 'element'), + ); + + JSAny? _jsArrayContainsAny(Map argsMap) { + final valuesMaps = argsMap['values'] as List?; + if (valuesMaps == null || valuesMaps.isEmpty) return null; + final valuesJs = valuesMaps + .map((v) => toExpression(v as Map)) + .toList() + .toJS; + return _pipelines.arrayContainsAny(_expr(argsMap, 'array'), valuesJs); + } + + JSAny? _jsArrayContainsAll(Map argsMap) { + final arrayExpr = _expr(argsMap, 'array'); + final valuesArg = argsMap['values'] as List?; + final arrayExpressionArg = argsMap['array_expression']; + if (valuesArg != null && valuesArg.isNotEmpty) { + final valuesJs = valuesArg + .map((v) => toExpression(v as Map)) + .toList() + .toJS; + return _pipelines.arrayContainsAll(arrayExpr, valuesJs); + } + if (arrayExpressionArg != null) { + return _pipelines.arrayContainsAll( + arrayExpr, + toExpression(arrayExpressionArg as Map), + ); + } + return null; + } + + JSAny? _jsEqualAny(Map argsMap) { + final valuesMaps = argsMap['values'] as List?; + if (valuesMaps == null || valuesMaps.isEmpty) return null; + final valuesJs = valuesMaps + .map((v) => toExpression(v as Map)) + .toList() + .toJS; + return _pipelines.equalAny(_expr(argsMap, 'value'), valuesJs); + } + + JSAny? _jsNotEqualAny(Map argsMap) { + final valuesMaps = argsMap['values'] as List?; + if (valuesMaps == null || valuesMaps.isEmpty) return null; + final valuesJs = valuesMaps + .map((v) => toExpression(v as Map)) + .toList() + .toJS; + return _pipelines.notEqualAny(_expr(argsMap, 'value'), valuesJs); + } + + interop.ExpressionJsImpl _binaryArithmetic( + Map argsMap, + interop.ExpressionJsImpl Function( + interop.ExpressionJsImpl left, interop.ExpressionJsImpl right) + op, + ) => + op( + toExpression(argsMap[_kLeft] as Map), + toExpression(argsMap[_kRight] as Map), + ); + + JSAny? _buildFilterExpression(Map argsMap) { + final operator = argsMap['operator'] as String?; + final expressions = argsMap['expressions'] as List?; + if (expressions == null || expressions.isEmpty) return null; + final jsList = expressions + .map((e) => toExpression(e as Map)) + .toList(); + if (jsList.length == 1) return jsList.single; + JSAny result = jsList[0]; + for (var i = 1; i < jsList.length; i++) { + result = operator == 'and' + ? _pipelines.and(result, jsList[i]) + : _pipelines.or(result, jsList[i]); + } + return result; + } + + List _toSelectableList(List expressions) => expressions + .map((e) => + toSelectable(e is Map ? e : {})) + .whereType() + .toList(); + + List _toOrderingList(List orderings) { + final list = []; + for (final ordering in orderings) { + final orderingMap = Map.from(ordering as Map); + final expr = orderingMap[_kExpression]; + if (expr == null) continue; + final exprJs = toExpression(expr as Map); + final dir = orderingMap['order_direction'] as String?; + list.add(dir == 'desc' + ? _pipelines.descending(exprJs) + : _pipelines.ascending(exprJs)); + } + return list; + } + + interop.AggregateStageOptionsJsImpl _buildAccumulators( + List items, { + List? groups, + }) { + final accumulators = items + .map((item) => _parseAccumulator(item as Map)) + .whereType() + .toList(); + + if (accumulators.isEmpty) { + throw UnsupportedError( + 'Pipeline aggregate() on web requires at least one valid accumulator.', + ); + } + + final opts = interop.AggregateStageOptionsJsImpl() + ..accumulators = accumulators.toJS; + if (groups != null && groups.isNotEmpty) { + opts.groups = _toSelectableList(groups).toJS; + } + return opts; + } + + JSAny? _parseAccumulator(Map item) { + final args = item[_kArgs] as Map; + final alias = args[_kAlias] as String?; + final aggregateFn = args['aggregate_function'] as Map?; + if (alias == null || aggregateFn == null) return null; + final fnName = aggregateFn[_kName] as String?; + if (fnName == null) return null; + final expressionMap = (aggregateFn[_kArgs] + as Map?)?[_kExpression] as Map?; + final exprJs = expressionMap != null ? toExpression(expressionMap) : null; + return _buildAggregateFunction(fnName, exprJs)?.asAlias(alias.toJS); + } + + /// Builds one JS aggregate function from a serialized [name] and optional [exprJs]. + interop.AggregateFunctionJsImpl? _buildAggregateFunction( + String name, JSAny? exprJs) { + switch (name) { + case 'count_all': + return _pipelines.countAll(); + case 'sum': + return exprJs != null ? _pipelines.sum(exprJs) : null; + case 'average': + return exprJs != null ? _pipelines.average(exprJs) : null; + case 'count': + return exprJs != null ? _pipelines.count(exprJs) : null; + case 'count_distinct': + return exprJs != null ? _pipelines.countDistinct(exprJs) : null; + case 'minimum': + return exprJs != null ? _pipelines.minimum(exprJs) : null; + case 'maximum': + return exprJs != null ? _pipelines.maximum(exprJs) : null; + case 'first': + return exprJs != null ? _pipelines.first(exprJs) : null; + case 'last': + return exprJs != null ? _pipelines.last(exprJs) : null; + case 'array_agg': + return exprJs != null ? _pipelines.arrayAgg(exprJs) : null; + case 'array_agg_distinct': + return exprJs != null ? _pipelines.arrayAggDistinct(exprJs) : null; + default: + return null; + } + } +} diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_web.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_web.dart new file mode 100644 index 000000000000..5872ce7c5aaa --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/pipeline_web.dart @@ -0,0 +1,110 @@ +// ignore_for_file: require_trailing_commas +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. 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:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; +import 'package:cloud_firestore_web/src/interop/utils/utils.dart'; + +import 'document_reference_web.dart'; +import 'interop/firestore.dart' as firestore_interop; +import 'interop/firestore_interop.dart' as interop; + +/// Web implementation of [PipelinePlatform]. +class PipelineWeb extends PipelinePlatform { + final firestore_interop.Firestore _firestoreWeb; + + PipelineWeb( + FirebaseFirestorePlatform firestore, + this._firestoreWeb, + List>? stages, + ) : super(firestore, stages); + + @override + PipelinePlatform addStage(Map serializedStage) { + return PipelineWeb( + firestore, + _firestoreWeb, + [...stages, serializedStage], + ); + } + + @override + Future execute({ + Map? options, + }) async { + return firestore.executePipeline(stages, options: options); + } +} + +/// Web implementation of [PipelineSnapshotPlatform]. +class PipelineSnapshotWeb extends PipelineSnapshotPlatform { + PipelineSnapshotWeb(this._results, this._executionTime) : super(); + + final List _results; + final DateTime _executionTime; + + @override + List get results => _results; + + @override + DateTime get executionTime => _executionTime; +} + +/// Web implementation of [PipelineResultPlatform]. +class PipelineResultWeb extends PipelineResultPlatform { + PipelineResultWeb( + FirebaseFirestorePlatform firestore, + firestore_interop.Firestore firestoreWeb, + interop.PipelineResultJsImpl jsResult, + ) : _document = jsResult.ref != null + ? DocumentReferenceWeb( + firestore, + firestoreWeb, + jsResult.ref!.path.toDart, + ) + : null, + _createTime = _timestampToDateTime(jsResult.createTime), + _updateTime = _timestampToDateTime(jsResult.updateTime), + _data = _dataFromResult(jsResult), + super(); + + final DocumentReferencePlatform? _document; + final DateTime? _createTime; + final DateTime? _updateTime; + final Map? _data; + + static Map? _dataFromResult( + interop.PipelineResultJsImpl jsResult) { + final d = jsResult.data(); + if (d == null) return null; + final parsed = dartify(d); + return parsed != null + ? Map.from(parsed as Map) + : null; + } + + static DateTime? _timestampToDateTime(JSAny? value) { + if (value == null) return null; + final d = dartify(value); + if (d == null) return null; + if (d is DateTime) return d; + if (d is Timestamp) return d.toDate(); + if (d is int) return DateTime.fromMillisecondsSinceEpoch(d); + return null; + } + + @override + DocumentReferencePlatform? get document => _document; + + @override + DateTime? get createTime => _createTime; + + @override + DateTime? get updateTime => _updateTime; + + @override + Map? get data => _data; +} diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart index ca5f5bf5ea4b..acc3f880b6e4 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart @@ -120,7 +120,10 @@ class EncodeUtility { } else if (value == FieldPath.documentId) { return firestore_interop.documentId(); } else if (value is Timestamp) { - return value.toDate(); + return firestore_interop.TimestampJsImpl( + value.seconds.toJS, + value.nanoseconds.toJS, + ); } else if (value is GeoPoint) { return firestore_interop.GeoPointJsImpl( value.latitude.toJS, value.longitude.toJS); diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/web_utils.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/web_utils.dart index 43d3b07cb1a8..c92ef6b73fb8 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/web_utils.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/web_utils.dart @@ -67,7 +67,7 @@ DocumentSnapshotPlatform convertWebDocumentSnapshot( )), firestore, ), - PigeonSnapshotMetadata( + InternalSnapshotMetadata( hasPendingWrites: webSnapshot.metadata.hasPendingWrites.toDart, isFromCache: webSnapshot.metadata.fromCache.toDart, ), diff --git a/packages/cloud_firestore/cloud_firestore_web/pubspec.yaml b/packages/cloud_firestore/cloud_firestore_web/pubspec.yaml index d44a714a4b83..60dc877769ac 100644 --- a/packages/cloud_firestore/cloud_firestore_web/pubspec.yaml +++ b/packages/cloud_firestore/cloud_firestore_web/pubspec.yaml @@ -3,25 +3,26 @@ description: The web implementation of cloud_firestore homepage: https://github.com/firebase/flutterfire/tree/main/packages/cloud_firestore/cloud_firestore_web repository: https://github.com/firebase/flutterfire/tree/main/packages/cloud_firestore/cloud_firestore_web -version: 5.2.0 +version: 5.6.0 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 - cloud_firestore_platform_interface: ^7.1.0 + _flutterfire_internals: ^1.3.73 + cloud_firestore_platform_interface: ^8.0.3 collection: ^1.0.0 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 flutter: sdk: flutter flutter_web_plugins: sdk: flutter dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/cloud_functions/analysis_options.yaml b/packages/cloud_functions/analysis_options.yaml index 1597da232319..b8c16f67fb33 100644 --- a/packages/cloud_functions/analysis_options.yaml +++ b/packages/cloud_functions/analysis_options.yaml @@ -7,4 +7,5 @@ include: ../../analysis_options.yaml analyzer: exclude: - cloud_functions_platform_interface/lib/src/pigeon/messages.pigeon.dart - - cloud_functions_platform_interface/test/pigeon/test_api.dart \ No newline at end of file + - cloud_functions_platform_interface/test/pigeon/test_api.dart + - cloud_functions_platform_interface/pigeons/messages.dart \ No newline at end of file diff --git a/packages/cloud_functions/cloud_functions/CHANGELOG.md b/packages/cloud_functions/cloud_functions/CHANGELOG.md index c37ecfbf7a2b..06e155cfcd83 100644 --- a/packages/cloud_functions/cloud_functions/CHANGELOG.md +++ b/packages/cloud_functions/cloud_functions/CHANGELOG.md @@ -1,3 +1,24 @@ +## 6.3.3 + + - Update a dependency to the latest release. + +## 6.3.2 + + - Update a dependency to the latest release. + +## 6.3.1 + + - Update a dependency to the latest release. + +## 6.3.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 6.2.0 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 6.1.0 - **FIX**(functions,web): fix a crash that could happen with the Int64 type ([#18066](https://github.com/firebase/flutterfire/issues/18066)). ([5eed50c1](https://github.com/firebase/flutterfire/commit/5eed50c15dd29ab97934a4bd0919378f61c46f9e)) diff --git a/packages/cloud_functions/cloud_functions/android/build.gradle b/packages/cloud_functions/cloud_functions/android/build.gradle index 9d1295a9567c..0e60c55e34f4 100644 --- a/packages/cloud_functions/cloud_functions/android/build.gradle +++ b/packages/cloud_functions/cloud_functions/android/build.gradle @@ -4,14 +4,18 @@ version '1.0-SNAPSHOT' apply plugin: 'com.android.library' apply from: file("local-config.gradle") -// AGP 9+ has built-in Kotlin support; older versions need the plugin explicitly. +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int -if (agpMajor < 9) { +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { apply plugin: 'kotlin-android' } buildscript { - ext.kotlin_version = "1.8.22" + ext.kotlin_version = "2.0.0" repositories { google() mavenCentral() @@ -61,12 +65,6 @@ android { targetCompatibility project.ext.javaVersion } - if (agpMajor < 9) { - kotlinOptions { - jvmTarget = project.ext.javaVersion - } - } - sourceSets { main.java.srcDirs += "src/main/kotlin" test.java.srcDirs += "src/test/kotlin" @@ -90,4 +88,12 @@ android { } +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } + } +} + apply from: file("./user-agent.gradle") \ No newline at end of file diff --git a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FirebaseFunctionsStreamHandler.kt b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FirebaseFunctionsStreamHandler.kt index 604c8e4dfa53..32544cdd3d34 100644 --- a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FirebaseFunctionsStreamHandler.kt +++ b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FirebaseFunctionsStreamHandler.kt @@ -10,13 +10,13 @@ import com.google.firebase.functions.HttpsCallableReference import com.google.firebase.functions.StreamResponse import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel.EventSink -import org.reactivestreams.Publisher import java.net.URL import java.util.Objects import java.util.concurrent.TimeUnit +import org.reactivestreams.Publisher class FirebaseFunctionsStreamHandler(private val firebaseFunctions: FirebaseFunctions) : - EventChannel.StreamHandler { + EventChannel.StreamHandler { private var subscriber: StreamResponseSubscriber? = null override fun onListen(arguments: Any, events: EventSink) { @@ -36,7 +36,7 @@ class FirebaseFunctionsStreamHandler(private val firebaseFunctions: FirebaseFunc val timeout = arguments["timeout"] as Int? val parameters = arguments["parameters"] val limitedUseAppCheckToken = - Objects.requireNonNull(arguments["limitedUseAppCheckToken"]) as Boolean + Objects.requireNonNull(arguments["limitedUseAppCheckToken"]) as Boolean if (origin != null) { val originUri = Uri.parse(origin) @@ -44,9 +44,10 @@ class FirebaseFunctionsStreamHandler(private val firebaseFunctions: FirebaseFunc } val httpsCallableReference: HttpsCallableReference - val options: HttpsCallableOptions =HttpsCallableOptions.Builder() - .setLimitedUseAppCheckTokens(limitedUseAppCheckToken) - .build() + val options: HttpsCallableOptions = + HttpsCallableOptions.Builder() + .setLimitedUseAppCheckTokens(limitedUseAppCheckToken) + .build() val publisher: Publisher if (functionName != null) { @@ -54,7 +55,7 @@ class FirebaseFunctionsStreamHandler(private val firebaseFunctions: FirebaseFunc publisher = httpsCallableReference.stream(parameters) } else if (functionUri != null) { httpsCallableReference = - firebaseFunctions.getHttpsCallableFromUrl(URL(functionUri), options) + firebaseFunctions.getHttpsCallableFromUrl(URL(functionUri), options) publisher = httpsCallableReference.stream() } else { throw IllegalArgumentException("Either functionName or functionUri must be set") diff --git a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseAppRegistrar.kt b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseAppRegistrar.kt index 14e4ce487ebd..a704f964b124 100644 --- a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseAppRegistrar.kt +++ b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseAppRegistrar.kt @@ -12,7 +12,6 @@ import com.google.firebase.platforminfo.LibraryVersionComponent class FlutterFirebaseAppRegistrar : ComponentRegistrar { override fun getComponents(): List> { return listOf( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION) - ) + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION)) } } diff --git a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.kt b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.kt index 3705e390c0b8..f2aff1556e6d 100644 --- a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.kt +++ b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.kt @@ -25,9 +25,7 @@ import java.util.Locale import java.util.Objects import java.util.concurrent.TimeUnit -class FlutterFirebaseFunctionsPlugin - - : FlutterPlugin, FlutterFirebasePlugin, CloudFunctionsHostApi { +class FlutterFirebaseFunctionsPlugin : FlutterPlugin, FlutterFirebasePlugin, CloudFunctionsHostApi { private var channel: MethodChannel? = null private var pluginBinding: FlutterPluginBinding? = null private var messenger: BinaryMessenger? = null @@ -66,7 +64,7 @@ class FlutterFirebaseFunctionsPlugin val origin = arguments["origin"] as String? val timeout = (arguments["timeout"] as Number?)?.toInt() val limitedUseAppCheckToken = - Objects.requireNonNull(arguments["limitedUseAppCheckToken"]) as Boolean + Objects.requireNonNull(arguments["limitedUseAppCheckToken"]) as Boolean val parameters = arguments["parameters"] if (origin != null) { @@ -76,23 +74,21 @@ class FlutterFirebaseFunctionsPlugin val httpsCallableReference: HttpsCallableReference val options: HttpsCallableOptions = - HttpsCallableOptions.Builder() - .setLimitedUseAppCheckTokens(limitedUseAppCheckToken) - .build() - - httpsCallableReference = if (functionName != null) { - firebaseFunctions.getHttpsCallable(functionName, options) - } else if (functionUri != null) { - firebaseFunctions.getHttpsCallableFromUrl(URL(functionUri), options) - } else { - throw IllegalArgumentException("Either functionName or functionUri must be set") - } + HttpsCallableOptions.Builder() + .setLimitedUseAppCheckTokens(limitedUseAppCheckToken) + .build() + + httpsCallableReference = + if (functionName != null) { + firebaseFunctions.getHttpsCallable(functionName, options) + } else if (functionUri != null) { + firebaseFunctions.getHttpsCallableFromUrl(URL(functionUri), options) + } else { + throw IllegalArgumentException("Either functionName or functionUri must be set") + } if (timeout != null) { - httpsCallableReference.setTimeout( - timeout.toLong(), - TimeUnit.MILLISECONDS - ) + httpsCallableReference.setTimeout(timeout.toLong(), TimeUnit.MILLISECONDS) } val result = Tasks.await(httpsCallableReference.call(parameters)) @@ -117,21 +113,20 @@ class FlutterFirebaseFunctionsPlugin var additionalData: Any? = null if (exception.cause is FirebaseFunctionsException) { - val functionsException = - exception.cause as FirebaseFunctionsException? + val functionsException = exception.cause as FirebaseFunctionsException? code = functionsException!!.code.name message = functionsException.message additionalData = functionsException.details - if (functionsException.cause is IOException - && "Canceled" == (functionsException.cause as IOException).message - ) { + if (functionsException.cause is IOException && + "Canceled" == (functionsException.cause as IOException).message) { // return DEADLINE_EXCEEDED for IOException cancel errors, to match iOS & Web code = FirebaseFunctionsException.Code.DEADLINE_EXCEEDED.name message = FirebaseFunctionsException.Code.DEADLINE_EXCEEDED.name - } else if (functionsException.cause is InterruptedIOException // return DEADLINE_EXCEEDED for InterruptedIOException errors, to match iOS & Web - && "timeout" == (functionsException.cause as InterruptedIOException).message - ) { + } else if (functionsException.cause is + InterruptedIOException // return DEADLINE_EXCEEDED for InterruptedIOException errors, to + // match iOS & Web + && "timeout" == (functionsException.cause as InterruptedIOException).message) { code = FirebaseFunctionsException.Code.DEADLINE_EXCEEDED.name message = FirebaseFunctionsException.Code.DEADLINE_EXCEEDED.name } else if (functionsException.cause is IOException) { @@ -172,28 +167,23 @@ class FlutterFirebaseFunctionsPlugin } override fun call(arguments: Map, callback: (Result) -> Unit) { - httpsFunctionCall(arguments as Map) - .addOnCompleteListener { task -> - if (task.isSuccessful){ - callback(Result.success(task.result)) - } - else { - val exception = task.exception - callback(Result.failure(FlutterError( - "firebase_functions", - exception?.message, - getExceptionDetails(exception) - ))) - } - + httpsFunctionCall(arguments as Map).addOnCompleteListener { task -> + if (task.isSuccessful) { + callback(Result.success(task.result)) + } else { + val exception = task.exception + callback( + Result.failure( + FlutterError( + "firebase_functions", exception?.message, getExceptionDetails(exception)))) + } } } override fun registerEventChannel(arguments: Map, callback: (Result) -> Unit) { val eventId = Objects.requireNonNull(arguments["eventChannelId"]) as String val eventChannelName = "$METHOD_CHANNEL_NAME/$eventId" - val eventChannel = - EventChannel(pluginBinding!!.binaryMessenger, eventChannelName) + val eventChannel = EventChannel(pluginBinding!!.binaryMessenger, eventChannelName) val functions = getFunctions(arguments) val streamHandler = FirebaseFunctionsStreamHandler(functions) eventChannel.setStreamHandler(streamHandler) diff --git a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/GeneratedAndroidCloudFunctions.g.kt b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/GeneratedAndroidCloudFunctions.g.kt index ed7f4b3c90fc..e3cb89c46f8b 100644 --- a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/GeneratedAndroidCloudFunctions.g.kt +++ b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/GeneratedAndroidCloudFunctions.g.kt @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -10,12 +10,11 @@ package io.flutter.plugins.firebase.functions import android.util.Log import io.flutter.plugin.common.BasicMessageChannel import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec import io.flutter.plugin.common.StandardMessageCodec import java.io.ByteArrayOutputStream import java.nio.ByteBuffer + private object GeneratedAndroidCloudFunctionsPigeonUtils { fun wrapResult(result: Any?): List { @@ -24,58 +23,66 @@ private object GeneratedAndroidCloudFunctionsPigeonUtils { fun wrapError(exception: Throwable): List { return if (exception is FlutterError) { - listOf( - exception.code, - exception.message, - exception.details - ) + listOf(exception.code, exception.message, exception.details) } else { listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) } } } /** * 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 FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() + private open class GeneratedAndroidCloudFunctionsPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { - return super.readValueOfType(type, buffer) + return super.readValueOfType(type, buffer) } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { super.writeValue(stream, value) } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface CloudFunctionsHostApi { fun call(arguments: Map, callback: (Result) -> Unit) + fun registerEventChannel(arguments: Map, callback: (Result) -> Unit) companion object { /** The codec used by CloudFunctionsHostApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidCloudFunctionsPigeonCodec() - } - /** Sets up an instance of `CloudFunctionsHostApi` to handle messages through the `binaryMessenger`. */ + val codec: MessageCodec by lazy { GeneratedAndroidCloudFunctionsPigeonCodec() } + /** + * Sets up an instance of `CloudFunctionsHostApi` to handle messages through the + * `binaryMessenger`. + */ @JvmOverloads - fun setUp(binaryMessenger: BinaryMessenger, api: CloudFunctionsHostApi?, messageChannelSuffix: String = "") { - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + fun setUp( + binaryMessenger: BinaryMessenger, + api: CloudFunctionsHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -95,7 +102,11 @@ interface CloudFunctionsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List diff --git a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/StreamResponseSubscriber.kt b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/StreamResponseSubscriber.kt index 9785ca3285b4..d6224703433d 100644 --- a/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/StreamResponseSubscriber.kt +++ b/packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/StreamResponseSubscriber.kt @@ -10,8 +10,7 @@ import io.flutter.plugin.common.EventChannel.EventSink import org.reactivestreams.Subscriber import org.reactivestreams.Subscription -class StreamResponseSubscriber(private val eventSink: EventSink?) : - Subscriber { +class StreamResponseSubscriber(private val eventSink: EventSink?) : Subscriber { private var subscription: Subscription? = null private val mainThreadHandler = Handler(Looper.getMainLooper()) diff --git a/packages/cloud_functions/cloud_functions/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/functions/example/MainActivity.kt b/packages/cloud_functions/cloud_functions/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/functions/example/MainActivity.kt index 1e409db224c4..30f8b8e8c69a 100644 --- a/packages/cloud_functions/cloud_functions/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/functions/example/MainActivity.kt +++ b/packages/cloud_functions/cloud_functions/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/functions/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.functions.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/cloud_functions/cloud_functions/example/pubspec.yaml b/packages/cloud_functions/cloud_functions/example/pubspec.yaml index eda9b9a2d54e..8b854b769f49 100644 --- a/packages/cloud_functions/cloud_functions/example/pubspec.yaml +++ b/packages/cloud_functions/cloud_functions/example/pubspec.yaml @@ -1,13 +1,14 @@ name: cloud_functions_example description: Demonstrates how to use the cloud_functions plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - cloud_functions: ^6.1.0 - firebase_core: ^4.6.0 + cloud_functions: ^6.3.3 + firebase_core: ^4.11.0 flutter: sdk: flutter diff --git a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Package.swift b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Package.swift index feff7c887fc8..3f8528c174c0 100644 --- a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Package.swift +++ b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "cloud_functions", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "cloud-functions", targets: ["cloud_functions"]), + .library(name: "cloud-functions", targets: ["cloud_functions"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/CloudFunctionsMessages.g.swift b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/CloudFunctionsMessages.g.swift index a24439b9a7c3..86ba0c92f137 100644 --- a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/CloudFunctionsMessages.g.swift +++ b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/CloudFunctionsMessages.g.swift @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -52,7 +52,7 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } @@ -88,8 +88,9 @@ class CloudFunctionsMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol CloudFunctionsHostApi { func call(arguments: [String: Any?], completion: @escaping (Result) -> Void) - func registerEventChannel(arguments: [String: Any], - completion: @escaping (Result) -> Void) + func registerEventChannel( + arguments: [String: Any], + completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -100,11 +101,14 @@ class CloudFunctionsHostApiSetup { /// Sets up an instance of `CloudFunctionsHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: CloudFunctionsHostApi?, - messageChannelSuffix: String = "") { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: CloudFunctionsHostApi?, + messageChannelSuffix: String = "" + ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let callChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -114,9 +118,9 @@ class CloudFunctionsHostApiSetup { let argumentsArg = args[0] as! [String: Any?] api.call(arguments: argumentsArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -125,7 +129,8 @@ class CloudFunctionsHostApiSetup { callChannel.setMessageHandler(nil) } let registerEventChannelChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel\(channelSuffix)", + name: + "dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -137,7 +142,7 @@ class CloudFunctionsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } diff --git a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/Constants.swift b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/Constants.swift index f83925b60ff3..794e70dce372 100644 --- a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/Constants.swift +++ b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/Constants.swift @@ -3,4 +3,4 @@ // found in the LICENSE file. /// Auto-generated file. Do not edit. -public let versionNumber = "6.1.0" +public let versionNumber = "6.3.3" diff --git a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FirebaseFunctionsPlugin.swift b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FirebaseFunctionsPlugin.swift index 7163bdda6f10..c5026ca8578b 100644 --- a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FirebaseFunctionsPlugin.swift +++ b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FirebaseFunctionsPlugin.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebaseFunctions + #if canImport(FlutterMacOS) import FlutterMacOS #else @@ -13,14 +15,14 @@ #else import firebase_core_shared #endif -import FirebaseFunctions extension FlutterError: Error {} let kFLTFirebaseFunctionsChannelName = "plugins.flutter.io/firebase_functions" public class FirebaseFunctionsPlugin: NSObject, FLTFirebasePluginProtocol, FlutterPlugin, - CloudFunctionsHostApi { + CloudFunctionsHostApi +{ func call(arguments: [String: Any?], completion: @escaping (Result) -> Void) { httpsFunctionCall(arguments: arguments) { result, error in if let error { @@ -31,8 +33,10 @@ public class FirebaseFunctionsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt } } - func registerEventChannel(arguments: [String: Any], - completion: @escaping (Result) -> Void) { + func registerEventChannel( + arguments: [String: Any], + completion: @escaping (Result) -> Void + ) { let eventChannelId = arguments["eventChannelId"]! let eventChannelName = "\(kFLTFirebaseFunctionsChannelName)/\(eventChannelId)" let eventChannel = FlutterEventChannel(name: eventChannelName, binaryMessenger: binaryMessenger) @@ -80,8 +84,10 @@ public class FirebaseFunctionsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt CloudFunctionsHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance) } - private func httpsFunctionCall(arguments: [String: Any], - completion: @escaping (Any?, FlutterError?) -> Void) { + private func httpsFunctionCall( + arguments: [String: Any], + completion: @escaping (Any?, FlutterError?) -> Void + ) { let appName = arguments["appName"] as? String ?? "" let functionName = arguments["functionName"] as? String let functionUri = arguments["functionUri"] as? String @@ -96,9 +102,10 @@ public class FirebaseFunctionsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt let functions = Functions.functions(app: app, region: region ?? "") if let origin, !origin.isEmpty, - let url = URL(string: origin), - let host = url.host, - let port = url.port { + let url = URL(string: origin), + let host = url.host, + let port = url.port + { functions.useEmulator(withHost: host, port: port) } @@ -109,14 +116,18 @@ public class FirebaseFunctionsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt if let functionName, !functionName.isEmpty { function = functions.httpsCallable(functionName, options: options) } else if let functionUri, !functionUri.isEmpty, - let url = URL(string: functionUri) { + let url = URL(string: functionUri) + { function = functions.httpsCallable(url, options: options) } else { - completion(nil, FlutterError( - code: "IllegalArgumentException", - message: "Either functionName or functionUri must be set", - details: nil - )) + completion( + nil, + FlutterError( + code: "IllegalArgumentException", + message: "Either functionName or functionUri must be set", + details: nil + ) + ) return } diff --git a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FunctionsStreamHandler.swift b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FunctionsStreamHandler.swift index 2cf023757450..7d2ce08e76cc 100644 --- a/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FunctionsStreamHandler.swift +++ b/packages/cloud_functions/cloud_functions/ios/cloud_functions/Sources/cloud_functions/FunctionsStreamHandler.swift @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebaseFunctions + #if canImport(FlutterMacOS) import FlutterMacOS #else import Flutter #endif -import FirebaseFunctions - class FunctionsStreamHandler: NSObject, FlutterStreamHandler { var functions: Functions private var streamTask: Task? @@ -19,8 +19,10 @@ class FunctionsStreamHandler: NSObject, FlutterStreamHandler { super.init() } - func onListen(withArguments arguments: Any?, - eventSink events: @escaping FlutterEventSink) -> FlutterError? { + func onListen( + withArguments arguments: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { streamTask = Task { await httpsStreamCall(arguments: arguments, events: events) } @@ -35,9 +37,13 @@ class FunctionsStreamHandler: NSObject, FlutterStreamHandler { private func httpsStreamCall(arguments: Any?, events: @escaping FlutterEventSink) async { guard let arguments = arguments as? [String: Any] else { await MainActor.run { - events(FlutterError(code: "invalid_arguments", - message: "Invalid arguments", - details: nil)) + events( + FlutterError( + code: "invalid_arguments", + message: "Invalid arguments", + details: nil + ) + ) } return } @@ -49,9 +55,10 @@ class FunctionsStreamHandler: NSObject, FlutterStreamHandler { let limitedUseAppCheckToken = arguments["limitedUseAppCheckToken"] as? Bool ?? false if let origin, - let url = URL(string: origin), - let host = url.host, - let port = url.port { + let url = URL(string: origin), + let host = url.host, + let port = url.port + { functions.useEmulator(withHost: host, port: port) } @@ -67,9 +74,13 @@ class FunctionsStreamHandler: NSObject, FlutterStreamHandler { function = functions.httpsCallable(url, options: options) } else { await MainActor.run { - events(FlutterError(code: "IllegalArgumentException", - message: "Either functionName or functionUri must be set", - details: nil)) + events( + FlutterError( + code: "IllegalArgumentException", + message: "Either functionName or functionUri must be set", + details: nil + ) + ) } return } @@ -86,9 +97,9 @@ class FunctionsStreamHandler: NSObject, FlutterStreamHandler { for try await response in stream { await MainActor.run { switch response { - case let .message(message): + case .message(let message): events(["message": message.value]) - case let .result(result): + case .result(let result): events(["result": result.value]) events(FlutterEndOfEventStream) } @@ -96,16 +107,24 @@ class FunctionsStreamHandler: NSObject, FlutterStreamHandler { } } catch { await MainActor.run { - events(FlutterError(code: "unknown", - message: error.localizedDescription, - details: ["code": "unknown", "message": error.localizedDescription])) + events( + FlutterError( + code: "unknown", + message: error.localizedDescription, + details: ["code": "unknown", "message": error.localizedDescription] + ) + ) } } } else { await MainActor.run { - events(FlutterError(code: "unknown", - message: "Streaming requires iOS 15+ or macOS 12+", - details: nil)) + events( + FlutterError( + code: "unknown", + message: "Streaming requires iOS 15+ or macOS 12+", + details: nil + ) + ) } } } diff --git a/packages/cloud_functions/cloud_functions/lib/cloud_functions.dart b/packages/cloud_functions/cloud_functions/lib/cloud_functions.dart index f95683cac2bd..3aacd08bc7e7 100644 --- a/packages/cloud_functions/cloud_functions/lib/cloud_functions.dart +++ b/packages/cloud_functions/cloud_functions/lib/cloud_functions.dart @@ -11,7 +11,7 @@ import 'dart:typed_data'; import 'package:cloud_functions_platform_interface/cloud_functions_platform_interface.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:flutter/foundation.dart'; export 'package:cloud_functions_platform_interface/cloud_functions_platform_interface.dart' diff --git a/packages/cloud_functions/cloud_functions/lib/src/firebase_functions.dart b/packages/cloud_functions/cloud_functions/lib/src/firebase_functions.dart index 94a3e25e85d1..aef9a37c036a 100644 --- a/packages/cloud_functions/cloud_functions/lib/src/firebase_functions.dart +++ b/packages/cloud_functions/cloud_functions/lib/src/firebase_functions.dart @@ -8,7 +8,7 @@ part of '../cloud_functions.dart'; /// The entry point for accessing FirebaseFunctions. /// /// You can get an instance by calling [FirebaseFunctions.instance]. -class FirebaseFunctions extends FirebasePluginPlatform { +class FirebaseFunctions extends FirebasePlugin { FirebaseFunctions._({required this.app, String? region}) : _region = region ??= 'us-central1', super(app.name, 'plugins.flutter.io/firebase_functions'); diff --git a/packages/cloud_functions/cloud_functions/macos/cloud_functions/Package.swift b/packages/cloud_functions/cloud_functions/macos/cloud_functions/Package.swift index f52247390a98..2c25ccde9d22 100644 --- a/packages/cloud_functions/cloud_functions/macos/cloud_functions/Package.swift +++ b/packages/cloud_functions/cloud_functions/macos/cloud_functions/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "cloud_functions", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "cloud-functions", targets: ["cloud_functions"]), + .library(name: "cloud-functions", targets: ["cloud_functions"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/cloud_functions/cloud_functions/pubspec.yaml b/packages/cloud_functions/cloud_functions/pubspec.yaml index 14f02dc869cb..243eb4c37e5c 100644 --- a/packages/cloud_functions/cloud_functions/pubspec.yaml +++ b/packages/cloud_functions/cloud_functions/pubspec.yaml @@ -1,6 +1,7 @@ name: cloud_functions description: A Flutter plugin allowing you to use Firebase Cloud Functions. -version: 6.1.0 +version: 6.3.3 +resolution: workspace homepage: https://firebase.google.com/docs/functions repository: https://github.com/firebase/flutterfire/tree/main/packages/cloud_functions/cloud_functions topics: @@ -13,14 +14,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - cloud_functions_platform_interface: ^5.8.11 - cloud_functions_web: ^5.1.4 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + cloud_functions_platform_interface: ^6.0.3 + cloud_functions_web: ^5.1.9 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter diff --git a/packages/cloud_functions/cloud_functions/windows/messages.g.cpp b/packages/cloud_functions/cloud_functions/windows/messages.g.cpp index 31724f26c541..50ab69ead469 100644 --- a/packages/cloud_functions/cloud_functions/windows/messages.g.cpp +++ b/packages/cloud_functions/cloud_functions/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace cloud_functions_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,32 +33,239 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + uint8_t type, ::flutter::ByteStreamReader* stream) const { + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { - flutter::StandardCodecSerializer::WriteValue(value, stream); + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by CloudFunctionsHostApi. -const flutter::StandardMessageCodec& CloudFunctionsHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& CloudFunctionsHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `CloudFunctionsHostApi` to handle messages through the // `binary_messenger`. -void CloudFunctionsHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void CloudFunctionsHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, CloudFunctionsHostApi* api) { CloudFunctionsHostApi::SetUp(binary_messenger, api, ""); } -void CloudFunctionsHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void CloudFunctionsHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, CloudFunctionsHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -72,7 +281,7 @@ void CloudFunctionsHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_arguments_arg = args.at(0); @@ -117,7 +326,7 @@ void CloudFunctionsHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_arguments_arg = args.at(0); diff --git a/packages/cloud_functions/cloud_functions/windows/messages.g.h b/packages/cloud_functions/cloud_functions/windows/messages.g.h index 7648ad06af7f..6f537decbd12 100644 --- a/packages/cloud_functions/cloud_functions/windows/messages.g.h +++ b/packages/cloud_functions/cloud_functions/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -58,7 +58,8 @@ class ErrorOr { std::variant v_; }; -class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -66,12 +67,12 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -82,24 +83,25 @@ class CloudFunctionsHostApi { CloudFunctionsHostApi& operator=(const CloudFunctionsHostApi&) = delete; virtual ~CloudFunctionsHostApi() {} virtual void Call( - const flutter::EncodableMap& arguments, - std::function> reply)> + const ::flutter::EncodableMap& arguments, + std::function< + void(ErrorOr> reply)> result) = 0; virtual void RegisterEventChannel( - const flutter::EncodableMap& arguments, + const ::flutter::EncodableMap& arguments, std::function reply)> result) = 0; // The codec used by CloudFunctionsHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `CloudFunctionsHostApi` to handle messages through // the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, CloudFunctionsHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, CloudFunctionsHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: CloudFunctionsHostApi() = default; diff --git a/packages/cloud_functions/cloud_functions_platform_interface/CHANGELOG.md b/packages/cloud_functions/cloud_functions_platform_interface/CHANGELOG.md index afa88dfe65b5..71b37c8b6c4a 100644 --- a/packages/cloud_functions/cloud_functions_platform_interface/CHANGELOG.md +++ b/packages/cloud_functions/cloud_functions_platform_interface/CHANGELOG.md @@ -1,3 +1,26 @@ +## 6.0.3 + + - Update a dependency to the latest release. + +## 6.0.2 + + - Update a dependency to the latest release. + +## 6.0.1 + + - Update a dependency to the latest release. + +## 6.0.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 5.8.12 + + - Update a dependency to the latest release. + ## 5.8.11 - **FIX**(functions): prevent collision when listening multiple times to the same stream ([#18052](https://github.com/firebase/flutterfire/issues/18052)). ([c13040e1](https://github.com/firebase/flutterfire/commit/c13040e15a42deddbf61b3180bbd002d58edca29)) diff --git a/packages/cloud_functions/cloud_functions_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/cloud_functions/cloud_functions_platform_interface/lib/src/pigeon/messages.pigeon.dart index 5249dd1cd031..ed2a18de54a0 100644 --- a/packages/cloud_functions/cloud_functions_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/cloud_functions/cloud_functions_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,21 +1,40 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.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, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } List wrapResponse( @@ -66,54 +85,41 @@ class CloudFunctionsHostApi { final String pigeonVar_messageChannelSuffix; Future call(Map arguments) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([arguments]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 pigeonVar_replyList[0]; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + return pigeonVar_replyValue; } Future registerEventChannel(Map arguments) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([arguments]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } diff --git a/packages/cloud_functions/cloud_functions_platform_interface/pubspec.yaml b/packages/cloud_functions/cloud_functions_platform_interface/pubspec.yaml index 6b0438bb0364..298c86cec507 100644 --- a/packages/cloud_functions/cloud_functions_platform_interface/pubspec.yaml +++ b/packages/cloud_functions/cloud_functions_platform_interface/pubspec.yaml @@ -5,22 +5,23 @@ repository: https://github.com/firebase/flutterfire/tree/main/packages/cloud_fun # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 5.8.11 +version: 6.0.3 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 - pigeon: 25.3.2 + pigeon: 26.3.4 diff --git a/packages/cloud_functions/cloud_functions_platform_interface/test/pigeon/test_api.dart b/packages/cloud_functions/cloud_functions_platform_interface/test/pigeon/test_api.dart index 085dfc1a66de..3583ef25e17d 100644 --- a/packages/cloud_functions/cloud_functions_platform_interface/test/pigeon/test_api.dart +++ b/packages/cloud_functions/cloud_functions_platform_interface/test/pigeon/test_api.dart @@ -1,9 +1,9 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers +// 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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -51,9 +51,7 @@ abstract class TestCloudFunctionsHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -64,15 +62,11 @@ abstract class TestCloudFunctionsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call was null.'); - final List args = (message as List?)!; - final Map? arg_arguments = - (args[0] as Map?)?.cast(); - assert(arg_arguments != null, - 'Argument for dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.call was null, expected non-null Map.'); + final List args = message! as List; + final Map arg_arguments = + (args[0]! as Map).cast(); try { - final Object? output = await api.call(arg_arguments!); + final Object? output = await api.call(arg_arguments); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -84,9 +78,7 @@ abstract class TestCloudFunctionsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -97,15 +89,11 @@ abstract class TestCloudFunctionsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel was null.'); - final List args = (message as List?)!; - final Map? arg_arguments = - (args[0] as Map?)?.cast(); - assert(arg_arguments != null, - 'Argument for dev.flutter.pigeon.cloud_functions_platform_interface.CloudFunctionsHostApi.registerEventChannel was null, expected non-null Map.'); + final List args = message! as List; + final Map arg_arguments = + (args[0]! as Map).cast(); try { - await api.registerEventChannel(arg_arguments!); + await api.registerEventChannel(arg_arguments); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/cloud_functions/cloud_functions_web/CHANGELOG.md b/packages/cloud_functions/cloud_functions_web/CHANGELOG.md index 0089a3225fce..8ec1fc957392 100644 --- a/packages/cloud_functions/cloud_functions_web/CHANGELOG.md +++ b/packages/cloud_functions/cloud_functions_web/CHANGELOG.md @@ -1,3 +1,24 @@ +## 5.1.9 + + - Update a dependency to the latest release. + +## 5.1.8 + + - Update a dependency to the latest release. + +## 5.1.7 + + - Update a dependency to the latest release. + +## 5.1.6 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(functions,web): dartify results from cloud function stream ([#18212](https://github.com/firebase/flutterfire/issues/18212)). ([9f32c614](https://github.com/firebase/flutterfire/commit/9f32c614a9fee53ceebc5540d91c76ba4fd91d8b)) + +## 5.1.5 + + - Update a dependency to the latest release. + ## 5.1.4 - Update a dependency to the latest release. diff --git a/packages/cloud_functions/cloud_functions_web/lib/interop/functions.dart b/packages/cloud_functions/cloud_functions_web/lib/interop/functions.dart index 0b548c34748f..e884d6954576 100644 --- a/packages/cloud_functions/cloud_functions_web/lib/interop/functions.dart +++ b/packages/cloud_functions/cloud_functions_web/lib/interop/functions.dart @@ -103,7 +103,7 @@ class HttpsCallable extends JsObjectWrapper { } final result = await streamResult.data.toDart; - yield {'result': result}; + yield {'result': _dartify(result)}; } } diff --git a/packages/cloud_functions/cloud_functions_web/lib/src/cloud_functions_version.dart b/packages/cloud_functions/cloud_functions_web/lib/src/cloud_functions_version.dart index 0bf512e408f0..065006fb014e 100644 --- a/packages/cloud_functions/cloud_functions_web/lib/src/cloud_functions_version.dart +++ b/packages/cloud_functions/cloud_functions_web/lib/src/cloud_functions_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '6.1.0'; +const packageVersion = '6.3.3'; diff --git a/packages/cloud_functions/cloud_functions_web/pubspec.yaml b/packages/cloud_functions/cloud_functions_web/pubspec.yaml index ee26b10cb6f8..11e8e305cda9 100644 --- a/packages/cloud_functions/cloud_functions_web/pubspec.yaml +++ b/packages/cloud_functions/cloud_functions_web/pubspec.yaml @@ -3,16 +3,17 @@ description: The web implementation of cloud_functions homepage: https://github.com/firebase/flutterfire/tree/main/packages/cloud_functions/cloud_functions_web repository: https://github.com/firebase/flutterfire/tree/main/packages/cloud_functions/cloud_functions_web -version: 5.1.4 +version: 5.1.9 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - cloud_functions_platform_interface: ^5.8.11 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 + cloud_functions_platform_interface: ^6.0.3 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 flutter: sdk: flutter flutter_web_plugins: @@ -20,7 +21,7 @@ dependencies: web: ^1.1.1 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_ai/firebase_ai/CHANGELOG.md b/packages/firebase_ai/firebase_ai/CHANGELOG.md index 52413ecfffa9..8f0e6f6744e3 100644 --- a/packages/firebase_ai/firebase_ai/CHANGELOG.md +++ b/packages/firebase_ai/firebase_ai/CHANGELOG.md @@ -1,3 +1,40 @@ +## 3.13.1 + + - Update a dependency to the latest release. + +## 3.13.0 + + - **FIX**(ai): firebase_ai server template image inputs passed as InlineDataPart were serialized as normal generative inlineData ([#18350](https://github.com/firebase/flutterfire/issues/18350)). ([f3c53792](https://github.com/firebase/flutterfire/commit/f3c53792f1acf19ab1c2c7c3d157fca3a183b5d1)) + - **FEAT**(firebaseai): Add speech config and TTS sample page ([#18358](https://github.com/firebase/flutterfire/issues/18358)). ([0af51b50](https://github.com/firebase/flutterfire/commit/0af51b501603a611c7c6800efd9d98c478abab4d)) + - **FEAT**(ai): add language code support for SpeechConfig ([#18353](https://github.com/firebase/flutterfire/issues/18353)). ([3471afc7](https://github.com/firebase/flutterfire/commit/3471afc7ffa7bae58981683982d58d669ac71d50)) + - **FEAT**(ai): add mediaResolution parameter ([#18354](https://github.com/firebase/flutterfire/issues/18354)). ([79547569](https://github.com/firebase/flutterfire/commit/795475692384385a17511b295640ad6f8ab625f6)) + - **FEAT**(ai): add support for cancellable clients for in-flight requests ([#18349](https://github.com/firebase/flutterfire/issues/18349)). ([566cfed4](https://github.com/firebase/flutterfire/commit/566cfed42599318cf0f24eefc5d696223e46128c)) + +## 3.12.2 + + - Update a dependency to the latest release. + +## 3.12.1 + + - Update a dependency to the latest release. + +## 3.12.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**(firebaseai): ImageConfig and FinishReasons ([#18180](https://github.com/firebase/flutterfire/issues/18180)). ([7d1a8b1d](https://github.com/firebase/flutterfire/commit/7d1a8b1db4c5f585ba38d46df37330d3d17de774)) + - **FEAT**(firebaseai): live session resumption ([#18038](https://github.com/firebase/flutterfire/issues/18038)). ([829fd949](https://github.com/firebase/flutterfire/commit/829fd949bf1644ad9ce73dfb8ed416d89654f989)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + - **FEAT**(ai): add missing enum types ([#18198](https://github.com/firebase/flutterfire/issues/18198)). ([889af7c7](https://github.com/firebase/flutterfire/commit/889af7c7b8f7705a55cdd90e39d306858a2e9fd4)) + - **FEAT**(firebaseai): add Google Maps Grounding support ([#18144](https://github.com/firebase/flutterfire/issues/18144)). ([385d9337](https://github.com/firebase/flutterfire/commit/385d93372f749843ee3d8ac409a878fa149ba7ed)) + - **FEAT**(ai): add unexpectedToolCall finish reason and corresponding tests ([#18188](https://github.com/firebase/flutterfire/issues/18188)). ([27852720](https://github.com/firebase/flutterfire/commit/278527207a4fb35a5854dd3f0a9405da9f80877c)) + +## 3.11.0 + + - **FEAT**(firebaseai): server prompt chat and function calling ([#17972](https://github.com/firebase/flutterfire/issues/17972)). ([4b8f2288](https://github.com/firebase/flutterfire/commit/4b8f22889808c0a55f4fc2abc52c72aa2d932379)) + - **FEAT**(firebaseai): add spm support for firebase_ai, and update example to running with spm ([#18159](https://github.com/firebase/flutterfire/issues/18159)). ([bb1e04f8](https://github.com/firebase/flutterfire/commit/bb1e04f8cd0f29cad3913af7bcf40744ffb2515a)) + - **FEAT**(firebaseai): deprecate imagen ([#18148](https://github.com/firebase/flutterfire/issues/18148)). ([99450317](https://github.com/firebase/flutterfire/commit/99450317d83f7b77ab192aed7071432785337789)) + - **FEAT**(firebaseai): Add ability for Schema class to export to json schema ([#18131](https://github.com/firebase/flutterfire/issues/18131)). ([5818a33c](https://github.com/firebase/flutterfire/commit/5818a33c060f775b4a00e11ad5ee04b71e939dad)) + ## 3.10.0 - **FEAT**(firebaseai): add proper headers for X-Android-Package, X-Android-Cert and x-ios-bundle-identifier ([#18076](https://github.com/firebase/flutterfire/issues/18076)). ([1351e94e](https://github.com/firebase/flutterfire/commit/1351e94ed3213c458a955cebf05802f12838d5f7)) diff --git a/packages/firebase_ai/firebase_ai/android/build.gradle b/packages/firebase_ai/firebase_ai/android/build.gradle index 13affc9f6740..278104b4b9c2 100644 --- a/packages/firebase_ai/firebase_ai/android/build.gradle +++ b/packages/firebase_ai/firebase_ai/android/build.gradle @@ -4,9 +4,13 @@ version '1.0-SNAPSHOT' apply plugin: 'com.android.library' apply from: file("local-config.gradle") -// AGP 9+ has built-in Kotlin support; older versions need the plugin explicitly. +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int -if (agpMajor < 9) { +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { apply plugin: 'kotlin-android' } @@ -33,12 +37,6 @@ android { targetCompatibility project.ext.javaVersion } - if (agpMajor < 9) { - kotlinOptions { - jvmTarget = project.ext.javaVersion - } - } - sourceSets { main.java.srcDirs += "src/main/kotlin" } @@ -47,3 +45,11 @@ android { disable 'InvalidPackage' } } + +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } + } +} diff --git a/packages/firebase_ai/firebase_ai/android/src/main/kotlin/io/flutter/plugins/firebase/ai/FirebaseAIPlugin.kt b/packages/firebase_ai/firebase_ai/android/src/main/kotlin/io/flutter/plugins/firebase/ai/FirebaseAIPlugin.kt index 3377f693d3e5..0a7e5168400a 100644 --- a/packages/firebase_ai/firebase_ai/android/src/main/kotlin/io/flutter/plugins/firebase/ai/FirebaseAIPlugin.kt +++ b/packages/firebase_ai/firebase_ai/android/src/main/kotlin/io/flutter/plugins/firebase/ai/FirebaseAIPlugin.kt @@ -25,77 +25,74 @@ import java.security.MessageDigest import java.security.NoSuchAlgorithmException class FirebaseAIPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { - private lateinit var channel: MethodChannel - private lateinit var context: Context + private lateinit var channel: MethodChannel + private lateinit var context: Context - override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - context = binding.applicationContext - channel = MethodChannel(binding.binaryMessenger, "plugins.flutter.io/firebase_ai") - channel.setMethodCallHandler(this) - } + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + context = binding.applicationContext + channel = MethodChannel(binding.binaryMessenger, "plugins.flutter.io/firebase_ai") + channel.setMethodCallHandler(this) + } - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - when (call.method) { - "getPlatformHeaders" -> { - val headers = mapOf( - "X-Android-Package" to context.packageName, - "X-Android-Cert" to (getSigningCertFingerprint() ?: "") - ) - result.success(headers) - } - else -> result.notImplemented() - } + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "getPlatformHeaders" -> { + val headers = + mapOf( + "X-Android-Package" to context.packageName, + "X-Android-Cert" to (getSigningCertFingerprint() ?: "")) + result.success(headers) + } + else -> result.notImplemented() } + } - @OptIn(ExperimentalStdlibApi::class) - private fun getSigningCertFingerprint(): String? { - val packageName = context.packageName - val signature = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - val packageInfo = try { + @OptIn(ExperimentalStdlibApi::class) + private fun getSigningCertFingerprint(): String? { + val packageName = context.packageName + val signature = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val packageInfo = + try { context.packageManager.getPackageInfo( - packageName, - PackageManager.GET_SIGNING_CERTIFICATES - ) - } catch (e: PackageManager.NameNotFoundException) { + packageName, PackageManager.GET_SIGNING_CERTIFICATES) + } catch (e: PackageManager.NameNotFoundException) { Log.e(TAG, "PackageManager couldn't find the package \"$packageName\"", e) return null - } - val signingInfo = packageInfo?.signingInfo ?: return null - if (signingInfo.hasMultipleSigners()) { - signingInfo.apkContentsSigners.firstOrNull() - } else { - signingInfo.signingCertificateHistory.lastOrNull() - } + } + val signingInfo = packageInfo?.signingInfo ?: return null + if (signingInfo.hasMultipleSigners()) { + signingInfo.apkContentsSigners.firstOrNull() + } else { + signingInfo.signingCertificateHistory.lastOrNull() + } } else { - @Suppress("DEPRECATION") - val packageInfo = try { - context.packageManager.getPackageInfo( - packageName, - PackageManager.GET_SIGNATURES - ) - } catch (e: PackageManager.NameNotFoundException) { + @Suppress("DEPRECATION") + val packageInfo = + try { + context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES) + } catch (e: PackageManager.NameNotFoundException) { Log.e(TAG, "PackageManager couldn't find the package \"$packageName\"", e) return null - } - @Suppress("DEPRECATION") - packageInfo?.signatures?.firstOrNull() + } + @Suppress("DEPRECATION") packageInfo?.signatures?.firstOrNull() } ?: return null - return try { - val messageDigest = MessageDigest.getInstance("SHA-1") - val digest = messageDigest.digest(signature.toByteArray()) - digest.toHexString(HexFormat.UpperCase) - } catch (e: NoSuchAlgorithmException) { - Log.w(TAG, "No support for SHA-1 algorithm found.", e) - null - } + return try { + val messageDigest = MessageDigest.getInstance("SHA-1") + val digest = messageDigest.digest(signature.toByteArray()) + digest.toHexString(HexFormat.UpperCase) + } catch (e: NoSuchAlgorithmException) { + Log.w(TAG, "No support for SHA-1 algorithm found.", e) + null } + } - companion object { - private const val TAG = "FirebaseAIPlugin" - } + companion object { + private const val TAG = "FirebaseAIPlugin" + } } diff --git a/packages/firebase_ai/firebase_ai/example/.metadata b/packages/firebase_ai/firebase_ai/example/.metadata index b6128fc4f019..9ab588718994 100644 --- a/packages/firebase_ai/firebase_ai/example/.metadata +++ b/packages/firebase_ai/firebase_ai/example/.metadata @@ -4,8 +4,8 @@ # This file should be version controlled and should not be manually edited. version: - revision: "2615ab6c1932e44e6802aaba8dc715b387ff155f" - channel: "main" + revision: "aa1d2edd012bbf31e58827bf3ac7010c10991e20" + channel: "beta" project_type: app @@ -13,11 +13,17 @@ project_type: app migration: platforms: - platform: root - create_revision: 2615ab6c1932e44e6802aaba8dc715b387ff155f - base_revision: 2615ab6c1932e44e6802aaba8dc715b387ff155f + create_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 + base_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 + - platform: ios + create_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 + base_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 + - platform: macos + create_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 + base_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 - platform: android - create_revision: 2615ab6c1932e44e6802aaba8dc715b387ff155f - base_revision: 2615ab6c1932e44e6802aaba8dc715b387ff155f + create_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 + base_revision: aa1d2edd012bbf31e58827bf3ac7010c10991e20 # User provided section diff --git a/packages/firebase_ai/firebase_ai/example/android/settings.gradle.kts b/packages/firebase_ai/firebase_ai/example/android/settings.gradle.kts index ab39a10a29ba..43394ed5e1fd 100644 --- a/packages/firebase_ai/firebase_ai/example/android/settings.gradle.kts +++ b/packages/firebase_ai/firebase_ai/example/android/settings.gradle.kts @@ -18,7 +18,7 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.7.3" apply false + id("com.android.application") version "8.9.1" apply false id("org.jetbrains.kotlin.android") version "2.1.0" apply false } diff --git a/packages/firebase_ai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist b/packages/firebase_ai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist index 7c5696400627..0d14080090af 100644 --- a/packages/firebase_ai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/firebase_ai/firebase_ai/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 15.0 diff --git a/packages/firebase_ai/firebase_ai/example/ios/Podfile b/packages/firebase_ai/firebase_ai/example/ios/Podfile index a419e4239013..620e46eba607 100644 --- a/packages/firebase_ai/firebase_ai/example/ios/Podfile +++ b/packages/firebase_ai/firebase_ai/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '15.0' +# platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -29,7 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do use_frameworks! - use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do diff --git a/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj index 40278fc43dec..550a5b9c22a6 100644 --- a/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/project.pbxproj @@ -13,11 +13,9 @@ 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 */; }; - 901FEC83A38129064032C578 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94CE5BFCDF90764354BB6740 /* Pods_Runner.framework */; }; 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 */; }; - B7B3CA2D70F15615E1B8E5D8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 154D9627A1C14A5ACE0B7B0D /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -46,18 +44,16 @@ /* 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 = ""; }; - 154D9627A1C14A5ACE0B7B0D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 232D95ECCEC6F04B9CEC8925 /* 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 = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 560CA017EC76D8AAE2E21549 /* 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 = ""; }; 5F1FA05866A2D0FCA3287B20 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* firebase_ai */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = firebase_ai; path = ../../ios/firebase_ai; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8ACDC47C7E9AF1A1B9595598 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 94CE5BFCDF90764354BB6740 /* 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; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -65,9 +61,6 @@ 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 = ""; }; - A85D07EF8959748E1D3E564B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - B0B22A9E291076BD22BA9F10 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - E1D0571EA0792087F8F27457 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,7 +68,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B7B3CA2D70F15615E1B8E5D8 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -84,7 +76,6 @@ buildActionMask = 2147483647; files = ( 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, - 901FEC83A38129064032C578 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,12 +93,6 @@ 3C3B3E8596675CC144D1BD5B /* Pods */ = { isa = PBXGroup; children = ( - E1D0571EA0792087F8F27457 /* Pods-Runner.debug.xcconfig */, - 232D95ECCEC6F04B9CEC8925 /* Pods-Runner.release.xcconfig */, - 560CA017EC76D8AAE2E21549 /* Pods-Runner.profile.xcconfig */, - A85D07EF8959748E1D3E564B /* Pods-RunnerTests.debug.xcconfig */, - 8ACDC47C7E9AF1A1B9595598 /* Pods-RunnerTests.release.xcconfig */, - B0B22A9E291076BD22BA9F10 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -115,6 +100,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78DABEA22ED26510000E7860 /* firebase_ai */, + 784666492D4C4C64000A1A5F /* FlutterFramework */, + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -132,7 +120,6 @@ 331C8082294A63A400263BE5 /* RunnerTests */, 5F1FA05866A2D0FCA3287B20 /* GoogleService-Info.plist */, 3C3B3E8596675CC144D1BD5B /* Pods */, - A50BECFB61A452F592070BAA /* Frameworks */, ); sourceTree = ""; }; @@ -160,15 +147,6 @@ path = Runner; sourceTree = ""; }; - A50BECFB61A452F592070BAA /* Frameworks */ = { - isa = PBXGroup; - children = ( - 94CE5BFCDF90764354BB6740 /* Pods_Runner.framework */, - 154D9627A1C14A5ACE0B7B0D /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -176,7 +154,6 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - F5C7CFE0E232B64D613F0623 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 0F5F3CD1ED7DB09B81C92173 /* Frameworks */, @@ -195,14 +172,12 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - F51794D56D63ACA383D5C2E4 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 34F21DFC67109DEAFD936E80 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -281,23 +256,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 34F21DFC67109DEAFD936E80 /* [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; @@ -329,50 +287,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - F51794D56D63ACA383D5C2E4 /* [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; - }; - F5C7CFE0E232B64D613F0623 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -499,7 +413,6 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A85D07EF8959748E1D3E564B /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -517,7 +430,6 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8ACDC47C7E9AF1A1B9595598 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -533,7 +445,6 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B0B22A9E291076BD22BA9F10 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8178cd1c619c..037ec0f249ad 100644 --- a/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/firebase_ai/firebase_ai/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -44,6 +44,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> { bool _useVertexBackend = false; late GenerativeModel _currentModel; - late ImagenModel _currentImagenModel; static final ThemeData _darkTheme = ThemeData( colorScheme: ColorScheme.fromSeed( @@ -72,32 +72,15 @@ class _GenerativeAISampleState extends State { void _initializeModel(bool useVertexBackend) { if (useVertexBackend) { - final vertexInstance = FirebaseAI.vertexAI(auth: FirebaseAuth.instance); - _currentModel = vertexInstance.generativeModel(model: 'gemini-2.5-flash'); - _currentImagenModel = _initializeImagenModel(vertexInstance); + final vertexInstance = FirebaseAI.vertexAI(location: 'global'); + _currentModel = + vertexInstance.generativeModel(model: 'gemini-3.1-flash-lite'); } else { - final googleAI = FirebaseAI.googleAI(auth: FirebaseAuth.instance); - _currentModel = googleAI.generativeModel(model: 'gemini-2.5-flash'); - _currentImagenModel = _initializeImagenModel(googleAI); + final googleAI = FirebaseAI.googleAI(); + _currentModel = googleAI.generativeModel(model: 'gemini-3.1-flash-lite'); } } - ImagenModel _initializeImagenModel(FirebaseAI instance) { - var generationConfig = ImagenGenerationConfig( - numberOfImages: 1, - aspectRatio: ImagenAspectRatio.square1x1, - imageFormat: ImagenFormat.jpeg(compressionQuality: 75), - ); - return instance.imagenModel( - model: 'imagen-3.0-capability-001', - generationConfig: generationConfig, - safetySettings: ImagenSafetySettings( - ImagenSafetyFilterLevel.blockLowAndAbove, - ImagenPersonFilterLevel.allowAdult, - ), - ); - } - void _toggleBackend(bool value) { setState(() { _useVertexBackend = value; @@ -117,7 +100,6 @@ class _GenerativeAISampleState extends State { '${_useVertexBackend}_${_currentModel.hashCode}', ), model: _currentModel, - imagenModel: _currentImagenModel, useVertexBackend: _useVertexBackend, onBackendChanged: _toggleBackend, ), @@ -127,14 +109,12 @@ class _GenerativeAISampleState extends State { class HomeScreen extends StatefulWidget { final GenerativeModel model; - final ImagenModel imagenModel; final bool useVertexBackend; final ValueChanged onBackendChanged; const HomeScreen({ super.key, required this.model, - required this.imagenModel, required this.useVertexBackend, required this.onBackendChanged, }); @@ -156,7 +136,6 @@ class _HomeScreenState extends State { Widget _buildSelectedPage( int index, GenerativeModel currentModel, - ImagenModel currentImagenModel, bool useVertexBackend, ) { switch (index) { @@ -166,7 +145,7 @@ class _HomeScreenState extends State { useVertexBackend: useVertexBackend, ); case 1: - return AudioPage(title: 'Audio', model: currentModel); + return MultimodalPage(title: 'Multimodal', model: currentModel); case 2: return TokenCountPage(title: 'Token Count', model: currentModel); case 3: @@ -178,26 +157,35 @@ class _HomeScreenState extends State { case 4: return ImagePromptPage(title: 'Image Prompt', model: currentModel); case 5: - return ImagenPage(title: 'Imagen Model', model: currentImagenModel); + return ImageGenerationPage( + title: 'Image Gen', + useVertexBackend: useVertexBackend, + ); case 6: return SchemaPromptPage(title: 'Schema Prompt', model: currentModel); case 7: return JsonSchemaPage(title: 'JSON Schema', model: currentModel); case 8: - return DocumentPage(title: 'Document Prompt', model: currentModel); - case 9: - return VideoPage(title: 'Video Prompt', model: currentModel); - case 10: return BidiPage( title: 'Live Stream', model: currentModel, useVertexBackend: useVertexBackend, ); - case 11: + case 9: return ServerTemplatePage( title: 'Server Template', useVertexBackend: useVertexBackend, ); + case 10: + return GroundingPage( + title: 'Grounding', + useVertexBackend: useVertexBackend, + ); + case 11: + return TTSPage( + title: 'TTS Test', + useVertexBackend: useVertexBackend, + ); default: // Fallback to the first page in case of an unexpected index @@ -259,7 +247,6 @@ class _HomeScreenState extends State { child: _buildSelectedPage( _selectedIndex, widget.model, - widget.imagenModel, widget.useVertexBackend, ), ), @@ -278,9 +265,9 @@ class _HomeScreenState extends State { tooltip: 'Chat', ), BottomNavigationBarItem( - icon: Icon(Icons.mic), - label: 'Audio', - tooltip: 'Audio Prompt', + icon: Icon(Icons.perm_media), + label: 'Multimodal', + tooltip: 'Multimodal Prompt', ), BottomNavigationBarItem( icon: Icon(Icons.numbers), @@ -298,9 +285,9 @@ class _HomeScreenState extends State { tooltip: 'Image Prompt', ), BottomNavigationBarItem( - icon: Icon(Icons.image_search), - label: 'Imagen', - tooltip: 'Imagen Model', + icon: Icon(Icons.brush), + label: 'NanoBanana', + tooltip: 'Image Generation', ), BottomNavigationBarItem( icon: Icon(Icons.schema), @@ -312,16 +299,6 @@ class _HomeScreenState extends State { label: 'JSON', tooltip: 'JSON Schema', ), - BottomNavigationBarItem( - icon: Icon(Icons.edit_document), - label: 'Document', - tooltip: 'Document Prompt', - ), - BottomNavigationBarItem( - icon: Icon(Icons.video_collection), - label: 'Video', - tooltip: 'Video Prompt', - ), BottomNavigationBarItem( icon: Icon( Icons.stream, @@ -336,6 +313,20 @@ class _HomeScreenState extends State { label: 'Server', tooltip: 'Server Template', ), + BottomNavigationBarItem( + icon: Icon( + Icons.location_on, + ), + label: 'Grounding', + tooltip: 'Search & Maps Grounding', + ), + BottomNavigationBarItem( + icon: Icon( + Icons.record_voice_over, + ), + label: 'TTS', + tooltip: 'Text to Speech', + ), ], currentIndex: _selectedIndex, onTap: _onItemTapped, diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/audio_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/audio_page.dart deleted file mode 100644 index 4af259693bac..000000000000 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/audio_page.dart +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:firebase_ai/firebase_ai.dart'; -import '../widgets/message_widget.dart'; -import 'package:record/record.dart'; -import 'package:path_provider/path_provider.dart'; - -final record = AudioRecorder(); - -class AudioPage extends StatefulWidget { - const AudioPage({super.key, required this.title, required this.model}); - - final String title; - final GenerativeModel model; - - @override - State createState() => _AudioPageState(); -} - -class _AudioPageState extends State { - ChatSession? chat; - final ScrollController _scrollController = ScrollController(); - final List _messages = []; - bool _recording = false; - - @override - void initState() { - super.initState(); - chat = widget.model.startChat(); - } - - Future recordAudio() async { - if (!await record.hasPermission()) { - print('Audio recording permission denied'); - return; - } - - final dir = Directory( - '${(await getApplicationDocumentsDirectory()).path}/libs/recordings', - ); - - // ignore: avoid_slow_async_io - if (!await dir.exists()) { - await dir.create(recursive: true); - } - - String filePath = - '${dir.path}/recording_${DateTime.now().millisecondsSinceEpoch}.wav'; - - await record.start( - const RecordConfig( - encoder: AudioEncoder.wav, - ), - path: filePath, - ); - } - - Future stopRecord() async { - var path = await record.stop(); - - if (path == null) { - print('Failed to stop recording'); - return; - } - - debugPrint('Recording saved to: $path'); - - try { - File file = File(path); - final audio = await file.readAsBytes(); - debugPrint('Audio file size: ${audio.length} bytes'); - - final audioPart = InlineDataPart('audio/wav', audio); - - await _submitAudioToModel(audioPart); - - await file.delete(); - debugPrint('Recording deleted successfully.'); - } catch (e) { - debugPrint('Error processing recording: $e'); - } - } - - Future _submitAudioToModel(audioPart) async { - try { - String textPrompt = 'What is in the audio recording?'; - const prompt = TextPart('What is in the audio recording?'); - - setState(() { - _messages.add(MessageData(text: textPrompt, fromUser: true)); - }); - - final response = await widget.model.generateContent([ - Content.multi([prompt, audioPart]), - ]); - - setState(() { - _messages.add(MessageData(text: response.text, fromUser: false)); - }); - - debugPrint(response.text); - } catch (e) { - debugPrint('Error sending audio to model: $e'); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: ListView.builder( - controller: _scrollController, - itemBuilder: (context, idx) { - return MessageWidget( - text: _messages[idx].text, - isFromUser: _messages[idx].fromUser ?? false, - ); - }, - itemCount: _messages.length, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), - child: Row( - children: [ - IconButton( - onPressed: () async { - setState(() { - _recording = !_recording; - }); - if (_recording) { - await recordAudio(); - } else { - await stopRecord(); - } - }, - icon: Icon( - Icons.mic, - color: _recording - ? Colors.blueGrey - : Theme.of(context).colorScheme.primary, - ), - ), - const SizedBox.square( - dimension: 15, - ), - const Text( - 'Tap the mic to record, tap again to submit', - ), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart index e6c53963bd5f..56c7e950d0ce 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/bidi_page.dart @@ -14,9 +14,9 @@ import 'dart:async'; import 'dart:developer' as developer; import 'package:flutter/foundation.dart'; - import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; +import 'package:waveform_flutter/waveform_flutter.dart'; import '../utils/audio_input.dart'; import '../utils/audio_output.dart'; @@ -25,466 +25,376 @@ import '../widgets/message_widget.dart'; import '../widgets/audio_visualizer.dart'; import '../widgets/camera_previews.dart'; -class BidiPage extends StatefulWidget { - const BidiPage({ - super.key, - required this.title, +// ============================================================================ +// MEDIA MANAGER +// Isolates Audio and Video hardware stream setup, start, stop, and cleanup. +// ============================================================================ +class BidiMediaManager { + final AudioOutput _audioOutput = AudioOutput(); + final AudioInput _audioInput = AudioInput(); + final VideoInput _videoInput = VideoInput(); + + StreamSubscription? _audioSubscription; + StreamSubscription? _videoSubscription; + + bool videoIsInitialized = false; + + // Expose hardware state/streams to the Controller and UI + Stream? get amplitudeStream => _audioInput.amplitudeStream; + dynamic get cameraController => _videoInput.cameraController; + String? get selectedCameraId => _videoInput.selectedCameraId; + bool get controllerInitialized => _videoInput.controllerInitialized; + + Future init() async { + try { + await _audioOutput.init(); + } catch (e) { + developer.log('Audio Output init error: $e'); + } + + try { + await _audioInput.init(); + } catch (e) { + developer.log('Audio Input init error: $e'); + } + + try { + await _videoInput.init(); + videoIsInitialized = true; + } catch (e) { + developer.log('Error during video initialization: $e'); + } + } + + Future startAudio(void Function(Uint8List) onData) async { + await stopAudio(); + try { + var inputStream = await _audioInput.startRecordingStream(); + await _audioOutput.playStream(); + if (inputStream != null) { + _audioSubscription = inputStream.listen( + onData, + onError: (e) { + developer.log('Audio Stream Error: $e'); + stopAudio(); + }, + cancelOnError: true, + ); + } + } catch (e) { + developer.log('BidiMediaManager.startAudio(): $e'); + rethrow; + } + } + + Future stopAudio() async { + await _audioSubscription?.cancel(); + _audioSubscription = null; + await _audioInput.stopRecording(); + } + + Future startVideo(void Function(Uint8List, String) onData) async { + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) return; + if (!videoIsInitialized) return; + + if (!_videoInput.controllerInitialized || + _videoInput.cameraController == null) { + await _videoInput.initializeCameraController(); + } + + // Wait for Mac Camera to Settle (Prevent audio hijack) + await Future.delayed(const Duration(milliseconds: 1000)); + + _videoSubscription = _videoInput.startStreamingImages().listen( + (data) { + String mimeType = 'image/jpeg'; + onData(data, mimeType); + }, + onError: (e) => developer.log('Video Stream Error: $e'), + ); + } + + Future stopVideo() async { + await _videoSubscription?.cancel(); + _videoSubscription = null; + await _videoInput.stopStreamingImages(); + } + + void playAudioChunk(Uint8List bytes) { + _audioOutput.addDataToAudioStream(bytes); + } +} + +// ============================================================================ +// BIDI SESSION CONTROLLER +// Isolates business logic, session start/stop, reconnection, and tool execution. +// ============================================================================ +class BidiSessionController extends ChangeNotifier { + BidiSessionController({ required this.model, required this.useVertexBackend, - }); + this.onShowError, + this.onScrollDown, + }) { + _initLiveModel(); + } - final String title; final GenerativeModel model; final bool useVertexBackend; + final void Function(String)? onShowError; + final VoidCallback? onScrollDown; - @override - State createState() => _BidiPageState(); -} + late LiveGenerativeModel _liveModel; + LiveSession? _session; + final BidiMediaManager mediaManager = BidiMediaManager(); -class LightControl { - final int? brightness; - final String? colorTemperature; + bool isLoading = false; + bool isSessionActive = false; + bool isMicOn = false; + bool isCameraOn = false; + bool _isDisposed = false; - LightControl({this.brightness, this.colorTemperature}); -} + @override + void notifyListeners() { + if (!_isDisposed) { + super.notifyListeners(); + } + } -class _BidiPageState extends State { - final ScrollController _scrollController = ScrollController(); - final TextEditingController _textController = TextEditingController(); - final FocusNode _textFieldFocus = FocusNode(); - final List _messages = []; - bool _loading = false; - bool _sessionOpening = false; - bool _recording = false; - late LiveGenerativeModel _liveModel; - late LiveSession _session; - final AudioOutput _audioOutput = AudioOutput(); - final AudioInput _audioInput = AudioInput(); - final VideoInput _videoInput = VideoInput(); - StreamSubscription? _audioSubscription; + // Intention state for robust stream reconnection + bool _intendedMicOn = false; + bool _intendedCameraOn = false; + + final List messages = []; + String? _activeSessionHandle; int? _inputTranscriptionMessageIndex; int? _outputTranscriptionMessageIndex; - bool _isCameraOn = false; - bool _videoIsInitialized = false; - - @override - void initState() { - super.initState(); + void _initLiveModel() { final config = LiveGenerationConfig( speechConfig: SpeechConfig(voiceName: 'Fenrir'), - responseModalities: [ - ResponseModalities.audio, - ], + responseModalities: [ResponseModalities.audio], inputAudioTranscription: AudioTranscriptionConfig(), outputAudioTranscription: AudioTranscriptionConfig(), ); - _liveModel = widget.useVertexBackend + final tools = [ + Tool.functionDeclarations([_lightControlTool]), + Tool.googleSearch(), + ]; + + _liveModel = useVertexBackend ? FirebaseAI.vertexAI().liveGenerativeModel( model: 'gemini-live-2.5-flash-preview-native-audio-09-2025', liveGenerationConfig: config, - tools: [ - Tool.functionDeclarations([lightControlTool]), - ], + tools: tools, ) : FirebaseAI.googleAI().liveGenerativeModel( model: 'gemini-2.5-flash-native-audio-preview-09-2025', liveGenerationConfig: config, - tools: [ - Tool.functionDeclarations([lightControlTool]), - ], + tools: tools, ); } - Future _initAudio() async { - try { - await _audioOutput.init(); - } catch (e) { - developer.log('Audio Output init error: $e'); - } + Future initialize() async { + isLoading = true; + notifyListeners(); + await mediaManager.init(); + isLoading = false; + notifyListeners(); + } - try { - await _audioInput.init(); - } catch (e) { - developer.log('Audio Input init error: $e'); + Future toggleSession() async { + if (isSessionActive) { + await _stopSession(explicit: true); + } else { + await _startSession(explicit: true); } } - Future _initVideo() async { + Future _startSession({required bool explicit}) async { + isLoading = true; + notifyListeners(); + try { - await _videoInput.init(); - setState(() { - _videoIsInitialized = true; - }); - } catch (e) { - developer.log('Error during video initialization: $e'); + _session = await _liveModel.connect( + sessionResumption: _activeSessionHandle != null + ? SessionResumptionConfig.resume(_activeSessionHandle!) + : SessionResumptionConfig(), + ); + } on Exception catch (e) { + developer.log( + 'Error setting up session with handle $_activeSessionHandle, error: $e, starting a new one.', + ); + _session = await _liveModel.connect(); } - } - void _scrollDown() { - if (!_scrollController.hasClients) return; + isSessionActive = true; + unawaited(_processMessagesContinuously()); - _scrollController.jumpTo( - _scrollController.position.maxScrollExtent, - ); + if (explicit) { + // Reconnect previously active hardware seamlessly into the new session + if (_intendedMicOn) await _startMicStream(); + if (_intendedCameraOn) await _startCameraStream(); + } + + isLoading = false; + notifyListeners(); } - @override - void dispose() { - if (_sessionOpening) { - _sessionOpening = false; - _session.close(); + Future _stopSession({required bool explicit}) async { + isLoading = true; + notifyListeners(); + + if (explicit) { + await mediaManager.stopAudio(); + await mediaManager.stopVideo(); + isMicOn = false; + isCameraOn = false; + // We purposefully DO NOT reset _intendedMicOn/CameraOn so we know what + // the user had active when they reconnect! } - super.dispose(); - } - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_isCameraOn) - Container( - height: 200, - color: Colors.black, - alignment: Alignment.center, - child: (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) - ? FullCameraPreview( - controller: _videoInput.cameraController, - deviceId: _videoInput.selectedCameraId, - onInitialized: (controller) { - // This is where the controller actually gets born on macOS - _videoInput.setMacOSController(controller); - }, - ) - : (_videoInput.cameraController != null && - _videoInput.controllerInitialized) - ? FullCameraPreview( - controller: _videoInput.cameraController, - deviceId: _videoInput.selectedCameraId, - onInitialized: (controller) { - // Web/Mobile callback (often unused if controller passed in) - }, - ) - : const Center(child: CircularProgressIndicator()), - ), - Expanded( - child: ListView.builder( - controller: _scrollController, - itemBuilder: (context, idx) { - return MessageWidget( - text: _messages[idx].text, - image: _messages[idx].imageBytes != null - ? Image.memory( - _messages[idx].imageBytes!, - cacheWidth: 400, - cacheHeight: 400, - ) - : null, - isFromUser: _messages[idx].fromUser ?? false, - isThought: _messages[idx].isThought, - ); - }, - itemCount: _messages.length, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), - child: Row( - children: [ - Expanded( - child: TextField( - focusNode: _textFieldFocus, - controller: _textController, - onSubmitted: _sendTextPrompt, - ), - ), - const SizedBox.square( - dimension: 15, - ), - AudioVisualizer( - audioStreamIsActive: _recording, - amplitudeStream: _audioInput.amplitudeStream, - ), - const SizedBox.square( - dimension: 15, - ), - IconButton( - tooltip: 'Start Streaming', - onPressed: !_loading - ? () async { - await _setupSession(); - } - : null, - icon: Icon( - Icons.network_wifi, - color: _sessionOpening - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).colorScheme.primary, - ), - ), - IconButton( - tooltip: 'Send Stream Message', - onPressed: !_loading - ? () async { - if (_recording) { - await _stopRecording(); - } else { - await _startRecording(); - } - } - : null, - icon: Icon( - _recording ? Icons.stop : Icons.mic, - color: _loading - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).colorScheme.primary, - ), - ), - IconButton( - tooltip: 'Toggle Camera', - onPressed: _isCameraOn ? _stopVideoStream : _startVideoStream, - icon: Icon( - _isCameraOn ? Icons.videocam_off : Icons.videocam, - color: _loading - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).colorScheme.primary, - ), - ), - if (!_loading) - IconButton( - onPressed: () async { - await _sendTextPrompt(_textController.text); - }, - icon: Icon( - Icons.send, - color: Theme.of(context).colorScheme.primary, - ), - ) - else - const CircularProgressIndicator(), - ], - ), - ), - ], - ), - ); - } + await _session?.close(); + _session = null; + isSessionActive = false; - final lightControlTool = FunctionDeclaration( - 'setLightValues', - 'Set the brightness and color temperature of a room light.', - parameters: { - 'brightness': Schema.integer( - description: 'Light level from 0 to 100. ' - 'Zero is off and 100 is full brightness.', - ), - 'colorTemperature': Schema.string( - description: 'Color temperature of the light fixture, ' - 'which can be `daylight`, `cool` or `warm`.', - ), - }, - ); + isLoading = false; + notifyListeners(); + } - Future> _setLightValues({ - int? brightness, - String? colorTemperature, - }) async { - final apiResponse = { - 'colorTemprature': 'warm', - 'brightness': brightness, - }; - return apiResponse; + Future _sessionResume() async { + if (isSessionActive) { + await _stopSession(explicit: false); + await _startSession(explicit: false); + } } - Future _setupSession() async { - setState(() { - _loading = true; - }); - await _initAudio(); + Future _onAudioData(Uint8List data) async { + if (isSessionActive && _session != null) { + try { + await _session!.sendAudioRealtime(InlineDataPart('audio/pcm', data)); + } catch (e) { + developer.log('Error sending audio realtime: $e'); + // If we hit a closed socket, stop trying to send until reconnected + isMicOn = false; + notifyListeners(); + } + } + } - try { - if (!_videoIsInitialized) { - await _initVideo(); - } else { - await _videoInput.initializeCameraController(); + Future _onVideoData(Uint8List data, String mimeType) async { + if (isSessionActive && _session != null) { + try { + await _session!.sendVideoRealtime(InlineDataPart(mimeType, data)); + } catch (e) { + developer.log('Error sending video realtime: $e'); } - } catch (e) { - developer.log('Video Hardware init error: $e'); } + } - if (!_sessionOpening) { - _session = await _liveModel.connect(); - _sessionOpening = true; - unawaited( - _processMessagesContinuously(), - ); + Future toggleMic() async { + _intendedMicOn = !_intendedMicOn; + if (_intendedMicOn) { + await _startMicStream(); } else { - await _session.close(); - _sessionOpening = false; + await mediaManager.stopAudio(); + isMicOn = false; + notifyListeners(); } - - setState(() { - _loading = false; - }); } - Future _startRecording() async { - await _audioSubscription?.cancel(); - _audioSubscription = null; - setState(() { - _recording = true; - }); + Future _startMicStream() async { + if (!isSessionActive) { + isMicOn = true; + notifyListeners(); + return; + } try { - var inputStream = await _audioInput.startRecordingStream(); - await _audioOutput.playStream(); - if (inputStream != null) { - _audioSubscription = inputStream.listen( - (data) { - _session.sendAudioRealtime(InlineDataPart('audio/pcm', data)); - }, - onError: (e) { - developer.log('Audio Stream Error: $e'); - _stopRecording(); - }, - cancelOnError: true, - ); - } + await mediaManager.startAudio(_onAudioData); + isMicOn = true; + notifyListeners(); } catch (e) { - developer.log('bidi_page._startRecording(): $e'); - _showError('bidi_page._startRecording(): $e'); - setState(() => _recording = false); + onShowError?.call(e.toString()); + isMicOn = false; + notifyListeners(); } } - Future _stopRecording() async { - await _audioSubscription?.cancel(); - _audioSubscription = null; + Future _startCameraStream() async { + if (!isSessionActive) { + isCameraOn = true; + notifyListeners(); + return; + } try { - await _audioInput.stopRecording(); + await mediaManager.startVideo(_onVideoData); + isCameraOn = true; + notifyListeners(); } catch (e) { - _showError(e.toString()); + developer.log('Error starting video stream: $e'); + onShowError?.call(e.toString()); + isCameraOn = false; + notifyListeners(); } - - setState(() { - _recording = false; - }); } - Future _startVideoStream() async { - // 1. Re-entry Guard: Prevent multiple clicks while switching - if (_loading || !_videoIsInitialized) return; + Future toggleCamera() async { + if (isLoading) return; // Prevent multiple clicks + _intendedCameraOn = !_intendedCameraOn; - // 2. Capture the current recording state - bool wasRecording = _recording; - - setState(() { - _loading = true; // Lock the UI during the switch - }); + isLoading = true; + notifyListeners(); try { - if (wasRecording) { - await _stopRecording(); - } - - // 4. Wait for ripple/UI (Prevent freeze) - await Future.delayed(const Duration(milliseconds: 250)); - - // 5. Initialize Camera if needed - if (!_videoInput.controllerInitialized || - _videoInput.cameraController == null) { - await _videoInput.initializeCameraController(); - } + if (!_intendedCameraOn) { + await mediaManager.stopVideo(); + isCameraOn = false; + } else { + // Stop audio momentarily to prevent hijacking (Mac quirk workaround) + bool wasMicOn = isMicOn; + if (wasMicOn) await mediaManager.stopAudio(); - // 6. Mount Camera UI - setState(() { - _isCameraOn = true; - }); - - if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - // ✅ Because we set _cameraController to null in stopStreamingImages, - // this loop will now CORRECTLY wait for the new View to initialize. - int attempts = 0; - while (_videoInput.cameraController == null) { - if (attempts > 50) break; // 5 second timeout safety - await Future.delayed(const Duration(milliseconds: 100)); - attempts++; - } - } + await Future.delayed(const Duration(milliseconds: 250)); - // 7. Wait for Mac Camera to Settle (Prevent audio hijack) - await Future.delayed(const Duration(milliseconds: 1000)); + await _startCameraStream(); - // 8. CLEAN RESTART: Use the helper method! - // Only restart if we were recording before. - if (wasRecording) { - developer.log('Resuming audio session...'); - await _startRecording(); + // Restart Audio + if (wasMicOn) await _startMicStream(); } - - // 9. Start Video Stream - _videoInput.startStreamingImages().listen( - (data) { - String mimeType = 'image/jpeg'; - if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - if (data.length > 3 && data[0] == 0x89 && data[1] == 0x50) { - mimeType = 'image/png'; - } - } - _session.sendVideoRealtime(InlineDataPart(mimeType, data)); - }, - onError: (e) => developer.log('Video Stream Error: $e'), - ); } catch (e) { developer.log('Error switching to video: $e'); - _showError(e.toString()); + onShowError?.call(e.toString()); + isCameraOn = false; } finally { - // 10. Always unlock the UI - setState(() { - _loading = false; - }); + isLoading = false; + notifyListeners(); } } - Future _stopVideoStream() async { - await _videoInput.stopStreamingImages(); - setState(() { - _isCameraOn = false; - }); - } + Future sendTextPrompt(String textPrompt) async { + if (!isSessionActive || _session == null) return; + isLoading = true; + notifyListeners(); - Future _sendTextPrompt(String textPrompt) async { - setState(() { - _loading = true; - }); try { - //final prompt = Content.text(textPrompt); - // await _session.send(input: prompt, turnComplete: true); - await _session.sendTextRealtime(textPrompt); + await _session!.sendTextRealtime(textPrompt); } catch (e) { - _showError(e.toString()); + onShowError?.call(e.toString()); } - setState(() { - _loading = false; - }); + isLoading = false; + notifyListeners(); } Future _processMessagesContinuously() async { + if (_session == null) return; try { - await for (final message in _session.receive()) { - if (!mounted) break; + await for (final message in _session!.receive()) { await _handleLiveServerMessage(message); } } catch (e) { - _showError(e.toString()); + onShowError?.call(e.toString()); } } @@ -496,40 +406,6 @@ class _BidiPageState extends State { await _handleLiveServerContent(message); } - int? _handleTranscription( - Transcription? transcription, - int? messageIndex, - String prefix, - bool fromUser, - ) { - int? currentIndex = messageIndex; - if (transcription?.text != null) { - if (currentIndex != null) { - _messages[currentIndex] = _messages[currentIndex].copyWith( - text: '${_messages[currentIndex].text}${transcription!.text!}', - ); - } else { - _messages.add( - MessageData( - text: '$prefix${transcription!.text!}', - fromUser: fromUser, - ), - ); - currentIndex = _messages.length - 1; - } - if (transcription.finished ?? false) { - currentIndex = null; - setState(_scrollDown); - } else { - // Use a scheduled frame instead of an immediate setState - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) setState(() {}); - }); - } - } - return currentIndex; - } - _inputTranscriptionMessageIndex = _handleTranscription( message.inputTranscription, _inputTranscriptionMessageIndex, @@ -549,18 +425,66 @@ class _BidiPageState extends State { } else if (message is LiveServerToolCall && message.functionCalls != null) { await _handleLiveServerToolCall(message); } else if (message is GoingAwayNotice) { - developer.log('Session is going away in ${message.timeLeft} seconds'); + if (_activeSessionHandle != null) { + unawaited(_sessionResume()); + } + } else if (message is SessionResumptionUpdate && + message.resumable != null && + message.resumable!) { + _activeSessionHandle = message.newHandle; } } + int? _handleTranscription( + Transcription? transcription, + int? messageIndex, + String prefix, + bool fromUser, + ) { + int? currentIndex = messageIndex; + if (transcription?.text != null) { + if (currentIndex != null) { + messages[currentIndex] = messages[currentIndex].copyWith( + text: '${messages[currentIndex].text}${transcription!.text!}', + ); + } else { + messages.add( + MessageData( + text: '$prefix${transcription!.text!}', + fromUser: fromUser, + ), + ); + currentIndex = messages.length - 1; + } + + if (transcription.finished ?? false) { + currentIndex = null; + onScrollDown?.call(); + } else { + notifyListeners(); // Trigger UI rebuild for streaming text + } + } + return currentIndex; + } + Future _handleLiveServerContent(LiveServerContent response) async { final partList = response.modelTurn?.parts; if (partList != null) { for (final part in partList) { if (part is TextPart) { - await _handleTextPart(part); + messages.add( + MessageData( + text: part.text, + fromUser: false, + isThought: part.isThought ?? false, + ), + ); + onScrollDown?.call(); + notifyListeners(); } else if (part is InlineDataPart) { - await _handleInlineDataPart(part); + if (part.mimeType.startsWith('audio')) { + mediaManager.playAudioChunk(part.bytes); + } } else { developer.log('receive part with type ${part.runtimeType}'); } @@ -568,31 +492,6 @@ class _BidiPageState extends State { } } - Future _handleTextPart(TextPart part) async { - if (!_loading) { - setState(() { - _loading = true; - }); - } - _messages.add( - MessageData( - text: part.text, - fromUser: false, - isThought: part.isThought ?? false, - ), - ); - setState(() { - _loading = false; - _scrollDown(); - }); - } - - Future _handleInlineDataPart(InlineDataPart part) async { - if (part.mimeType.startsWith('audio')) { - _audioOutput.addDataToAudioStream(part.bytes); - } - } - Future _handleLiveServerToolCall(LiveServerToolCall response) async { final functionCalls = response.functionCalls!.toList(); if (functionCalls.isNotEmpty) { @@ -600,11 +499,15 @@ class _BidiPageState extends State { if (functionCall.name == 'setLightValues') { var color = functionCall.args['colorTemperature']! as String; var brightness = functionCall.args['brightness']! as int; - final functionResult = await _setLightValues( - brightness: brightness, - colorTemperature: color, - ); - await _session.sendToolResponse([ + + // Mock Tool Execution + final functionResult = { + 'colorTemperature': + color, // original had a typo, keeping to preserve functionality intent + 'brightness': brightness, + }; + + await _session?.sendToolResponse([ FunctionResponse( functionCall.name, functionResult, @@ -612,27 +515,109 @@ class _BidiPageState extends State { ), ]); } else { - throw UnimplementedError( - 'Function not declared to the model: ${functionCall.name}', - ); + throw UnimplementedError('Function not declared: ${functionCall.name}'); } } } + void simulateGoingAway() { + if (isSessionActive && _session != null) { + developer.log('Simulating GoingAwayNotice locally'); + _handleLiveServerMessage( + LiveServerResponse(message: const GoingAwayNotice(timeLeft: '10')), + ); + } + } + + @override + void dispose() { + _isDisposed = true; + _stopSession(explicit: true); + super.dispose(); + } + + static final _lightControlTool = FunctionDeclaration( + 'setLightValues', + 'Set the brightness and color temperature of a room light.', + parameters: { + 'brightness': Schema.integer( + description: + 'Light level from 0 to 100. Zero is off and 100 is full brightness.', + ), + 'colorTemperature': Schema.string( + description: + 'Color temperature of the light fixture, which can be `daylight`, `cool` or `warm`.', + ), + }, + ); +} + +// ============================================================================ +// UI WIDGET +// Isolates presentation, keeping state out of the visual hierarchy. +// ============================================================================ +class BidiPage extends StatefulWidget { + const BidiPage({ + super.key, + required this.title, + required this.model, + required this.useVertexBackend, + }); + + final String title; + final GenerativeModel model; + final bool useVertexBackend; + + @override + State createState() => _BidiPageState(); +} + +class _BidiPageState extends State { + late final BidiSessionController _controller; + final ScrollController _scrollController = ScrollController(); + final TextEditingController _textController = TextEditingController(); + final FocusNode _textFieldFocus = FocusNode(); + + @override + void initState() { + super.initState(); + _controller = BidiSessionController( + model: widget.model, + useVertexBackend: widget.useVertexBackend, + onShowError: _showError, + onScrollDown: _scrollDown, + ); + _controller.initialize(); + } + + @override + void dispose() { + _controller.dispose(); + _scrollController.dispose(); + _textController.dispose(); + _textFieldFocus.dispose(); + super.dispose(); + } + + void _scrollDown() { + if (!_scrollController.hasClients) return; + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + _scrollController.jumpTo(_scrollController.position.maxScrollExtent); + } + }); + } + void _showError(String message) { showDialog( context: context, builder: (context) { return AlertDialog( title: const Text('Something went wrong'), - content: SingleChildScrollView( - child: SelectableText(message), - ), + content: SingleChildScrollView(child: SelectableText(message)), actions: [ TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, + onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), ), ], @@ -640,4 +625,155 @@ class _BidiPageState extends State { }, ); } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Padding( + padding: const EdgeInsets.all(8), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Live Stream Session', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ElevatedButton.icon( + icon: const Icon(Icons.speed, size: 16), + label: const Text('Simulate GoAway'), + style: ElevatedButton.styleFrom( + visualDensity: VisualDensity.compact, + ), + onPressed: _controller.isSessionActive + ? () => _controller.simulateGoingAway() + : null, + ), + ], + ), + const SizedBox(height: 8), + if (_controller.isCameraOn) + Container( + height: 200, + color: Colors.black, + alignment: Alignment.center, + child: (_controller.mediaManager.cameraController != null && + _controller.mediaManager.controllerInitialized) + ? FullCameraPreview( + controller: _controller.mediaManager.cameraController, + deviceId: _controller.mediaManager.selectedCameraId, + onInitialized: (controller) {}, + ) + : const Center(child: CircularProgressIndicator()), + ), + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: _controller.messages.length, + itemBuilder: (context, idx) { + final message = _controller.messages[idx]; + return MessageWidget( + text: message.text, + image: message.imageBytes != null + ? Image.memory( + message.imageBytes!, + cacheWidth: 400, + cacheHeight: 400, + ) + : null, + isFromUser: message.fromUser ?? false, + isThought: message.isThought, + ); + }, + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(vertical: 25, horizontal: 15), + child: Row( + children: [ + Expanded( + child: TextField( + focusNode: _textFieldFocus, + controller: _textController, + onSubmitted: (text) { + _controller.sendTextPrompt(text); + _textController.clear(); + }, + ), + ), + const SizedBox.square(dimension: 15), + AudioVisualizer( + audioStreamIsActive: _controller.isMicOn, + amplitudeStream: _controller.mediaManager.amplitudeStream, + ), + const SizedBox.square(dimension: 15), + IconButton( + tooltip: 'Start Streaming', + onPressed: !_controller.isLoading + ? () => _controller.toggleSession() + : null, + icon: Icon( + Icons.network_wifi, + color: _controller.isSessionActive + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.primary, + ), + ), + IconButton( + tooltip: 'Send Stream Message', + onPressed: !_controller.isLoading + ? () => _controller.toggleMic() + : null, + icon: Icon( + _controller.isMicOn ? Icons.stop : Icons.mic, + color: _controller.isLoading + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.primary, + ), + ), + if (!(!kIsWeb && + defaultTargetPlatform == TargetPlatform.macOS)) + IconButton( + tooltip: 'Toggle Camera', + onPressed: !_controller.isLoading + ? () => _controller.toggleCamera() + : null, + icon: Icon( + _controller.isCameraOn + ? Icons.videocam_off + : Icons.videocam, + color: _controller.isLoading + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.primary, + ), + ), + if (!_controller.isLoading) + IconButton( + tooltip: 'Send Text', + onPressed: () { + _controller.sendTextPrompt(_textController.text); + _textController.clear(); + }, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.primary, + ), + ) + else + const CircularProgressIndicator(), + ], + ), + ), + ], + ), + ); + }, + ); + } } diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart index 8a98241001d4..6562420bbdc4 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/chat_page.dart @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; import '../widgets/message_widget.dart'; @@ -50,20 +49,17 @@ class _ChatPageState extends State { void _initializeChat() { final generationConfig = GenerationConfig( thinkingConfig: _enableThinking - ? ThinkingConfig.withThinkingBudget( - null, - includeThoughts: true, - ) // Using thinkingBudget since we are testing with gemini 2.5 + ? ThinkingConfig.withThinkingLevel(ThinkingLevel.medium) : null, ); if (widget.useVertexBackend) { - _model = FirebaseAI.vertexAI(auth: FirebaseAuth.instance).generativeModel( - model: 'gemini-2.5-flash', + _model = FirebaseAI.vertexAI(location: 'global').generativeModel( + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, ); } else { - _model = FirebaseAI.googleAI(auth: FirebaseAuth.instance).generativeModel( - model: 'gemini-2.5-flash', + _model = FirebaseAI.googleAI().generativeModel( + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, ); } @@ -153,6 +149,7 @@ class _ChatPageState extends State { Icons.send, color: Theme.of(context).colorScheme.primary, ), + tooltip: 'Send', ), IconButton( onPressed: () { @@ -162,6 +159,7 @@ class _ChatPageState extends State { Icons.stream, color: Theme.of(context).colorScheme.primary, ), + tooltip: 'Send Stream', ), ], ) diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/document.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/document.dart deleted file mode 100644 index db2715c402e0..000000000000 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/document.dart +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:flutter/material.dart'; -import 'package:firebase_ai/firebase_ai.dart'; -import 'package:flutter/services.dart'; -import '../widgets/message_widget.dart'; - -class DocumentPage extends StatefulWidget { - const DocumentPage({super.key, required this.title, required this.model}); - - final String title; - final GenerativeModel model; - - @override - State createState() => _DocumentPageState(); -} - -class _DocumentPageState extends State { - ChatSession? chat; - late final GenerativeModel model; - final List _messages = []; - bool _loading = false; - - @override - void initState() { - super.initState(); - chat = widget.model.startChat(); - } - - Future _testDocumentReading(model) async { - try { - ByteData docBytes = - await rootBundle.load('assets/documents/gemini_summary.pdf'); - - const _prompt = - 'Write me a summary in one sentence what this document is about.'; - - const prompt = TextPart(_prompt); - - setState(() { - _messages.add(MessageData(text: _prompt, fromUser: true)); - }); - - final pdfPart = - InlineDataPart('application/pdf', docBytes.buffer.asUint8List()); - - final response = await widget.model.generateContent([ - Content.multi([prompt, pdfPart]), - ]); - - setState(() { - _messages.add(MessageData(text: response.text, fromUser: false)); - }); - } catch (e) { - print('Error sending document to model: $e'); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: ListView.builder( - itemBuilder: (context, idx) { - return MessageWidget( - text: _messages[idx].text, - isFromUser: _messages[idx].fromUser ?? false, - ); - }, - itemCount: _messages.length, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), - child: Center( - child: SizedBox( - child: ElevatedButton( - onPressed: !_loading - ? () async { - await _testDocumentReading(widget.model); - } - : null, - child: const Text('Test Document Reading'), - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart index 633106664ba1..b6db853772ee 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/function_calling_page.dart @@ -14,7 +14,8 @@ import 'package:flutter/material.dart'; import 'package:firebase_ai/firebase_ai.dart'; -import 'package:firebase_auth/firebase_auth.dart'; + +import '../utils/function_call_utils.dart'; import '../widgets/message_widget.dart'; class FunctionCallingPage extends StatefulWidget { @@ -31,13 +32,6 @@ class FunctionCallingPage extends StatefulWidget { State createState() => _FunctionCallingPageState(); } -class Location { - final String city; - final String state; - - Location(this.city, this.state); -} - class _FunctionCallingPageState extends State { late GenerativeModel _functionCallModel; late GenerativeModel _autoFunctionCallModel; @@ -80,7 +74,7 @@ class _FunctionCallingPageState extends State { 'The date for which to get the weather. Date must be in the format: YYYY-MM-DD.', ), }, - callable: _fetchWeatherCallable, + callable: fetchWeatherCallable, ); _autoFindRestaurantsTool = AutoFunctionDeclaration( name: 'findRestaurants', @@ -231,16 +225,6 @@ class _FunctionCallingPageState extends State { }; } - Future> _fetchWeatherCallable( - Map args, - ) async { - final locationData = args['location']! as Map; - final city = locationData['city']! as String; - final state = locationData['state']! as String; - final date = args['date']! as String; - return fetchWeather(Location(city, state), date); - } - void _initializeModel() { final generationConfig = GenerationConfig( thinkingConfig: _enableThinking @@ -252,25 +236,25 @@ class _FunctionCallingPageState extends State { ); final aiClient = widget.useVertexBackend - ? FirebaseAI.vertexAI(auth: FirebaseAuth.instance) - : FirebaseAI.googleAI(auth: FirebaseAuth.instance); + ? FirebaseAI.vertexAI(location: 'global') + : FirebaseAI.googleAI(); _functionCallModel = aiClient.generativeModel( - model: 'gemini-2.5-flash', + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, tools: [ Tool.functionDeclarations([fetchWeatherTool]), ], ); _autoFunctionCallModel = aiClient.generativeModel( - model: 'gemini-2.5-flash', + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, tools: [ Tool.functionDeclarations([_autoFetchWeatherTool]), ], ); _parallelAutoFunctionCallModel = aiClient.generativeModel( - model: 'gemini-2.5-flash', + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, tools: [ Tool.functionDeclarations( @@ -279,21 +263,21 @@ class _FunctionCallingPageState extends State { ], ); _codeExecutionModel = aiClient.generativeModel( - model: 'gemini-2.5-flash', + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, tools: [ Tool.codeExecution(), ], ); _complexSchemaModel = aiClient.generativeModel( - model: 'gemini-2.5-flash', + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, tools: [ Tool.functionDeclarations([_autoPlanVacationTool]), ], ); _refDefJsonSchemaModel = aiClient.generativeModel( - model: 'gemini-2.5-flash', + model: 'gemini-3.1-flash-lite', generationConfig: generationConfig, tools: [ Tool.functionDeclarations([_autoProcessTransactionTool]), @@ -301,23 +285,6 @@ class _FunctionCallingPageState extends State { ); } - // This is a hypothetical API to return a fake weather data collection for - // certain location - Future> fetchWeather( - Location location, - String date, - ) async { - // TODO(developer): Call a real weather API. - // Mock response from the API. In developer live code this would call the - // external API and return what that API returns. - final apiResponse = { - 'temperature': 38, - 'chancePrecipitation': '56%', - 'cloudConditions': 'partly-cloudy', - }; - return apiResponse; - } - /// Actual function to demonstrate the function calling feature. final fetchWeatherTool = FunctionDeclaration( 'fetchWeather', diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/grounding_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/grounding_page.dart new file mode 100644 index 000000000000..6c5aad8fb6e0 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/grounding_page.dart @@ -0,0 +1,302 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import '../widgets/message_widget.dart'; + +class GroundingPage extends StatefulWidget { + const GroundingPage({ + super.key, + required this.title, + required this.useVertexBackend, + }); + + final String title; + final bool useVertexBackend; + + @override + State createState() => _GroundingPageState(); +} + +class _GroundingPageState extends State { + GenerativeModel? _model; + final ScrollController _scrollController = ScrollController(); + final TextEditingController _textController = TextEditingController(); + final TextEditingController _latController = TextEditingController(); + final TextEditingController _lngController = TextEditingController(); + final FocusNode _textFieldFocus = FocusNode(); + final List _messages = []; + + bool _loading = false; + bool _enableSearchGrounding = false; + bool _enableMapsGrounding = false; + + @override + void initState() { + super.initState(); + _latController.text = '37.422'; // Default Googleplex lat + _lngController.text = '-122.084'; // Default Googleplex lng + } + + void _initializeModel() { + List tools = []; + ToolConfig? toolConfig; + + if (_enableSearchGrounding) { + tools.add(Tool.googleSearch()); + } + + if (_enableMapsGrounding) { + tools.add(Tool.googleMaps()); + + final lat = double.tryParse(_latController.text); + final lng = double.tryParse(_lngController.text); + + if (lat != null && lng != null) { + toolConfig = ToolConfig( + retrievalConfig: RetrievalConfig( + latLng: LatLng(latitude: lat, longitude: lng), + ), + ); + } + } + + final aiProvider = widget.useVertexBackend + ? FirebaseAI.vertexAI(location: 'global') + : FirebaseAI.googleAI(); + + _model = aiProvider.generativeModel( + model: 'gemini-3.1-flash-lite', + tools: tools.isNotEmpty ? tools : null, + toolConfig: toolConfig, + ); + } + + void _scrollDown() { + WidgetsBinding.instance.addPostFrameCallback( + (_) => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 750), + curve: Curves.easeOutCirc, + ), + ); + } + + Future _sendPrompt(String message) async { + if (message.isEmpty) return; + + _initializeModel(); // Re-initialize before sending to capture current toggles + + setState(() { + _loading = true; + }); + + try { + _messages.add(MessageData(text: message, fromUser: true)); + + final response = await _model?.generateContent([Content.text(message)]); + + var text = response?.text; + + // Extract grounding metadata to display + final groundingMetadata = + response?.candidates.firstOrNull?.groundingMetadata; + if (groundingMetadata != null) { + final chunks = groundingMetadata.groundingChunks.map((chunk) { + if (chunk.web != null) { + final title = chunk.web!.title ?? chunk.web!.uri; + return '- [$title](${chunk.web!.uri})'; + } + if (chunk.maps != null) { + final title = chunk.maps!.title ?? chunk.maps!.uri; + return '- [${title ?? 'Maps Result'}](${chunk.maps!.uri ?? ''})'; + } + return '- Unknown chunk'; + }).join('\n'); + + if (chunks.isNotEmpty) { + text = '$text\n\n**Grounding Sources:**\n$chunks'; + } + } + + _messages.add(MessageData(text: text, fromUser: false)); + + if (text == null) { + _showError('No response from API.'); + return; + } + } catch (e) { + _showError(e.toString()); + } finally { + if (mounted) { + _textController.clear(); + setState(() { + _loading = false; + }); + _textFieldFocus.requestFocus(); + _scrollDown(); + } + } + } + + void _showError(String message) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Something went wrong'), + content: SingleChildScrollView( + child: SelectableText(message), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('OK'), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Padding( + padding: const EdgeInsets.all(8), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: SwitchListTile( + title: const Text( + 'Search Grounding', + style: TextStyle(fontSize: 12), + ), + value: _enableSearchGrounding, + onChanged: (bool value) { + setState(() { + _enableSearchGrounding = value; + }); + }, + ), + ), + Expanded( + child: SwitchListTile( + title: const Text( + 'Maps Grounding', + style: TextStyle(fontSize: 12), + ), + value: _enableMapsGrounding, + onChanged: (bool value) { + setState(() { + _enableMapsGrounding = value; + }); + }, + ), + ), + ], + ), + if (_enableMapsGrounding) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _latController, + decoration: + const InputDecoration(labelText: 'Latitude'), + keyboardType: const TextInputType.numberWithOptions( + decimal: true, + signed: true, + ), + ), + ), + const SizedBox(width: 16), + Expanded( + child: TextField( + controller: _lngController, + decoration: + const InputDecoration(labelText: 'Longitude'), + keyboardType: const TextInputType.numberWithOptions( + decimal: true, + signed: true, + ), + ), + ), + ], + ), + ), + const Divider(), + Expanded( + child: ListView.builder( + controller: _scrollController, + itemBuilder: (context, idx) { + final message = _messages[idx]; + return MessageWidget( + text: message.text, + isFromUser: message.fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, + horizontal: 15, + ), + child: Row( + children: [ + Expanded( + child: TextField( + autofocus: true, + focusNode: _textFieldFocus, + controller: _textController, + onSubmitted: _sendPrompt, + decoration: const InputDecoration( + hintText: 'Enter a prompt...', + ), + ), + ), + const SizedBox.square(dimension: 15), + if (!_loading) + IconButton( + onPressed: () { + _sendPrompt(_textController.text); + }, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.primary, + ), + ) + else + const CircularProgressIndicator(), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/image_generation_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/image_generation_page.dart new file mode 100644 index 000000000000..6206760bdfb1 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/image_generation_page.dart @@ -0,0 +1,280 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; + +import '../widgets/message_widget.dart'; + +class ImageGenerationPage extends StatefulWidget { + const ImageGenerationPage({ + super.key, + required this.title, + required this.useVertexBackend, + }); + + final String title; + final bool useVertexBackend; + + @override + State createState() => _ImageGenerationPageState(); +} + +class _ImageGenerationPageState extends State { + late GenerativeModel _model; + final ScrollController _scrollController = ScrollController(); + final TextEditingController _textController = TextEditingController(); + final FocusNode _textFieldFocus = FocusNode(); + final List _messages = []; + bool _loading = false; + ImageAspectRatio? _selectedAspectRatio; + ImageSize? _selectedImageSize; + + @override + void initState() { + super.initState(); + _initializeModel(); + } + + void _initializeModel() { + final aiClient = + widget.useVertexBackend ? FirebaseAI.vertexAI() : FirebaseAI.googleAI(); + + _model = aiClient.generativeModel( + model: 'gemini-2.5-flash-image', + generationConfig: GenerationConfig( + responseModalities: [ResponseModalities.text, ResponseModalities.image], + ), + ); + } + + void _scrollDown() { + WidgetsBinding.instance.addPostFrameCallback( + (_) => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 750), + curve: Curves.easeOutCirc, + ), + ); + } + + Future _generateImage(String prompt) async { + if (prompt.trim().isEmpty) return; + + setState(() { + _loading = true; + _messages.add(MessageData(text: prompt, fromUser: true)); + }); + _textController.clear(); + _scrollDown(); + + try { + final response = await _model.generateContent( + [Content.text(prompt)], + generationConfig: GenerationConfig( + responseModalities: [ + ResponseModalities.text, + ResponseModalities.image, + ], + imageConfig: ImageConfig( + aspectRatio: _selectedAspectRatio, + imageSize: _selectedImageSize, + ), + ), + ); + + String? textResponse = response.text; + Uint8List? imageBytes; + + if (response.inlineDataParts.isNotEmpty) { + imageBytes = response.inlineDataParts.first.bytes; + } + + setState(() { + _messages.add( + MessageData( + text: (textResponse ?? '') + + (imageBytes != null + ? '\nGenerated Image:' + : 'No picture generated'), + imageBytes: imageBytes, + fromUser: false, + ), + ); + }); + } catch (e) { + _showError(e.toString()); + } finally { + setState(() { + _loading = false; + }); + _scrollDown(); + _textFieldFocus.requestFocus(); + } + } + + void _showError(String message) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Something went wrong'), + content: SingleChildScrollView( + child: SelectableText(message), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Padding( + padding: const EdgeInsets.all(8), + child: Column( + children: [ + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: _messages.length, + itemBuilder: (context, index) { + final message = _messages[index]; + return MessageWidget( + text: message.text, + image: message.imageBytes == null + ? null + : Image.memory( + message.imageBytes!, + fit: BoxFit.contain, + ), + isFromUser: message.fromUser ?? false, + ); + }, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: DropdownButtonFormField( + initialValue: _selectedAspectRatio, + decoration: const InputDecoration( + labelText: 'Aspect Ratio', + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), + ), + items: [ + const DropdownMenuItem( + child: Text('Default'), + ), + ...ImageAspectRatio.values.map( + (e) => DropdownMenuItem( + value: e, + child: Text('${e.name} (${e.toJson()})'), + ), + ), + ], + onChanged: (value) { + setState(() { + _selectedAspectRatio = value; + }); + }, + ), + ), + const SizedBox(width: 15), + Expanded( + child: DropdownButtonFormField( + initialValue: _selectedImageSize, + decoration: const InputDecoration( + labelText: 'Image Size', + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), + ), + items: [ + const DropdownMenuItem( + child: Text('Default'), + ), + ...ImageSize.values.map( + (e) => DropdownMenuItem( + value: e, + child: Text('${e.name} (${e.toJson()})'), + ), + ), + ], + onChanged: (value) { + setState(() { + _selectedImageSize = value; + }); + }, + ), + ), + ], + ), + const SizedBox(height: 15), + Row( + children: [ + Expanded( + child: TextField( + autofocus: true, + focusNode: _textFieldFocus, + controller: _textController, + decoration: const InputDecoration( + hintText: 'Enter image prompt...', + ), + onSubmitted: _generateImage, + ), + ), + const SizedBox(width: 15), + if (!_loading) + IconButton( + onPressed: () => _generateImage(_textController.text), + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.primary, + ), + ) + else + const CircularProgressIndicator(), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart deleted file mode 100644 index 390279c1ffd2..000000000000 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/imagen_page.dart +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:typed_data'; - -import 'package:image_picker/image_picker.dart'; -import 'package:firebase_ai/firebase_ai.dart'; - -import 'package:flutter/material.dart'; -//import 'package:firebase_storage/firebase_storage.dart'; -import '../widgets/message_widget.dart'; -import '../utils/image_utils.dart'; - -class ImagenPage extends StatefulWidget { - const ImagenPage({ - super.key, - required this.title, - required this.model, - }); - - final String title; - final ImagenModel model; - - @override - State createState() => _ImagenPageState(); -} - -class _ImagenPageState extends State { - final ScrollController _scrollController = ScrollController(); - final TextEditingController _textController = TextEditingController(); - final FocusNode _textFieldFocus = FocusNode(); - final List _generatedContent = []; - bool _loading = false; - - // For image picking - ImagenInlineImage? _sourceImage; - ImagenInlineImage? _maskImageForEditing; - - void _scrollDown() { - WidgetsBinding.instance.addPostFrameCallback( - (_) => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: const Duration( - milliseconds: 750, - ), - curve: Curves.easeOutCirc, - ), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: ListView.builder( - controller: _scrollController, - itemBuilder: (context, idx) { - return MessageWidget( - text: _generatedContent[idx].text, - image: Image.memory( - _generatedContent[idx].imageBytes!, - cacheWidth: 400, - cacheHeight: 400, - ), - isFromUser: _generatedContent[idx].fromUser ?? false, - ); - }, - itemCount: _generatedContent.length, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), - child: Column( - children: [ - // Generate Image Row - Row( - children: [ - Expanded( - child: TextField( - autofocus: true, - focusNode: _textFieldFocus, - decoration: const InputDecoration( - hintText: 'Enter a prompt...', - ), - controller: _textController, - ), - ), - const SizedBox.square(dimension: 15), - IconButton( - onPressed: () async { - await _pickSourceImage(); - }, - icon: Icon( - Icons.add_a_photo, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Pick Source Image', - ), - IconButton( - onPressed: () async { - await _pickMaskImage(); - }, - icon: Icon( - Icons.add_to_photos, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Pick mask', - ), - IconButton( - onPressed: () async { - await _editWithMask(); - }, - icon: Icon( - Icons.brush, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Edit with Mask', - ), - IconButton( - onPressed: () async { - await _editWithStyle(); - }, - icon: Icon( - Icons.edit, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Edit with Style', - ), - IconButton( - onPressed: () async { - await _outpaintImage(); - }, - icon: Icon( - Icons.masks, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Outpaint', - ), - IconButton( - onPressed: () async { - await _inpaintImageHappyPath(); - }, - icon: Icon( - Icons.plus_one, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Inpaint', - ), - if (!_loading) - IconButton( - onPressed: () async { - await _generateImageFromPrompt( - _textController.text, - ); - }, - icon: Icon( - Icons.image_search, - color: Theme.of(context).colorScheme.primary, - ), - tooltip: 'Generate Image', - ) - else - const CircularProgressIndicator(), - ], - ), - ], - ), - ), - ], - ), - ), - ); - } - - Future _pickImage() async { - final ImagePicker picker = ImagePicker(); - try { - final XFile? imageFile = - await picker.pickImage(source: ImageSource.gallery); - if (imageFile != null) { - // Attempt to get mimeType, default if null. - // Note: imageFile.mimeType might be null on some platforms or for some files. - final String mimeType = imageFile.mimeType ?? 'image/jpeg'; - final Uint8List imageBytes = await imageFile.readAsBytes(); - return ImagenInlineImage( - bytesBase64Encoded: imageBytes, - mimeType: mimeType, - ); - } - } catch (e) { - _showError('Error picking image: $e'); - } - return null; - } - - Future _pickSourceImage() async { - final pickedImage = await _pickImage(); - if (pickedImage != null) { - setState(() { - _sourceImage = pickedImage; - }); - } - } - - Future _pickMaskImage() async { - final pickedImage = await _pickImage(); - if (pickedImage != null) { - setState(() { - _maskImageForEditing = pickedImage; - }); - } - } - - Future _inpaintImageHappyPath() async { - if (_sourceImage == null) { - _showError('Please pick a source image for inpaint insertion.'); - return; - } - setState(() { - _loading = true; - }); - - final String prompt = _textController.text; - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: 'Try to inpaint image with prompt: $prompt', - fromUser: true, - ); - - MessageData? resultMessage; - - try { - // ignore: experimental_member_use - final response = await widget.model.inpaintImage( - _sourceImage!, - prompt, - // ignore: experimental_member_use - ImagenBackgroundMask(), - // ignore: experimental_member_use - config: ImagenEditingConfig(editMode: ImagenEditMode.inpaintInsertion), - ); - if (response.images.isNotEmpty) { - final inpaintImage = response.images[0]; - resultMessage = MessageData( - imageBytes: inpaintImage.bytesBase64Encoded, - text: 'Inpaint image result with prompt: $prompt', - fromUser: false, - ); - } else { - _showError('No image was returned from inpaint.'); - } - } catch (e) { - _showError('Error inpaint image: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - _loading = false; - _scrollDown(); - }); - } - - Future _editWithMask() async { - if (_sourceImage == null) { - _showError('Please pick a source image for editing.'); - return; - } - if (_maskImageForEditing == null) { - _showError('Please pick a mask image for editing.'); - return; - } - - setState(() { - _loading = true; - }); - - final String prompt = _textController.text; - // Create a message to show what we are doing - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: 'Editing image with mask and prompt: $prompt', - fromUser: true, - ); - - MessageData? resultMessage; - - try { - // ignore: experimental_member_use - final response = await widget.model.editImage( - [ - // ignore: experimental_member_use - ImagenRawImage(image: _sourceImage!), - // ignore: experimental_member_use - ImagenRawMask(mask: _maskImageForEditing!), - ], - prompt, - ); - - if (response.images.isNotEmpty) { - final editedImage = response.images[0]; - resultMessage = MessageData( - imageBytes: editedImage.bytesBase64Encoded, - text: 'Edited image result with prompt: $prompt', - fromUser: false, - ); - } else { - _showError('No image was returned from editing with mask.'); - } - } catch (e) { - _showError('Error editing image with mask: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - _loading = false; - _scrollDown(); - }); - } - - Future _outpaintImage() async { - if (_sourceImage == null) { - _showError('Please pick a source image for outpainting.'); - return; - } - setState(() { - _loading = true; - }); - - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: 'Outpaint the picture to 1400*1400', - fromUser: true, - ); - - MessageData? resultMessage; - try { - final referenceImages = await generateMaskAndPadForOutpainting( - image: _sourceImage!, - // ignore: experimental_member_use - newDimensions: ImagenDimensions(width: 1400, height: 1400), - ); - // ignore: experimental_member_use - final response = await widget.model.editImage( - referenceImages, - '', - // ignore: experimental_member_use - config: ImagenEditingConfig(editMode: ImagenEditMode.outpaint), - ); - if (response.images.isNotEmpty) { - final editedImage = response.images[0]; - resultMessage = MessageData( - imageBytes: editedImage.bytesBase64Encoded, - text: 'Edited image Outpaint 1400*1400', - fromUser: false, - ); - } else { - _showError('No image was returned from editing.'); - } - } catch (e) { - _showError('Error editing image: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - _loading = false; - _scrollDown(); - }); - } - - Future _editWithStyle() async { - if (_sourceImage == null) { - _showError('Please pick a source image for style editing.'); - return; - } - setState(() { - _loading = true; - }); - - final String prompt = _textController.text; - final promptMessage = MessageData( - imageBytes: _sourceImage!.bytesBase64Encoded, - text: prompt, - fromUser: true, - ); - MessageData? resultMessage; - try { - // ignore: experimental_member_use - final response = await widget.model.editImage( - [ - // ignore: experimental_member_use - ImagenStyleReference( - image: _sourceImage!, - description: 'van goh style', - referenceId: 1, - ), - ], - prompt, - // ignore: experimental_member_use - config: ImagenEditingConfig(editSteps: 50), - ); - if (response.images.isNotEmpty) { - final editedImage = response.images[0]; - - resultMessage = MessageData( - imageBytes: editedImage.bytesBase64Encoded, - text: 'Edited image with style: $prompt', - fromUser: false, - ); - } else { - _showError('No image was returned from style editing.'); - } - } catch (e) { - _showError('Error performing style edit: $e'); - } - - setState(() { - _generatedContent.add(promptMessage); - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - - _loading = false; - _scrollDown(); - }); - } - - Future _generateImageFromPrompt(String prompt) async { - setState(() { - _loading = true; - }); - MessageData? resultMessage; - try { - var response = await widget.model.generateImages(prompt); - - if (response.images.isNotEmpty) { - var imagenImage = response.images[0]; - - resultMessage = MessageData( - imageBytes: imagenImage.bytesBase64Encoded, - text: prompt, - fromUser: false, - ); - } else { - // Handle the case where no images were generated - _showError('Error: No images were generated.'); - } - } catch (e) { - _showError(e.toString()); - } - - setState(() { - if (resultMessage != null) { - _generatedContent.add(resultMessage); - } - - _loading = false; - _scrollDown(); - }); - } - // NOTE: Keep this API private until future release. - // Future _testImagenGCS(String prompt) async { - // setState(() { - // _loading = true; - // }); - // var gcsUrl = 'gs://vertex-ai-example-ef5a2.appspot.com/imagen'; - - // var response = await widget.model.generateImagesGCS(prompt, gcsUrl); - - // if (response.images.isNotEmpty) { - // var imagenImage = response.images[0]; - // final returnImageUri = imagenImage.gcsUri; - // final reference = FirebaseStorage.instance.refFromURL(returnImageUri); - // final downloadUrl = await reference.getDownloadURL(); - // // Process the image - // _generatedContent.add( - // MessageData( - // image: Image(image: NetworkImage(downloadUrl)), - // text: prompt, - // fromUser: false, - // ), - // ); - // } else { - // // Handle the case where no images were generated - // _showError('Error: No images were generated.'); - // } - // setState(() { - // _loading = false; - // }); - // } - - void _showError(String message) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text('Something went wrong'), - content: SingleChildScrollView( - child: SelectableText(message), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('OK'), - ), - ], - ); - }, - ); - } -} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/multimodal_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/multimodal_page.dart new file mode 100644 index 000000000000..9c559abdaada --- /dev/null +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/multimodal_page.dart @@ -0,0 +1,317 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:flutter/services.dart'; +import '../widgets/message_widget.dart'; +import 'package:record/record.dart'; +import 'package:path_provider/path_provider.dart'; + +final record = AudioRecorder(); + +class MultimodalPage extends StatefulWidget { + const MultimodalPage({super.key, required this.title, required this.model}); + + final String title; + final GenerativeModel model; + + @override + State createState() => _MultimodalPageState(); +} + +class _MultimodalPageState extends State { + final ScrollController _scrollController = ScrollController(); + final List _messages = []; + bool _recording = false; + bool _loading = false; + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + Future recordAudio() async { + if (!await record.hasPermission()) { + debugPrint('Audio recording permission denied'); + return; + } + + final dir = Directory( + '${(await getApplicationDocumentsDirectory()).path}/libs/recordings', + ); + + await dir.create(recursive: true); + + String filePath = + '${dir.path}/recording_${DateTime.now().millisecondsSinceEpoch}.wav'; + + await record.start( + const RecordConfig( + encoder: AudioEncoder.wav, + ), + path: filePath, + ); + } + + Future stopRecord() async { + var path = await record.stop(); + + if (path == null) { + debugPrint('Failed to stop recording'); + return; + } + + debugPrint('Recording saved to: $path'); + + try { + File file = File(path); + final audio = await file.readAsBytes(); + debugPrint('Audio file size: ${audio.length} bytes'); + + final audioPart = InlineDataPart('audio/wav', audio); + + await _submitAudioToModel(audioPart); + + await file.delete(); + debugPrint('Recording deleted successfully.'); + } catch (e) { + debugPrint('Error processing recording: $e'); + } + } + + Future _submitAudioToModel(InlineDataPart audioPart) async { + try { + String textPrompt = 'What is in the audio recording?'; + const prompt = TextPart('What is in the audio recording?'); + + setState(() { + _messages.add(MessageData(text: textPrompt, fromUser: true)); + _loading = true; + }); + + final response = await widget.model.generateContent([ + Content.multi([prompt, audioPart]), + ]); + + setState(() { + _messages.add(MessageData(text: response.text, fromUser: false)); + _loading = false; + }); + + _scrollToBottom(); + } catch (e) { + debugPrint('Error sending audio to model: $e'); + setState(() { + _loading = false; + }); + } + } + + Future _testVideo() async { + try { + setState(() { + _loading = true; + }); + + ByteData videoBytes = + await rootBundle.load('assets/videos/landscape.mp4'); + + const promptText = 'Can you tell me what is in the video?'; + + setState(() { + _messages.add(MessageData(text: promptText, fromUser: true)); + }); + + final videoPart = + InlineDataPart('video/mp4', videoBytes.buffer.asUint8List()); + + final response = await widget.model.generateContent([ + Content.multi([const TextPart(promptText), videoPart]), + ]); + + setState(() { + _messages.add(MessageData(text: response.text, fromUser: false)); + _loading = false; + }); + + _scrollToBottom(); + } catch (e) { + debugPrint('Error sending video to model: $e'); + setState(() { + _loading = false; + }); + } + } + + Future _testDocumentReading() async { + try { + setState(() { + _loading = true; + }); + + ByteData docBytes = + await rootBundle.load('assets/documents/gemini_summary.pdf'); + + const promptText = + 'Write me a summary in one sentence what this document is about.'; + + setState(() { + _messages.add(MessageData(text: promptText, fromUser: true)); + }); + + final pdfPart = + InlineDataPart('application/pdf', docBytes.buffer.asUint8List()); + + final response = await widget.model.generateContent([ + Content.multi([const TextPart(promptText), pdfPart]), + ]); + + setState(() { + _messages.add(MessageData(text: response.text, fromUser: false)); + _loading = false; + }); + + _scrollToBottom(); + } catch (e) { + debugPrint('Error sending document to model: $e'); + setState(() { + _loading = false; + }); + } + } + + void _scrollToBottom() { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollController.hasClients) { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Padding( + padding: const EdgeInsets.all(8), + child: Column( + children: [ + Expanded( + child: ListView.builder( + controller: _scrollController, + itemBuilder: (context, idx) { + return MessageWidget( + text: _messages[idx].text, + isFromUser: _messages[idx].fromUser ?? false, + ); + }, + itemCount: _messages.length, + ), + ), + if (_loading) + const Padding( + padding: EdgeInsets.all(8), + child: CircularProgressIndicator(), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 10, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + onPressed: _loading + ? null + : () async { + setState(() { + _recording = !_recording; + }); + if (_recording) { + await recordAudio(); + } else { + await stopRecord(); + } + }, + icon: Icon( + Icons.mic, + color: _recording + ? Colors.red + : Theme.of(context).colorScheme.primary, + ), + iconSize: 32, + ), + Text( + _recording ? 'Stop' : 'Record', + style: const TextStyle(fontSize: 12), + ), + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + onPressed: _loading ? null : _testVideo, + icon: Icon( + Icons.video_collection, + color: Theme.of(context).colorScheme.primary, + ), + iconSize: 32, + ), + const Text( + 'Test Video', + style: TextStyle(fontSize: 12), + ), + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + onPressed: _loading ? null : _testDocumentReading, + icon: Icon( + Icons.edit_document, + color: Theme.of(context).colorScheme.primary, + ), + iconSize: 32, + ), + const Text( + 'Test Doc', + style: TextStyle(fontSize: 12), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart index 1be4e6d11ffe..ad351e4778c7 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/server_template_page.dart @@ -14,6 +14,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import '../utils/function_call_utils.dart'; import '../widgets/message_widget.dart'; import 'package:firebase_ai/firebase_ai.dart'; @@ -40,8 +41,11 @@ class _ServerTemplatePageState extends State { // ignore: experimental_member_use TemplateGenerativeModel? _templateGenerativeModel; - // ignore: experimental_member_use - TemplateImagenModel? _templateImagenModel; + + TemplateChatSession? _chatSession; + TemplateChatSession? _chatFunctionOverrideSession; + TemplateChatSession? _chatAutoFunctionSession; + TemplateChatSession? _chatStreamFunctionSession; @override void initState() { @@ -54,26 +58,89 @@ class _ServerTemplatePageState extends State { _templateGenerativeModel = // ignore: experimental_member_use FirebaseAI.vertexAI(location: 'global').templateGenerativeModel(); - _templateImagenModel = - // ignore: experimental_member_use - FirebaseAI.vertexAI(location: 'global').templateImagenModel(); } else { _templateGenerativeModel = // ignore: experimental_member_use FirebaseAI.googleAI().templateGenerativeModel(); - _templateImagenModel = - // ignore: experimental_member_use - FirebaseAI.googleAI().templateImagenModel(); } + + // Inputs are now provided ONCE here when creating the session + _chatSession = _templateGenerativeModel?.startChat( + 'chat_history.prompt', + inputs: {}, + ); + _chatFunctionOverrideSession = _templateGenerativeModel?.startChat( + 'cj-function-calling-weather-override', + inputs: {}, + tools: [ + TemplateTool.functionDeclarations([ + TemplateFunctionDeclaration( + 'fetchWeather', + parameters: { + 'location': JSONSchema.object( + description: + 'The name of the city and its state for which to get ' + 'the weather. Only cities in the USA are supported.', + properties: { + 'city': JSONSchema.string( + description: 'The city of the location.', + ), + 'state': JSONSchema.string( + description: 'The state of the location.', + ), + 'zipCode': JSONSchema.integer( + description: 'Optional zip code of the location.', + nullable: true, + ), + }, + optionalProperties: ['zipCode'], + ), + 'date': JSONSchema.string( + description: 'The date for which to get the weather. ' + 'Date must be in the format: YYYY-MM-DD.', + ), + 'unit': JSONSchema.enumString( + enumValues: ['CELSIUS', 'FAHRENHEIT'], + description: 'The temperature unit.', + nullable: true, + ), + }, + optionalParameters: ['unit'], + ), + ]), + ], + ); + _chatAutoFunctionSession = _templateGenerativeModel?.startChat( + 'cj-function-calling-weather', + inputs: {}, + tools: [ + TemplateTool.functionDeclarations([ + TemplateAutoFunctionDeclaration( + name: 'fetchWeather', + callable: fetchWeatherCallable, + ), + ]), + ], + ); + _chatStreamFunctionSession = _templateGenerativeModel?.startChat( + 'cj-function-calling-weather-stream', + inputs: {}, + tools: [ + TemplateTool.functionDeclarations([ + TemplateAutoFunctionDeclaration( + name: 'fetchWeather', + callable: fetchWeatherCallable, + ), + ]), + ], + ); } void _scrollDown() { WidgetsBinding.instance.addPostFrameCallback( (_) => _scrollController.animateTo( _scrollController.position.maxScrollExtent, - duration: const Duration( - milliseconds: 750, - ), + duration: const Duration(milliseconds: 750), curve: Curves.easeOutCirc, ), ); @@ -82,9 +149,7 @@ class _ServerTemplatePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), + appBar: AppBar(title: Text(widget.title)), body: Padding( padding: const EdgeInsets.all(8), child: Column( @@ -112,10 +177,7 @@ class _ServerTemplatePageState extends State { ), ), Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 15), child: Row( children: [ Expanded( @@ -126,20 +188,58 @@ class _ServerTemplatePageState extends State { onSubmitted: _sendServerTemplateMessage, ), ), - const SizedBox.square( - dimension: 15, - ), + const SizedBox.square(dimension: 15), if (!_loading) ...[ - IconButton( - onPressed: () async { - await _serverTemplateImagen(_textController.text); - }, - icon: Icon( - Icons.image_search, - color: Theme.of(context).colorScheme.primary, + if (!_loading) + IconButton( + onPressed: () async { + await _serverTemplateAutoFunctionCall( + _textController.text, + ); + }, + icon: Icon( + Icons.auto_mode, + color: Theme.of(context).colorScheme.primary, + ), + tooltip: 'Auto Function Calling', + ), + if (!_loading) + IconButton( + onPressed: () async { + await _serverTemplateFunctionCall( + _textController.text, + ); + }, + icon: Icon( + Icons.functions, + color: Theme.of(context).colorScheme.primary, + ), + tooltip: 'Function Calling (client override)', + ), + if (!_loading) + IconButton( + onPressed: () async { + await _serverTemplateAutoStreamFunctionCall( + _textController.text, + ); + }, + icon: Icon( + Icons.smart_toy, + color: Theme.of(context).colorScheme.primary, + ), + tooltip: 'Auto Stream Function Calling', + ), + if (!_loading) + IconButton( + onPressed: () async { + await _serverTemplateChat(_textController.text); + }, + icon: Icon( + Icons.chat, + color: Theme.of(context).colorScheme.primary, + ), + tooltip: 'Chat', ), - tooltip: 'Imagen', - ), IconButton( onPressed: () async { await _serverTemplateImageInput(_textController.text); @@ -160,6 +260,18 @@ class _ServerTemplatePageState extends State { ), tooltip: 'URL Context', ), + IconButton( + onPressed: () async { + await _serverTemplateMapsGrounding( + _textController.text, + ); + }, + icon: Icon( + Icons.map, + color: Theme.of(context).colorScheme.primary, + ), + tooltip: 'Maps Grounding', + ), IconButton( onPressed: () async { await _sendServerTemplateMessage(_textController.text); @@ -189,13 +301,31 @@ class _ServerTemplatePageState extends State { ); } - Future _serverTemplateUrlContext(String message) async { + Future _handleServerTemplateMessage( + String message, + Future Function(String) generateContent, + ) async { setState(() { _loading = true; }); try { _messages.add(MessageData(text: message, fromUser: true)); + await generateContent(message); + } catch (e) { + _showError(e.toString()); + } finally { + _textController.clear(); + setState(() { + _loading = false; + }); + _textFieldFocus.requestFocus(); + _scrollDown(); + } + } + + Future _serverTemplateUrlContext(String message) async { + await _handleServerTemplateMessage(message, (message) async { var response = await _templateGenerativeModel // ignore: experimental_member_use ?.generateContent('cj-urlcontext', inputs: {'url': message}); @@ -235,93 +365,157 @@ class _ServerTemplatePageState extends State { } _messages.add(MessageData(text: buffer.toString(), fromUser: false)); } - - setState(() { - _loading = false; - _scrollDown(); - }); - } catch (e) { - _showError(e.toString()); - setState(() { - _loading = false; - }); - } finally { - _textController.clear(); - setState(() { - _loading = false; - }); - _textFieldFocus.requestFocus(); - } + }); } - Future _serverTemplateImagen(String message) async { - setState(() { - _loading = true; + Future _serverTemplateMapsGrounding(String message) async { + await _handleServerTemplateMessage(message, (message) async { + var response = await _templateGenerativeModel + // ignore: experimental_member_use + ?.generateContent( + 'cj-googlemaps', + inputs: {'question': message}, + toolConfig: TemplateToolConfig( + retrievalConfig: RetrievalConfig( + latLng: LatLng(latitude: 37.422, longitude: -122.084), // Googleplex + ), + ), + ); + + final candidate = response?.candidates.first; + if (candidate == null) { + _messages.add(MessageData(text: 'No response', fromUser: false)); + } else { + final responseText = candidate.text ?? ''; + final groundingMetadata = candidate.groundingMetadata; + + final buffer = StringBuffer(responseText); + if (groundingMetadata != null) { + buffer.writeln('\n\n--- Grounding Metadata ---'); + buffer.writeln('Grounding Chunks:'); + for (final chunk in groundingMetadata.groundingChunks) { + if (chunk.web != null) { + buffer.writeln(' - Web Chunk:'); + buffer.writeln(' - Title: ${chunk.web!.title}'); + buffer.writeln(' - URI: ${chunk.web!.uri}'); + } + if (chunk.maps != null) { + buffer.writeln(' - Maps Chunk:'); + buffer.writeln(' - Title: ${chunk.maps!.title}'); + buffer.writeln(' - URI: ${chunk.maps!.uri}'); + } + } + } + + _messages.add(MessageData(text: buffer.toString(), fromUser: false)); + } }); - MessageData? resultMessage; - try { - _messages.add(MessageData(text: message, fromUser: true)); - // ignore: experimental_member_use - var response = await _templateImagenModel?.generateImages( - 'portrait-googleai', - inputs: { - 'animal': message, - }, + } + + Future _serverTemplateAutoFunctionCall(String message) async { + await _handleServerTemplateMessage(message, (message) async { + // Inputs are no longer passed during sendMessage + var response = await _chatAutoFunctionSession?.sendMessage( + Content.text(message), ); - if (response!.images.isNotEmpty) { - var imagenImage = response.images[0]; + _messages.add(MessageData(text: response?.text, fromUser: false)); + }); + } - resultMessage = MessageData( - imageBytes: imagenImage.bytesBase64Encoded, - text: message, - fromUser: false, - ); - } else { - // Handle the case where no images were generated - _showError('Error: No images were generated.'); + Future _serverTemplateFunctionCall(String message) async { + await _handleServerTemplateMessage(message, (message) async { + // Inputs are no longer passed during sendMessage + var response = await _chatFunctionOverrideSession?.sendMessage( + Content.text(message), + ); + + _messages.add(MessageData(text: response?.text, fromUser: false)); + final functionCalls = response?.functionCalls.toList(); + if (functionCalls!.isNotEmpty) { + final functionCall = functionCalls.first; + if (functionCall.name == 'fetchWeather') { + final location = + functionCall.args['location']! as Map; + final date = functionCall.args['date']! as String; + final city = location['city'] as String; + final state = location['state'] as String; + final functionResult = await fetchWeather( + Location(city, state), + date, + ); + + // Respond to the function call + var functionResponse = + await _chatFunctionOverrideSession?.sendMessage( + Content.functionResponse(functionCall.name, functionResult), + ); + _messages.add( + MessageData(text: functionResponse?.text, fromUser: false), + ); + } } + }); + } - setState(() { - if (resultMessage != null) { - _messages.add(resultMessage); + Future _serverTemplateAutoStreamFunctionCall(String message) async { + await _handleServerTemplateMessage(message, (message) async { + var responseStream = _chatStreamFunctionSession?.sendMessageStream( + Content.text(message), + ); + + var accumulatedText = ''; + MessageData? modelMessage; + + if (responseStream != null) { + await for (final response in responseStream) { + if (response.text case final text?) { + accumulatedText += text; + if (modelMessage == null) { + modelMessage = MessageData( + text: accumulatedText, + fromUser: false, + ); + _messages.add(modelMessage); + } else { + modelMessage = modelMessage.copyWith(text: accumulatedText); + _messages.last = modelMessage; + } + setState(() {}); + } } - _loading = false; - _scrollDown(); - }); - } catch (e) { - _showError(e.toString()); - setState(() { - _loading = false; - }); - } finally { - _textController.clear(); - setState(() { - _loading = false; - }); - _textFieldFocus.requestFocus(); - } + } + + if (accumulatedText.isEmpty) { + _messages.add( + MessageData(text: 'No text response from model.', fromUser: false), + ); + } + }); } - Future _serverTemplateImageInput(String message) async { - setState(() { - _loading = true; + Future _serverTemplateChat(String message) async { + await _handleServerTemplateMessage(message, (message) async { + // Inputs are no longer passed during sendMessage + var response = await _chatSession?.sendMessage(Content.text(message)); + + var text = response?.text; + + _messages.add(MessageData(text: text, fromUser: false)); }); + } - try { + Future _serverTemplateImageInput(String message) async { + await _handleServerTemplateMessage(message, (message) async { ByteData catBytes = await rootBundle.load('assets/images/cat.jpg'); var imageBytes = catBytes.buffer.asUint8List(); _messages.add( - MessageData( - text: message, - imageBytes: imageBytes, - fromUser: true, - ), + MessageData(text: message, imageBytes: imageBytes, fromUser: true), ); // ignore: experimental_member_use var response = await _templateGenerativeModel?.generateContent( - 'media.prompt', + 'media', inputs: { 'imageData': { 'isInline': true, @@ -331,23 +525,7 @@ class _ServerTemplatePageState extends State { }, ); _messages.add(MessageData(text: response?.text, fromUser: false)); - - setState(() { - _loading = false; - _scrollDown(); - }); - } catch (e) { - _showError(e.toString()); - setState(() { - _loading = false; - }); - } finally { - _textController.clear(); - setState(() { - _loading = false; - }); - _textFieldFocus.requestFocus(); - } + }); } Future _sendServerTemplateMessage(String message) async { @@ -362,11 +540,6 @@ class _ServerTemplatePageState extends State { ?.generateContent('new-greeting', inputs: {}); _messages.add(MessageData(text: response?.text, fromUser: false)); - - setState(() { - _loading = false; - _scrollDown(); - }); } catch (e) { _showError(e.toString()); setState(() { @@ -387,8 +560,9 @@ class _ServerTemplatePageState extends State { }); try { - _messages - .add(MessageData(text: 'Testing code execution', fromUser: true)); + _messages.add( + MessageData(text: 'Testing code execution', fromUser: true), + ); final response = await _templateGenerativeModel // ignore: experimental_member_use ?.generateContent('cj-code-execution', inputs: {}); @@ -411,12 +585,7 @@ class _ServerTemplatePageState extends State { } if (buffer.isNotEmpty) { - _messages.add( - MessageData( - text: buffer.toString(), - fromUser: false, - ), - ); + _messages.add(MessageData(text: buffer.toString(), fromUser: false)); } setState(() { @@ -443,9 +612,7 @@ class _ServerTemplatePageState extends State { builder: (context) { return AlertDialog( title: const Text('Something went wrong'), - content: SingleChildScrollView( - child: SelectableText(message), - ), + content: SingleChildScrollView(child: SelectableText(message)), actions: [ TextButton( onPressed: () { diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/tts_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/tts_page.dart new file mode 100644 index 000000000000..41b221c193c1 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/example/lib/pages/tts_page.dart @@ -0,0 +1,487 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:firebase_ai/firebase_ai.dart'; +import 'package:waveform_flutter/waveform_flutter.dart'; +import '../utils/audio_output.dart'; +import '../widgets/audio_visualizer.dart'; + +class TTSPage extends StatefulWidget { + const TTSPage({ + super.key, + required this.title, + required this.useVertexBackend, + }); + + final String title; + final bool useVertexBackend; + + @override + State createState() => _TTSPageState(); +} + +class _TTSPageState extends State { + final AudioOutput _audioOutput = AudioOutput(); + final MockAmplitudeGenerator _mockAmpGen = MockAmplitudeGenerator(); + + bool _isMultiSpeaker = false; + bool _loading = false; + bool _isPlaying = false; + String? _responseText; + + // Single Speaker Controller + final TextEditingController _singlePromptController = TextEditingController( + text: 'Say cheerfully: Have a wonderful day!', + ); + String _selectedVoice = 'Kore'; + + // Multi Speaker Controllers + final TextEditingController _speaker1NameController = + TextEditingController(text: 'Joe'); + final TextEditingController _speaker1LineController = TextEditingController( + text: "How's it going today Jane?", + ); + String _speaker1Voice = 'Kore'; + final TextEditingController _speaker2NameController = + TextEditingController(text: 'Jane'); + final TextEditingController _speaker2LineController = TextEditingController( + text: 'Not too bad, how about you?', + ); + String _speaker2Voice = 'Puck'; + + final List _availableVoices = [ + 'Kore', + 'Puck', + 'Fenrir', + 'Aoede', + 'Charon', + 'Leda', + ]; + + Stream? _amplitudeStream; + Timer? _playbackTimer; + + @override + void initState() { + super.initState(); + _audioOutput.init(); + } + + @override + void dispose() { + _audioOutput.dispose(); + _mockAmpGen.stop(); + _playbackTimer?.cancel(); + _singlePromptController.dispose(); + _speaker1NameController.dispose(); + _speaker1LineController.dispose(); + _speaker2NameController.dispose(); + _speaker2LineController.dispose(); + super.dispose(); + } + + void _showError(String message) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Something went wrong'), + content: SingleChildScrollView( + child: SelectableText(message), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('OK'), + ), + ], + ); + }, + ); + } + + Future _generateAndPlay() async { + setState(() { + _loading = true; + _responseText = null; + }); + + try { + final GenerationConfig config; + final String prompt; + + if (_isMultiSpeaker) { + prompt = + '${_speaker1NameController.text}: ${_speaker1LineController.text}\n' + '${_speaker2NameController.text}: ${_speaker2LineController.text}'; + config = GenerationConfig( + responseModalities: [ResponseModalities.audio], + speechConfig: SpeechConfig.multiSpeaker( + multiSpeakerVoiceConfig: MultiSpeakerVoiceConfig( + speakerVoiceConfigs: [ + SpeakerVoiceConfig( + speaker: _speaker1NameController.text, + voiceName: _speaker1Voice, + ), + SpeakerVoiceConfig( + speaker: _speaker2NameController.text, + voiceName: _speaker2Voice, + ), + ], + ), + ), + ); + } else { + prompt = _singlePromptController.text; + config = GenerationConfig( + responseModalities: [ResponseModalities.audio], + speechConfig: SpeechConfig( + voiceName: _selectedVoice, + languageCode: 'en-US', + ), + ); + } + + // Use the preview model for TTS + const modelName = 'gemini-3.1-flash-tts-preview'; + final GenerativeModel model; + if (widget.useVertexBackend) { + model = FirebaseAI.vertexAI().generativeModel( + model: modelName, + generationConfig: config, + ); + } else { + model = FirebaseAI.googleAI().generativeModel( + model: modelName, + generationConfig: config, + ); + } + + final response = await model.generateContent([Content.text(prompt)]); + + // Extract text response + _responseText = response.text; + + // Find audio bytes + Uint8List? audioBytes; + for (final candidate in response.candidates) { + for (final part in candidate.content.parts) { + if (part is InlineDataPart && part.mimeType.startsWith('audio/')) { + audioBytes = part.bytes; + break; + } + } + if (audioBytes != null) break; + } + + if (audioBytes == null || audioBytes.isEmpty) { + throw Exception('No audio received from the model.'); + } + + // Play audio and start visualizer + await _audioOutput.playStream(); + _audioOutput.addDataToAudioStream(audioBytes); + _audioOutput.finishStream(); + + // Calculate duration: 24000 Hz, 1 channel, 16-bit (2 bytes) = 48000 bytes/sec + final durationMs = (audioBytes.length / 48.0).round(); + final duration = Duration(milliseconds: durationMs); + + setState(() { + _loading = false; + _isPlaying = true; + _amplitudeStream = _mockAmpGen.start(duration); + }); + + _playbackTimer = Timer(duration, () { + setState(() { + _isPlaying = false; + _amplitudeStream = null; + }); + }); + } catch (e) { + setState(() { + _loading = false; + }); + _showError(e.toString()); + } + } + + void _stopPlayback() { + _audioOutput.stopStream(); + _mockAmpGen.stop(); + _playbackTimer?.cancel(); + setState(() { + _isPlaying = false; + _amplitudeStream = null; + }); + } + + Widget _buildSingleSpeakerForm() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + controller: _singlePromptController, + decoration: const InputDecoration( + labelText: 'Prompt', + hintText: 'Enter text to generate speech from', + border: OutlineInputBorder(), + ), + maxLines: 3, + ), + const SizedBox(height: 16), + DropdownButtonFormField( + initialValue: _selectedVoice, + decoration: const InputDecoration( + labelText: 'Voice Name', + border: OutlineInputBorder(), + ), + items: _availableVoices.map((voice) { + return DropdownMenuItem( + value: voice, + child: Text(voice), + ); + }).toList(), + onChanged: (value) { + if (value != null) { + setState(() { + _selectedVoice = value; + }); + } + }, + ), + ], + ); + } + + Widget _buildSpeakerCard({ + required String title, + required TextEditingController nameController, + required String selectedVoice, + required ValueChanged onVoiceChanged, + required TextEditingController lineController, + required Color accentColor, + }) { + return Card( + elevation: 2, + margin: const EdgeInsets.symmetric(vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: BorderSide( + color: accentColor.withAlpha(80), + width: 1.5, + ), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(Icons.person, color: accentColor), + const SizedBox(width: 8), + Text( + title, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: accentColor, + ), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + flex: 2, + child: TextField( + controller: nameController, + decoration: const InputDecoration( + labelText: 'Speaker Name', + prefixIcon: Icon(Icons.badge_outlined), + border: OutlineInputBorder(), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + flex: 3, + child: DropdownButtonFormField( + initialValue: selectedVoice, + decoration: const InputDecoration( + labelText: 'Voice Name', + prefixIcon: Icon(Icons.settings_voice_outlined), + border: OutlineInputBorder(), + ), + items: _availableVoices.map((voice) { + return DropdownMenuItem( + value: voice, + child: Text(voice), + ); + }).toList(), + onChanged: onVoiceChanged, + ), + ), + ], + ), + const SizedBox(height: 12), + TextField( + controller: lineController, + decoration: const InputDecoration( + labelText: 'Speech Line', + prefixIcon: Icon(Icons.chat_bubble_outline), + hintText: 'Enter what this speaker will say', + border: OutlineInputBorder(), + ), + maxLines: 2, + ), + ], + ), + ), + ); + } + + Widget _buildMultiSpeakerForm() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSpeakerCard( + title: 'Speaker 1', + nameController: _speaker1NameController, + selectedVoice: _speaker1Voice, + onVoiceChanged: (value) { + if (value != null) { + setState(() { + _speaker1Voice = value; + }); + } + }, + lineController: _speaker1LineController, + accentColor: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 8), + _buildSpeakerCard( + title: 'Speaker 2', + nameController: _speaker2NameController, + selectedVoice: _speaker2Voice, + onVoiceChanged: (value) { + if (value != null) { + setState(() { + _speaker2Voice = value; + }); + } + }, + lineController: _speaker2LineController, + accentColor: Theme.of(context).colorScheme.secondary, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + SegmentedButton( + segments: const [ + ButtonSegment(value: false, label: Text('Single Speaker')), + ButtonSegment(value: true, label: Text('Multi Speaker')), + ], + selected: {_isMultiSpeaker}, + onSelectionChanged: (value) { + setState(() { + _isMultiSpeaker = value.first; + }); + }, + ), + const SizedBox(height: 16), + Expanded( + child: SingleChildScrollView( + child: _isMultiSpeaker + ? _buildMultiSpeakerForm() + : _buildSingleSpeakerForm(), + ), + ), + const Divider(), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), + child: Row( + children: [ + if (_loading) + const CircularProgressIndicator() + else + IconButton( + icon: Icon(_isPlaying ? Icons.stop : Icons.play_arrow), + iconSize: 36, + color: Theme.of(context).colorScheme.primary, + onPressed: _isPlaying ? _stopPlayback : _generateAndPlay, + ), + const SizedBox(width: 16), + AudioVisualizer( + audioStreamIsActive: _isPlaying, + amplitudeStream: _amplitudeStream, + ), + if (!_isPlaying && !_loading && _responseText != null) + Expanded( + child: Text( + _responseText!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ], + ), + ), + ], + ), + ); + } +} + +class MockAmplitudeGenerator { + StreamController? _controller; + Timer? _timer; + final Random _random = Random(); + + Stream start(Duration duration) { + stop(); + _controller = StreamController.broadcast(); + + _timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { + double current = -60.0 + _random.nextDouble() * 60.0; + _controller?.add(Amplitude(current: current, max: 0)); + }); + + return _controller!.stream; + } + + void stop() { + _timer?.cancel(); + _timer = null; + _controller?.close(); + _controller = null; + } +} diff --git a/packages/firebase_ai/firebase_ai/example/lib/pages/video_page.dart b/packages/firebase_ai/firebase_ai/example/lib/pages/video_page.dart deleted file mode 100644 index 565555e19cd6..000000000000 --- a/packages/firebase_ai/firebase_ai/example/lib/pages/video_page.dart +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:flutter/material.dart'; -import 'package:firebase_ai/firebase_ai.dart'; -import 'package:flutter/services.dart'; -import '../widgets/message_widget.dart'; - -class VideoPage extends StatefulWidget { - const VideoPage({super.key, required this.title, required this.model}); - - final String title; - final GenerativeModel model; - - @override - State createState() => _VideoPageState(); -} - -class _VideoPageState extends State { - ChatSession? chat; - late final GenerativeModel model; - final List _messages = []; - bool _loading = false; - - @override - void initState() { - super.initState(); - chat = widget.model.startChat(); - } - - Future _testVideo(model) async { - try { - ByteData videoBytes = - await rootBundle.load('assets/videos/landscape.mp4'); - - const _prompt = 'Can you tell me what is in the video?'; - - setState(() { - _messages.add(MessageData(text: _prompt, fromUser: true)); - }); - - final videoPart = - InlineDataPart('video/mp4', videoBytes.buffer.asUint8List()); - - final response = await widget.model.generateContent([ - Content.multi([const TextPart(_prompt), videoPart]), - ]); - - setState(() { - _messages.add(MessageData(text: response.text, fromUser: false)); - }); - } catch (e) { - print('Error sending video to model: $e'); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: ListView.builder( - itemBuilder: (context, idx) { - return MessageWidget( - text: _messages[idx].text, - isFromUser: _messages[idx].fromUser ?? false, - ); - }, - itemCount: _messages.length, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), - child: Center( - child: SizedBox( - child: ElevatedButton( - onPressed: !_loading - ? () async { - await _testVideo(widget.model); - } - : null, - child: const Text('Test Video Prompt'), - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/packages/firebase_ai/firebase_ai/example/lib/utils/audio_input.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/audio_input.dart index 2698537b048b..9a768d64be9e 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/utils/audio_input.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/utils/audio_input.dart @@ -110,8 +110,6 @@ class AudioInput extends ChangeNotifier { sampleRate: 24000, device: selectedDevice, numChannels: 1, - echoCancel: true, - noiseSuppress: true, androidConfig: const AndroidRecordConfig( audioSource: AndroidAudioSource.voiceCommunication, ), diff --git a/packages/firebase_ai/firebase_ai/example/lib/utils/audio_output.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/audio_output.dart index 4e0623eeef97..d1cfc988b453 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/utils/audio_output.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/utils/audio_output.dart @@ -69,7 +69,7 @@ class AudioOutput { return null; } // Play audio stream - handle = await SoLoud.instance.play(myStream); + handle = SoLoud.instance.play(myStream); return stream = myStream; } @@ -94,4 +94,11 @@ class AudioOutput { SoLoud.instance.setDataIsEnded(currentStream); await SoLoud.instance.stop(currentHandle); } + + void finishStream() { + var currentStream = stream; + if (currentStream != null) { + SoLoud.instance.setDataIsEnded(currentStream); + } + } } diff --git a/packages/firebase_ai/firebase_ai/example/lib/utils/function_call_utils.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/function_call_utils.dart new file mode 100644 index 000000000000..fcbd01151063 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/example/lib/utils/function_call_utils.dart @@ -0,0 +1,47 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +class Location { + final String city; + final String state; + + Location(this.city, this.state); +} + +// This is a hypothetical API to return a fake weather data collection for +// certain location +Future> fetchWeather( + Location location, + String date, +) async { + // TODO(developer): Call a real weather API. + // Mock response from the API. In developer live code this would call the + // external API and return what that API returns. + final apiResponse = { + 'temperature': 38, + 'chancePrecipitation': '56%', + 'cloudConditions': 'partly-cloudy', + }; + return apiResponse; +} + +Future> fetchWeatherCallable( + Map args, +) async { + final locationData = args['location']! as Map; + final city = locationData['city']! as String; + final state = locationData['state']! as String; + final date = args['date']! as String; + return fetchWeather(Location(city, state), date); +} diff --git a/packages/firebase_ai/firebase_ai/example/lib/utils/image_utils.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/image_utils.dart deleted file mode 100644 index f297c9b25e66..000000000000 --- a/packages/firebase_ai/firebase_ai/example/lib/utils/image_utils.dart +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -import 'package:firebase_ai/firebase_ai.dart'; -import 'package:image/image.dart' as img; -import 'package:flutter/foundation.dart'; - -/// The parameters passed to the isolate -class _IsolateParams { - final Uint8List imageBytes; - // ignore: experimental_member_use - final ImagenDimensions newDimensions; - // ignore: experimental_member_use - final ImagenImagePlacement newPosition; - - _IsolateParams({ - required Uint8List imageBytes, - required this.newDimensions, - required this.newPosition, - }) : imageBytes = Uint8List.fromList(imageBytes); -} - -/// The results returned from the isolate -class _IsolateResult { - final Uint8List paddedImageBytes; - final Uint8List maskBytes; - - _IsolateResult({ - required this.paddedImageBytes, - required this.maskBytes, - }); -} - -/// Processes the image request. -/// -/// This is the top-level function that will run in the background isolate. -/// It uses the 'image' package for all manipulations. -Future<_IsolateResult> _generateMaskAndPadInIsolate( - _IsolateParams params, -) async { - // 1. Decode the original image - final originalImage = img.decodeImage(params.imageBytes); - if (originalImage == null) { - throw StateError('Failed to decode image in isolate.'); - } - // Validate dimensions - if (originalImage.width >= params.newDimensions.width || - originalImage.height >= params.newDimensions.height) { - throw ArgumentError( - 'New Dimensions must be strictly larger than original image dimensions.', - ); - } - // 2. Calculate the position - // ignore: experimental_member_use - final originalDimensions = ImagenDimensions( - width: originalImage.width, - height: originalImage.height, - ); - final normalizedPosition = params.newPosition.normalizeToDimensions( - originalDimensions, - params.newDimensions, - ); - final x = normalizedPosition.x ?? 0; - final y = normalizedPosition.y ?? 0; - // 3. Create the mask image - final mask = img.Image( - width: params.newDimensions.width, - height: params.newDimensions.height, - ); - // Fill with white and draw a black rectangle for the original image area - img.fill(mask, color: img.ColorRgb8(255, 255, 255)); - img.fillRect( - mask, - x1: x, - y1: y, - x2: x + originalImage.width, - y2: y + originalImage.height, - color: img.ColorRgb8(0, 0, 0), - ); - // 4. Create the padded image - final paddedImage = img.Image( - width: params.newDimensions.width, - height: params.newDimensions.height, - ); - // Fill with black and draw the original image on top - img.fill(paddedImage, color: img.ColorRgb8(0, 0, 0)); - img.compositeImage( - paddedImage, - originalImage, - dstX: x, - dstY: y, - ); - // 5. Encode both images to PNG format (which is lossless) - final maskBytes = img.encodePng(mask); - final paddedBytes = img.encodePng(paddedImage); - return _IsolateResult( - paddedImageBytes: Uint8List.fromList(paddedBytes), - maskBytes: Uint8List.fromList(maskBytes), - ); -} - -/// Generates a mask and pads the image for outpainting. -// ignore: experimental_member_use -Future> generateMaskAndPadForOutpainting({ - required ImagenInlineImage image, - // ignore: experimental_member_use - required ImagenDimensions newDimensions, - // ignore: experimental_member_use - ImagenImagePlacement newPosition = ImagenImagePlacement.center, -}) async { - // Prepare the parameters for the isolate - // Note: We are assuming `image` has a way to get its raw bytes, - // which seems to be the case from `bytesBase64Encoded` in your example. - // If not, you'd need to convert the `ui.Image` to bytes here first. - final params = _IsolateParams( - imageBytes: image.bytesBase64Encoded, // Assuming this is Uint8List - newDimensions: newDimensions, - newPosition: newPosition, - ); - // Execute the image processing in a separate isolate and wait for the result - final result = await compute(_generateMaskAndPadInIsolate, params); - - // Use the resulting bytes to create your final objects - return [ - // ignore: experimental_member_use - ImagenRawImage( - image: ImagenInlineImage( - bytesBase64Encoded: result.paddedImageBytes, - mimeType: 'image/png', // The isolate always returns PNG - ), - ), - // ignore: experimental_member_use - ImagenRawMask( - mask: ImagenInlineImage( - bytesBase64Encoded: result.maskBytes, - mimeType: 'image/png', // The isolate always returns PNG - ), - ), - ]; -} diff --git a/packages/firebase_ai/firebase_ai/example/lib/utils/video_input.dart b/packages/firebase_ai/firebase_ai/example/lib/utils/video_input.dart index 97dc8108343b..a04f961b6f9e 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/utils/video_input.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/utils/video_input.dart @@ -15,7 +15,6 @@ import 'dart:developer'; import 'dart:async'; import 'package:camera/camera.dart'; -import 'package:camera_macos/camera_macos.dart'; import 'package:flutter/foundation.dart'; class VideoInput extends ChangeNotifier { @@ -34,8 +33,7 @@ class VideoInput extends ChangeNotifier { Future init() async { try { if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - //await camera_macos_lib.loadLibrary(); - _cameras = await CameraMacOS.instance.listDevices(); + _cameras = []; } else { _cameras = await availableCameras(); } @@ -53,7 +51,7 @@ class VideoInput extends ChangeNotifier { stopStreamingImages(); if (controllerInitialized && _cameraController != null) { if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - (_cameraController as CameraMacOSController).destroy(); + // No-op on macOS } else { (_cameraController as CameraController).dispose(); } @@ -63,7 +61,7 @@ class VideoInput extends ChangeNotifier { String? get selectedCameraId { if (_selectedCamera == null) return null; if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - return _selectedCamera.deviceId; + return null; } return null; } @@ -77,7 +75,7 @@ class VideoInput extends ChangeNotifier { Future initializeCameraController() async { if (controllerInitialized && _cameraController != null) { if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - await (_cameraController as CameraMacOSController).destroy(); + // No-op on macOS } else { await (_cameraController as CameraController).dispose(); } @@ -142,13 +140,7 @@ class VideoInput extends ChangeNotifier { try { if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - final controller = _cameraController as CameraMacOSController; - final CameraMacOSFile? image = await controller.takePicture(); - if (image != null && image.bytes != null) { - if (!_imageStreamController.isClosed) { - _imageStreamController.add(image.bytes!); - } - } + // No-op on macOS } else { final controller = _cameraController as CameraController; if (controller.value.isTakingPicture) return; @@ -197,12 +189,7 @@ class VideoInput extends ChangeNotifier { Future flipCamera() async { if (_cameras.length > 1) { if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) { - final currentSelected = _selectedCamera; - final otherCamera = _cameras.firstWhere( - (camera) => camera.deviceId != currentSelected.deviceId, - orElse: () => _cameras[0], - ); - _selectedCamera = otherCamera; + // No-op on macOS } else { final currentSelected = _selectedCamera as CameraDescription; final otherCamera = _cameras.firstWhere( diff --git a/packages/firebase_ai/firebase_ai/example/lib/widgets/camera_previews.dart b/packages/firebase_ai/firebase_ai/example/lib/widgets/camera_previews.dart index 5bf338f7e345..5aa42d1c8c81 100644 --- a/packages/firebase_ai/firebase_ai/example/lib/widgets/camera_previews.dart +++ b/packages/firebase_ai/firebase_ai/example/lib/widgets/camera_previews.dart @@ -13,7 +13,6 @@ // limitations under the License. import 'package:camera/camera.dart'; -import 'package:camera_macos/camera_macos.dart' deferred as camera_macos_lib; import 'package:flutter/foundation.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter/material.dart'; @@ -58,24 +57,7 @@ class SquareCameraPreview extends StatelessWidget { scale: aspectRatio / 1, child: Center( child: !kIsWeb && defaultTargetPlatform == TargetPlatform.macOS - ? FutureBuilder( - future: camera_macos_lib.loadLibrary(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - return camera_macos_lib.CameraMacOSView( - deviceId: deviceId, - cameraMode: - camera_macos_lib.CameraMacOSMode.photo, - enableAudio: false, - onCameraInizialized: (dynamic controller) { - onInitialized?.call(controller); - }, - ); - } - return const SizedBox.shrink(); - }, - ) + ? const SizedBox.shrink() : CameraPreview(controller as CameraController), ), ), @@ -128,22 +110,7 @@ class _FullCameraPreviewState extends State child: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: !kIsWeb && defaultTargetPlatform == TargetPlatform.macOS - ? FutureBuilder( - future: camera_macos_lib.loadLibrary(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return camera_macos_lib.CameraMacOSView( - deviceId: widget.deviceId, - cameraMode: camera_macos_lib.CameraMacOSMode.photo, - enableAudio: false, - onCameraInizialized: (dynamic controller) { - widget.onInitialized?.call(controller); - }, - ); - } - return const SizedBox.shrink(); - }, - ) + ? const SizedBox.shrink() : CameraPreview(widget.controller as CameraController), ), ).animate(controller: _animController).scaleXY().fadeIn(); diff --git a/packages/firebase_ai/firebase_ai/example/macos/Podfile b/packages/firebase_ai/firebase_ai/example/macos/Podfile index b52666a10389..ff5ddb3b8bdc 100644 --- a/packages/firebase_ai/firebase_ai/example/macos/Podfile +++ b/packages/firebase_ai/firebase_ai/example/macos/Podfile @@ -28,7 +28,6 @@ flutter_macos_podfile_setup target 'Runner' do use_frameworks! - use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do diff --git a/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj index 4bc66a519ca5..e2ab2e60ccd7 100644 --- a/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 1E1464098F5197FB1E35FDA1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E05DB31CC6D204C7C78D127 /* Pods_RunnerTests.framework */; }; 20C13FC2C906153EF4A40292 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 08B0491E23641E5BA5DD096C /* GoogleService-Info.plist */; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; @@ -29,7 +28,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 */; }; - 3D1CF19370CB8E26E5C667A5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4DAA18FE8B79A454BF3F8CB /* Pods_Runner.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -64,7 +63,6 @@ /* Begin PBXFileReference section */ 08B0491E23641E5BA5DD096C /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; - 0E05DB31CC6D204C7C78D127 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; @@ -81,15 +79,11 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 3A40C9AE19ACEC6C433878E9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - 587C61AFC0E2B0BF5340F8E8 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 5C2B5E4F1CE100E1FA5D9DC5 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - 766A2E414AFDFA56243527A6 /* 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 = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* firebase_ai */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = firebase_ai; path = ../../macos/firebase_ai; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 816B0EE72BF94FC5261D04E6 /* 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 = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - A2911B8EF91B3925874FDE6A /* 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 = ""; }; - C4DAA18FE8B79A454BF3F8CB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -97,7 +91,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1E1464098F5197FB1E35FDA1 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -105,7 +98,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3D1CF19370CB8E26E5C667A5 /* Pods_Runner.framework in Frameworks */, + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -138,7 +131,6 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, 08B0491E23641E5BA5DD096C /* GoogleService-Info.plist */, BE277C424FC00920BE07E371 /* Pods */, ); @@ -167,6 +159,9 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78DABEA22ED26510000E7860 /* firebase_ai */, + 784666492D4C4C64000A1A5F /* FlutterFramework */, + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, @@ -191,24 +186,8 @@ BE277C424FC00920BE07E371 /* Pods */ = { isa = PBXGroup; children = ( - A2911B8EF91B3925874FDE6A /* Pods-Runner.debug.xcconfig */, - 816B0EE72BF94FC5261D04E6 /* Pods-Runner.release.xcconfig */, - 766A2E414AFDFA56243527A6 /* Pods-Runner.profile.xcconfig */, - 3A40C9AE19ACEC6C433878E9 /* Pods-RunnerTests.debug.xcconfig */, - 587C61AFC0E2B0BF5340F8E8 /* Pods-RunnerTests.release.xcconfig */, - 5C2B5E4F1CE100E1FA5D9DC5 /* Pods-RunnerTests.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - C4DAA18FE8B79A454BF3F8CB /* Pods_Runner.framework */, - 0E05DB31CC6D204C7C78D127 /* Pods_RunnerTests.framework */, ); - name = Frameworks; + path = Pods; sourceTree = ""; }; /* End PBXGroup section */ @@ -218,7 +197,6 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 33B83C0D35C3606AED8215FE /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -237,13 +215,11 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - E10F886575A4AF9F1D3D5C5B /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 1D3525FBE401B81EB0265948 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -251,6 +227,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* example.app */; productType = "com.apple.product-type.application"; @@ -295,6 +274,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -327,23 +309,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1D3525FBE401B81EB0265948 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -362,28 +327,6 @@ shellPath = /bin/sh; shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; }; - 33B83C0D35C3606AED8215FE /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 33CC111E2044C6BF0003C045 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -404,28 +347,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - E10F886575A4AF9F1D3D5C5B /* [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 */ @@ -477,7 +398,6 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3A40C9AE19ACEC6C433878E9 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -492,7 +412,6 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 587C61AFC0E2B0BF5340F8E8 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -507,7 +426,6 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5C2B5E4F1CE100E1FA5D9DC5 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -648,6 +566,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; @@ -800,6 +719,21 @@ 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; + package = 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b0a82f087ad1..49eac59ad872 100644 --- a/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/firebase_ai/firebase_ai/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + =3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -19,25 +21,24 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - camera: ^0.11.2+1 - camera_macos: ^0.0.9 + camera: ^0.12.0+1 cupertino_icons: ^1.0.6 - firebase_ai: ^3.10.0 - firebase_core: ^4.6.0 - firebase_storage: ^13.2.0 + firebase_ai: ^3.13.1 + firebase_core: ^4.11.0 + firebase_storage: ^13.4.3 flutter: sdk: flutter flutter_animate: ^4.5.2 - flutter_markdown: ^0.6.20 - flutter_soloud: ^3.1.6 + flutter_markdown: ^0.7.7+1 + flutter_soloud: ^4.0.4 image: ^4.5.4 image_picker: ^1.1.2 path_provider: ^2.1.5 - record: ^5.2.1 + record: ^6.2.0 waveform_flutter: ^1.2.0 dev_dependencies: - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 flutter_test: sdk: flutter diff --git a/packages/firebase_ai/firebase_ai/ios/firebase_ai/Package.swift b/packages/firebase_ai/firebase_ai/ios/firebase_ai/Package.swift new file mode 100644 index 000000000000..a9bd49d4e4d1 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/ios/firebase_ai/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import PackageDescription + +let package = Package( + name: "firebase_ai", + platforms: [ + .iOS("15.0") + ], + products: [ + .library(name: "firebase-ai", targets: ["firebase_ai"]) + ], + dependencies: [], + targets: [ + .target( + name: "firebase_ai", + dependencies: [], + resources: [ + .process("Resources") + ] + ) + ] +) diff --git a/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart b/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart index 6f90f6a6d977..06eeaaa1195f 100644 --- a/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart +++ b/packages/firebase_ai/firebase_ai/lib/firebase_ai.dart @@ -22,8 +22,11 @@ export 'src/api.dart' FinishReason, GenerateContentResponse, GenerationConfig, + GoogleMapsGroundingChunk, + GroundingChunk, ThinkingConfig, ThinkingLevel, + MediaResolution, HarmBlockThreshold, HarmCategory, HarmProbability, @@ -32,7 +35,8 @@ export 'src/api.dart' ResponseModalities, SafetyRating, SafetySetting, - UsageMetadata; + UsageMetadata, + WebGroundingChunk; export 'src/base_model.dart' show GenerativeModel, @@ -63,6 +67,7 @@ export 'src/error.dart' QuotaExceeded, UnsupportedUserLocation; export 'src/firebase_ai.dart' show FirebaseAI; +export 'src/image_config.dart' show ImageConfig, ImageAspectRatio, ImageSize; export 'src/imagen/imagen_api.dart' show ImagenSafetySettings, @@ -99,19 +104,31 @@ export 'src/imagen/imagen_reference.dart' ImagenControlReference; export 'src/live_api.dart' show - LiveGenerationConfig, - SpeechConfig, AudioTranscriptionConfig, + ContextWindowCompressionConfig, + GoingAwayNotice, + LiveGenerationConfig, LiveServerMessage, LiveServerContent, LiveServerToolCall, LiveServerToolCallCancellation, LiveServerResponse, - GoingAwayNotice, + SessionResumptionConfig, + SessionResumptionUpdate, + SlidingWindow, Transcription; export 'src/live_session.dart' show LiveSession; export 'src/schema.dart' show JSONSchema, Schema, SchemaType; - +export 'src/server_template/template_chat.dart' + show TemplateChatSession, StartTemplateChatExtension; +export 'src/server_template/template_tool.dart' + show + TemplateAutoFunctionDeclaration, + TemplateFunctionDeclaration, + TemplateTool, + TemplateToolConfig; +export 'src/speech_config.dart' + show SpeechConfig, MultiSpeakerVoiceConfig, SpeakerVoiceConfig; export 'src/tool.dart' show AutoFunctionDeclaration, @@ -121,5 +138,8 @@ export 'src/tool.dart' Tool, ToolConfig, GoogleSearch, + GoogleMaps, CodeExecution, - UrlContext; + UrlContext, + LatLng, + RetrievalConfig; diff --git a/packages/firebase_ai/firebase_ai/lib/src/api.dart b/packages/firebase_ai/firebase_ai/lib/src/api.dart index 653ef2097013..d2c2c7c9b3e9 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/api.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/api.dart @@ -14,7 +14,9 @@ import 'content.dart'; import 'error.dart'; +import 'image_config.dart'; import 'schema.dart'; +import 'speech_config.dart'; import 'tool.dart' show Tool, ToolConfig; /// Response for Count Tokens @@ -347,6 +349,23 @@ final class WebGroundingChunk { final String? domain; } +/// A grounding chunk sourced from Google Maps. +final class GoogleMapsGroundingChunk { + // ignore: public_member_api_docs + GoogleMapsGroundingChunk({this.uri, this.title, this.placeId}); + + /// The URI of the place. + final String? uri; + + /// The title of the place. + final String? title; + + /// This Place's resource name, in `places/{place_id}` format. + /// + /// This can be used to look up the place using the Google Maps API. + final String? placeId; +} + /// Represents a chunk of retrieved data that supports a claim in the model's /// response. /// @@ -354,10 +373,13 @@ final class WebGroundingChunk { /// enabled. final class GroundingChunk { // ignore: public_member_api_docs - GroundingChunk({this.web}); + GroundingChunk({this.web, this.maps}); /// Contains details if the grounding chunk is from a web source. final WebGroundingChunk? web; + + /// Contains details if the grounding chunk is from a Google Maps source. + final GoogleMapsGroundingChunk? maps; } /// Provides information about how a specific segment of the model's response @@ -573,7 +595,7 @@ enum BlockReason { 'BLOCK_REASON_UNSPECIFIED' => BlockReason.unknown, 'SAFETY' => BlockReason.safety, 'OTHER' => BlockReason.other, - _ => throw FormatException('Unhandled BlockReason format', jsonObject), + _ => BlockReason.unknown, }; } @@ -606,7 +628,19 @@ enum HarmCategory { sexuallyExplicit('HARM_CATEGORY_SEXUALLY_EXPLICIT'), /// Promotes or enables access to harmful goods, services, and activities. - dangerousContent('HARM_CATEGORY_DANGEROUS_CONTENT'); + dangerousContent('HARM_CATEGORY_DANGEROUS_CONTENT'), + + /// Image content containing hate speech. + imageHate('HARM_CATEGORY_IMAGE_HATE'), + + /// Image content that is dangerous. + imageDangerousContent('HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT'), + + /// Image content containing harassment. + imageHarassment('HARM_CATEGORY_IMAGE_HARASSMENT'), + + /// Image content that is sexually explicit. + imageSexuallyExplicit('HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT'); const HarmCategory(this._jsonString); @@ -618,7 +652,13 @@ enum HarmCategory { 'HARM_CATEGORY_HATE_SPEECH' => HarmCategory.hateSpeech, 'HARM_CATEGORY_SEXUALLY_EXPLICIT' => HarmCategory.sexuallyExplicit, 'HARM_CATEGORY_DANGEROUS_CONTENT' => HarmCategory.dangerousContent, - _ => throw FormatException('Unhandled HarmCategory format', jsonObject), + 'HARM_CATEGORY_IMAGE_HATE' => HarmCategory.imageHate, + 'HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT' => + HarmCategory.imageDangerousContent, + 'HARM_CATEGORY_IMAGE_HARASSMENT' => HarmCategory.imageHarassment, + 'HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT' => + HarmCategory.imageSexuallyExplicit, + _ => HarmCategory.unknown, }; } @@ -661,8 +701,7 @@ enum HarmProbability { 'LOW' => HarmProbability.low, 'MEDIUM' => HarmProbability.medium, 'HIGH' => HarmProbability.high, - _ => - throw FormatException('Unhandled HarmProbability format', jsonObject), + _ => HarmProbability.unknown, }; } @@ -704,7 +743,7 @@ enum HarmSeverity { 'HARM_SEVERITY_LOW' => HarmSeverity.low, 'HARM_SEVERITY_MEDIUM' => HarmSeverity.medium, 'HARM_SEVERITY_HIGH' => HarmSeverity.high, - _ => throw FormatException('Unhandled HarmSeverity format', jsonObject), + _ => HarmSeverity.unknown, }; } @@ -770,6 +809,45 @@ enum FinishReason { /// The candidate content was flagged for malformed function call reasons. malformedFunctionCall('MALFORMED_FUNCTION_CALL'), + /// Token generation was stopped because the response contained forbidden terms. + blocklist('BLOCKLIST'), + + /// Token generation was stopped because the response contained potentially prohibited content. + prohibitedContent('PROHIBITED_CONTENT'), + + /// Token generation was stopped because of Sensitive Personally Identifiable Information (SPII). + spii('SPII'), + + /// Token generation stopped because generated images contain safety violations. + imageSafety('IMAGE_SAFETY'), + + /// Image generation stopped because generated images have other prohibited content. + imageProhibitedContent('IMAGE_PROHIBITED_CONTENT'), + + /// Image generation stopped because of other miscellaneous issues. + imageOther('IMAGE_OTHER'), + + /// The model was expected to generate an image, but none was generated. + noImage('NO_IMAGE'), + + /// Image generation stopped due to recitation. + imageRecitation('IMAGE_RECITATION'), + + /// The response candidate content was flagged for using an unsupported language. + language('LANGUAGE'), + + /// Model generated a tool call but no tools were enabled in the request. + unexpectedToolCall('UNEXPECTED_TOOL_CALL'), + + /// Model called too many tools consecutively, thus the system exited execution. + tooManyToolCalls('TOO_MANY_TOOL_CALLS'), + + /// Request has at least one thought signature missing. + missingThoughtSignature('MISSING_THOUGHT_SIGNATURE'), + + /// Finished due to malformed response. + malformedResponse('MALFORMED_RESPONSE'), + /// Unknown reason. other('OTHER'); @@ -790,7 +868,21 @@ enum FinishReason { 'RECITATION' => FinishReason.recitation, 'OTHER' => FinishReason.other, 'MALFORMED_FUNCTION_CALL' => FinishReason.malformedFunctionCall, - _ => throw FormatException('Unhandled FinishReason format', jsonObject), + 'BLOCKLIST' => FinishReason.blocklist, + 'PROHIBITED_CONTENT' => FinishReason.prohibitedContent, + 'SPII' => FinishReason.spii, + 'IMAGE_SAFETY' => FinishReason.imageSafety, + 'IMAGE_PROHIBITED_CONTENT' => FinishReason.imageProhibitedContent, + 'IMAGE_OTHER' => FinishReason.imageOther, + 'NO_IMAGE' => FinishReason.noImage, + 'IMAGE_RECITATION' => FinishReason.imageRecitation, + 'LANGUAGE' => FinishReason.language, + 'UNEXPECTED_TOOL_CALL' => FinishReason.unexpectedToolCall, + 'TOO_MANY_TOOL_CALLS' => FinishReason.tooManyToolCalls, + 'MISSING_THOUGHT_SIGNATURE' => FinishReason.missingThoughtSignature, + 'MALFORMED_RESPONSE' => FinishReason.malformedResponse, + 'UNKNOWN' => FinishReason.unknown, + _ => FinishReason.unknown, }; } @@ -840,8 +932,7 @@ enum ContentModality { 'VIDEO' => ContentModality.video, 'AUDIO' => ContentModality.audio, 'DOCUMENT' => ContentModality.document, - _ => - throw FormatException('Unhandled ContentModality format', jsonObject), + _ => ContentModality.unspecified, }; } @@ -946,8 +1037,7 @@ enum HarmBlockMethod { 'SEVERITY' => HarmBlockMethod.severity, 'PROBABILITY' => HarmBlockMethod.probability, 'HARM_BLOCK_METHOD_UNSPECIFIED' => HarmBlockMethod.unspecified, - _ => - throw FormatException('Unhandled HarmBlockMethod format', jsonObject), + _ => HarmBlockMethod.unspecified, }; } @@ -978,6 +1068,41 @@ enum ResponseModalities { String toJson() => _jsonString; } +/// The media resolution to use for media inputs. +enum MediaResolution { + /// Default media resolution selected by the model. + unspecified('MEDIA_RESOLUTION_UNSPECIFIED'), + + /// Lower token count, resulting in faster processing and lower cost. + low('MEDIA_RESOLUTION_LOW'), + + /// A balance between detail, cost, and latency. + medium('MEDIA_RESOLUTION_MEDIUM'), + + /// Higher token count, providing more detail for media inputs. + high('MEDIA_RESOLUTION_HIGH'), + + /// Highest token count for image inputs. + /// + /// This value is only supported on individual media parts. + ultraHigh('MEDIA_RESOLUTION_ULTRA_HIGH'); + + const MediaResolution(this._jsonString); + final String _jsonString; + + /// Parse a media resolution from a JSON value. + static MediaResolution parseValue(String value) => switch (value) { + 'MEDIA_RESOLUTION_LOW' => MediaResolution.low, + 'MEDIA_RESOLUTION_MEDIUM' => MediaResolution.medium, + 'MEDIA_RESOLUTION_HIGH' => MediaResolution.high, + 'MEDIA_RESOLUTION_ULTRA_HIGH' => MediaResolution.ultraHigh, + _ => MediaResolution.unspecified, + }; + + // ignore: public_member_api_docs + String toJson() => _jsonString; +} + /// A preset that balances the trade-off between reasoning quality and response /// speed for a model's "thinking" process. /// @@ -1081,7 +1206,10 @@ abstract class BaseGenerationConfig { this.presencePenalty, this.frequencyPenalty, this.responseModalities, - }); + this.mediaResolution, + this.speechConfig, + }) : assert(mediaResolution != MediaResolution.ultraHigh, + 'MediaResolution.ultraHigh is only supported on individual media parts.'); /// Number of generated responses to return. /// @@ -1160,6 +1288,17 @@ abstract class BaseGenerationConfig { /// The list of desired response modalities. final List? responseModalities; + /// The resolution to use for media inputs. + /// + /// Higher resolutions provide more detail to the model, at the cost of + /// increased token usage, latency, and cost. + /// + /// [MediaResolution.ultraHigh] is only supported on individual media parts. + final MediaResolution? mediaResolution; + + /// The configuration parameters controlling the model's speech and audio generation. + final SpeechConfig? speechConfig; + // ignore: public_member_api_docs Map toJson() => { if (candidateCount case final candidateCount?) @@ -1176,6 +1315,10 @@ abstract class BaseGenerationConfig { if (responseModalities case final responseModalities?) 'responseModalities': responseModalities.map((modality) => modality.toJson()).toList(), + if (mediaResolution case final mediaResolution?) + 'mediaResolution': mediaResolution.toJson(), + if (speechConfig case final speechConfig?) + 'speechConfig': speechConfig.toJson(), }; } @@ -1192,10 +1335,13 @@ final class GenerationConfig extends BaseGenerationConfig { super.presencePenalty, super.frequencyPenalty, super.responseModalities, + super.mediaResolution, + super.speechConfig, this.responseMimeType, this.responseSchema, this.responseJsonSchema, this.thinkingConfig, + this.imageConfig, }) : assert(responseSchema == null || responseJsonSchema == null, 'responseSchema and responseJsonSchema cannot both be set.'); @@ -1244,6 +1390,9 @@ final class GenerationConfig extends BaseGenerationConfig { /// support thinking. final ThinkingConfig? thinkingConfig; + /// Configuration options for generating images with Gemini models. + final ImageConfig? imageConfig; + @override Map toJson() => { ...super.toJson(), @@ -1258,6 +1407,8 @@ final class GenerationConfig extends BaseGenerationConfig { 'responseJsonSchema': responseJsonSchema, if (thinkingConfig case final thinkingConfig?) 'thinkingConfig': thinkingConfig.toJson(), + if (imageConfig case final imageConfig?) + 'imageConfig': imageConfig.toJson(), }; } @@ -1292,7 +1443,7 @@ enum TaskType { 'SEMANTIC_SIMILARITY' => TaskType.semanticSimilarity, 'CLASSIFICATION' => TaskType.classification, 'CLUSTERING' => TaskType.clustering, - _ => throw FormatException('Unhandled TaskType format', jsonObject), + _ => TaskType.unspecified, }; } @@ -1689,6 +1840,18 @@ WebGroundingChunk _parseWebGroundingChunk(Object? jsonObject) { ); } +GoogleMapsGroundingChunk _parseGoogleMapsGroundingChunk(Object? jsonObject) { + if (jsonObject is! Map) { + throw unhandledFormat('GoogleMapsGroundingChunk', jsonObject); + } + + return GoogleMapsGroundingChunk( + uri: jsonObject['uri'] as String?, + title: jsonObject['title'] as String?, + placeId: jsonObject['placeId'] as String?, + ); +} + GroundingChunk _parseGroundingChunk(Object? jsonObject) { if (jsonObject is! Map) { throw unhandledFormat('GroundingChunk', jsonObject); @@ -1698,6 +1861,9 @@ GroundingChunk _parseGroundingChunk(Object? jsonObject) { web: jsonObject['web'] != null ? _parseWebGroundingChunk(jsonObject['web']) : null, + maps: jsonObject['maps'] != null + ? _parseGoogleMapsGroundingChunk(jsonObject['maps']) + : null, ); } @@ -1818,7 +1984,7 @@ enum Outcome { 'OUTCOME_OK' => Outcome.ok, 'OUTCOME_FAILED' => Outcome.failed, 'OUTCOME_DEADLINE_EXCEEDED' => Outcome.deadlineExceeded, - _ => throw FormatException('Unhandled Outcome format', jsonObject), + _ => Outcome.unspecified, }; } } diff --git a/packages/firebase_ai/firebase_ai/lib/src/base_model.dart b/packages/firebase_ai/firebase_ai/lib/src/base_model.dart index cf1f98db8b1a..96972eb5dafb 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/base_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/base_model.dart @@ -18,11 +18,8 @@ import 'dart:convert'; import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; -import 'package:web_socket_channel/io.dart'; -import 'package:web_socket_channel/web_socket_channel.dart'; import 'api.dart'; import 'client.dart'; @@ -37,6 +34,7 @@ import 'imagen/imagen_reference.dart'; import 'live_api.dart'; import 'live_session.dart'; import 'platform_header_helper.dart'; +import 'server_template/template_tool.dart'; import 'tool.dart'; part 'generative_model.dart'; @@ -281,19 +279,23 @@ abstract class BaseModel { ) { return () async { Map headers = {}; + + final effectiveAppCheck = appCheck ?? app?.getService(); + final effectiveAuth = auth ?? app?.getService(); + // Override the client name in Google AI SDK headers['x-goog-api-client'] = 'gl-dart/$packageVersion fire/$packageVersion'; - if (appCheck != null) { + if (effectiveAppCheck != null) { final appCheckToken = useLimitedUseAppCheckTokens == true - ? await appCheck.getLimitedUseToken() - : await appCheck.getToken(); + ? await effectiveAppCheck.getLimitedUseToken() + : await effectiveAppCheck.getToken(); if (appCheckToken != null) { headers['X-Firebase-AppCheck'] = appCheckToken; } } - if (auth != null) { - final idToken = await auth.currentUser?.getIdToken(); + if (effectiveAuth != null) { + final idToken = await effectiveAuth.currentUser?.getIdToken(); if (idToken != null) { headers['Authorization'] = 'Firebase $idToken'; } @@ -369,14 +371,22 @@ abstract class BaseTemplateApiClientModel extends BaseApiClientModel { String templateId, Map? inputs, Iterable? history, + List? tools, + TemplateToolConfig? toolConfig, T Function(Map) parse) { Map body = {}; if (inputs != null) { - body['inputs'] = inputs; + body['inputs'] = _serializeTemplateInputs(inputs); } if (history != null) { body['history'] = history.map((c) => c.toJson()).toList(); } + if (tools != null) { + body['tools'] = tools.map((t) => t.toJson()).toList(); + } + if (toolConfig != null) { + body['toolConfig'] = toolConfig.toJson(); + } return _client .makeRequest(templateTaskUri(task, templateId), body) .then(parse); @@ -391,14 +401,22 @@ abstract class BaseTemplateApiClientModel extends BaseApiClientModel { String templateId, Map? inputs, Iterable? history, + List? tools, + TemplateToolConfig? toolConfig, T Function(Map) parse) { Map body = {}; if (inputs != null) { - body['inputs'] = inputs; + body['inputs'] = _serializeTemplateInputs(inputs); } if (history != null) { body['history'] = history.map((c) => c.toJson()).toList(); } + if (tools != null) { + body['tools'] = tools.map((t) => t.toJson()).toList(); + } + if (toolConfig != null) { + body['toolConfig'] = toolConfig.toJson(); + } final response = _client.streamRequest(templateTaskUri(task, templateId), body); return response.map(parse); @@ -411,4 +429,26 @@ abstract class BaseTemplateApiClientModel extends BaseApiClientModel { /// Returns the template name for the given [templateId]. String templateName(String templateId) => _templateUri.templateName(templateId); + + Map _serializeTemplateInputs(Map inputs) { + return inputs.map((key, value) { + return MapEntry(key, _serializeTemplateInputValue(value)); + }); + } + + Object? _serializeTemplateInputValue(Object? value) { + return switch (value) { + InlineDataPart(:final mimeType, :final bytes) => { + 'isInline': true, + 'mimeType': mimeType, + 'contents': base64Encode(bytes), + }, + Map() => value.map((key, nestedValue) { + return MapEntry(key, _serializeTemplateInputValue(nestedValue)); + }), + List() => + value.map(_serializeTemplateInputValue).toList(growable: false), + _ => value, + }; + } } diff --git a/packages/firebase_ai/firebase_ai/lib/src/content.dart b/packages/firebase_ai/firebase_ai/lib/src/content.dart index c9ed7813c4ee..1cac4e3d9dd1 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/content.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/content.dart @@ -41,8 +41,10 @@ final class Content { static Content text(String text) => Content('user', [TextPart(text)]); /// Return a [Content] with [InlineDataPart]. - static Content inlineData(String mimeType, Uint8List bytes) => - Content('user', [InlineDataPart(mimeType, bytes)]); + static Content inlineData(String mimeType, Uint8List bytes, + {MediaResolution? mediaResolution}) => + Content('user', + [InlineDataPart(mimeType, bytes, mediaResolution: mediaResolution)]); /// Return a [Content] with multiple [Part]s. static Content multi(Iterable parts) => Content('user', [...parts]); @@ -100,15 +102,19 @@ Part parsePart(Object? jsonObject) { final thoughtSignature = jsonObject.containsKey('thoughtSignature') ? jsonObject['thoughtSignature']! as String : null; + final mediaResolution = switch (jsonObject['mediaResolution']) { + {'level': final String level} => MediaResolution.parseValue(level), + _ => null, + }; if (jsonObject.containsKey('functionCall')) { final functionCall = jsonObject['functionCall']; - if (functionCall is Map && - functionCall.containsKey('name') && - functionCall.containsKey('args')) { + if (functionCall is Map && functionCall.containsKey('name')) { return FunctionCall._( functionCall['name'] as String, - functionCall['args'] as Map, + functionCall.containsKey('args') + ? functionCall['args'] as Map + : {}, id: functionCall['id'] as String?, isThought: isThought, thoughtSignature: thoughtSignature, @@ -157,6 +163,7 @@ Part parsePart(Object? jsonObject) { inlineDataResult['mimeType'] as String, base64Decode(inlineDataResult['data'] as String), willContinue: inlineDataResult['willContinue'] as bool?, + mediaResolution: mediaResolution, isThought: isThought, thoughtSignature: thoughtSignature, ); @@ -174,7 +181,9 @@ Part parsePart(Object? jsonObject) { } } => FileData._(mimeType, fileUri, - isThought: isThought, thoughtSignature: thoughtSignature), + mediaResolution: mediaResolution, + isThought: isThought, + thoughtSignature: thoughtSignature), _ => () { log('unhandled part format: $jsonObject'); return UnknownPart(jsonObject); @@ -261,6 +270,7 @@ final class InlineDataPart extends Part { this.mimeType, this.bytes, { this.willContinue, + this.mediaResolution, bool? isThought, }) : super( isThought: isThought, @@ -273,6 +283,7 @@ final class InlineDataPart extends Part { this.mimeType, this.bytes, { this.willContinue, + this.mediaResolution, bool? isThought, String? thoughtSignature, }) : super( @@ -284,6 +295,7 @@ final class InlineDataPart extends Part { this.mimeType, this.bytes, { this.willContinue, + this.mediaResolution, bool? isThought, String? thoughtSignature, }) : super( @@ -300,6 +312,12 @@ final class InlineDataPart extends Part { /// Whether there's more inline data coming for streaming. final bool? willContinue; + + /// The resolution to use for this media part. + /// + /// This overrides the request-level media resolution for this part. + final MediaResolution? mediaResolution; + @override Object toJson() { final superJson = super.toJson() as Map; @@ -310,6 +328,8 @@ final class InlineDataPart extends Part { 'mimeType': mimeType, if (willContinue != null) 'willContinue': willContinue, }, + if (mediaResolution != null) + 'mediaResolution': {'level': mediaResolution!.toJson()}, }; } @@ -433,6 +453,7 @@ final class FileData extends Part { const FileData( this.mimeType, this.fileUri, { + this.mediaResolution, bool? isThought, }) : super( isThought: isThought, @@ -444,6 +465,7 @@ final class FileData extends Part { const FileData.forTest( this.mimeType, this.fileUri, { + this.mediaResolution, bool? isThought, String? thoughtSignature, }) : super( @@ -454,6 +476,7 @@ final class FileData extends Part { const FileData._( this.mimeType, this.fileUri, { + this.mediaResolution, bool? isThought, String? thoughtSignature, }) : super( @@ -468,12 +491,19 @@ final class FileData extends Part { /// The gs link for Firebase Storage reference final String fileUri; + /// The resolution to use for this media part. + /// + /// This overrides the request-level media resolution for this part. + final MediaResolution? mediaResolution; + @override Object toJson() { final superJson = super.toJson() as Map; return { ...superJson, 'file_data': {'file_uri': fileUri, 'mime_type': mimeType}, + if (mediaResolution != null) + 'mediaResolution': {'level': mediaResolution!.toJson()}, }; } } diff --git a/packages/firebase_ai/firebase_ai/lib/src/developer/api.dart b/packages/firebase_ai/firebase_ai/lib/src/developer/api.dart index bb2e78811277..d89f711aea27 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/developer/api.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/developer/api.dart @@ -49,7 +49,13 @@ String _harmCategoryToJson(HarmCategory harmCategory) => switch (harmCategory) { HarmCategory.harassment => 'HARM_CATEGORY_HARASSMENT', HarmCategory.hateSpeech => 'HARM_CATEGORY_HATE_SPEECH', HarmCategory.sexuallyExplicit => 'HARM_CATEGORY_SEXUALLY_EXPLICIT', - HarmCategory.dangerousContent => 'HARM_CATEGORY_DANGEROUS_CONTENT' + HarmCategory.dangerousContent => 'HARM_CATEGORY_DANGEROUS_CONTENT', + HarmCategory.imageHate => 'HARM_CATEGORY_IMAGE_HATE', + HarmCategory.imageDangerousContent => + 'HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT', + HarmCategory.imageHarassment => 'HARM_CATEGORY_IMAGE_HARASSMENT', + HarmCategory.imageSexuallyExplicit => + 'HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT', }; Object _safetySettingToJson(SafetySetting safetySetting) { @@ -241,5 +247,11 @@ HarmCategory _parseHarmCategory(Object jsonObject) => switch (jsonObject) { 'HARM_CATEGORY_HATE_SPEECH' => HarmCategory.hateSpeech, 'HARM_CATEGORY_SEXUALLY_EXPLICIT' => HarmCategory.sexuallyExplicit, 'HARM_CATEGORY_DANGEROUS_CONTENT' => HarmCategory.dangerousContent, - _ => throw unhandledFormat('HarmCategory', jsonObject), + 'HARM_CATEGORY_IMAGE_HATE' => HarmCategory.imageHate, + 'HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT' => + HarmCategory.imageDangerousContent, + 'HARM_CATEGORY_IMAGE_HARASSMENT' => HarmCategory.imageHarassment, + 'HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT' => + HarmCategory.imageSexuallyExplicit, + _ => HarmCategory.unknown, }; diff --git a/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart b/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart index 3d7b22023e15..5841c0d2de47 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/firebase_ai.dart @@ -16,7 +16,8 @@ import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; +import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; import '../firebase_ai.dart'; @@ -25,7 +26,7 @@ import 'base_model.dart'; const _defaultLocation = 'us-central1'; /// The entrypoint for generative models. -class FirebaseAI extends FirebasePluginPlatform { +class FirebaseAI extends FirebasePlugin { FirebaseAI._({ required this.app, required this.location, @@ -62,12 +63,18 @@ class FirebaseAI extends FirebasePluginPlatform { /// If pass in [appCheck], request session will get protected from abusing. static FirebaseAI vertexAI({ FirebaseApp? app, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') FirebaseAppCheck? appCheck, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') FirebaseAuth? auth, String? location, bool? useLimitedUseAppCheckTokens, }) { app ??= Firebase.app(); + appCheck ??= app.getService(); + auth ??= app.getService(); var instanceKey = '${app.name}::vertexai::$location'; if (_cachedInstances.containsKey(instanceKey)) { @@ -95,11 +102,17 @@ class FirebaseAI extends FirebasePluginPlatform { /// If pass in [appCheck], request session will get protected from abusing. static FirebaseAI googleAI({ FirebaseApp? app, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') FirebaseAppCheck? appCheck, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') FirebaseAuth? auth, bool? useLimitedUseAppCheckTokens, }) { app ??= Firebase.app(); + appCheck ??= app.getService(); + auth ??= app.getService(); var instanceKey = '${app.name}::googleai'; if (_cachedInstances.containsKey(instanceKey)) { @@ -130,6 +143,9 @@ class FirebaseAI extends FirebasePluginPlatform { /// The optional [safetySettings] and [generationConfig] can be used to /// control and guide the generation. See [SafetySetting] and /// [GenerationConfig] for details. + /// + /// The optional [httpClient] can be used to customize HTTP request handling, + /// such as adding support for cancelling in-flight requests. GenerativeModel generativeModel({ required String model, List? safetySettings, @@ -137,6 +153,7 @@ class FirebaseAI extends FirebasePluginPlatform { List? tools, ToolConfig? toolConfig, Content? systemInstruction, + http.Client? httpClient, }) { return createGenerativeModel( model: model, @@ -151,6 +168,7 @@ class FirebaseAI extends FirebasePluginPlatform { toolConfig: toolConfig, systemInstruction: systemInstruction, useLimitedUseAppCheckTokens: useLimitedUseAppCheckTokens, + httpClient: httpClient, ); } diff --git a/packages/firebase_ai/firebase_ai/lib/src/firebaseai_version.dart b/packages/firebase_ai/firebase_ai/lib/src/firebaseai_version.dart index ba2295b2f513..bf472cb55ffa 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/firebaseai_version.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/firebaseai_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '0.2.4'; +const packageVersion = '0.3.0+5'; diff --git a/packages/firebase_ai/firebase_ai/lib/src/generative_model.dart b/packages/firebase_ai/firebase_ai/lib/src/generative_model.dart index 2bf58351eafe..844614583889 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/generative_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/generative_model.dart @@ -210,6 +210,7 @@ GenerativeModel createGenerativeModel({ List? tools, ToolConfig? toolConfig, Content? systemInstruction, + http.Client? httpClient, }) => GenerativeModel._( model: model, @@ -224,6 +225,7 @@ GenerativeModel createGenerativeModel({ tools: tools, toolConfig: toolConfig, systemInstruction: systemInstruction, + httpClient: httpClient, ); /// Creates a model with an overridden [ApiClient] for testing. diff --git a/packages/firebase_ai/firebase_ai/lib/src/image_config.dart b/packages/firebase_ai/firebase_ai/lib/src/image_config.dart new file mode 100644 index 000000000000..277272b82c29 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/lib/src/image_config.dart @@ -0,0 +1,110 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Configuration options for generating images with Gemini models. +final class ImageConfig { + /// Initializes configuration options for generating images with Gemini. + const ImageConfig({this.aspectRatio, this.imageSize}); + + /// The aspect ratio of generated images. + final ImageAspectRatio? aspectRatio; + + /// The size of the generated images. + final ImageSize? imageSize; + + /// Convert to json format. + Map toJson() => { + if (aspectRatio case final aspectRatio?) + 'aspectRatio': aspectRatio.toJson(), + if (imageSize case final imageSize?) 'imageSize': imageSize.toJson(), + }; +} + +/// An aspect ratio for generated images. +enum ImageAspectRatio { + /// Square (1:1) aspect ratio. + square1x1('1:1'), + + /// Portrait widescreen (9:16) aspect ratio. + portrait9x16('9:16'), + + /// Widescreen (16:9) aspect ratio. + landscape16x9('16:9'), + + /// Portrait full screen (3:4) aspect ratio. + portrait3x4('3:4'), + + /// Fullscreen (4:3) aspect ratio. + landscape4x3('4:3'), + + /// Portrait (2:3) aspect ratio. + portrait2x3('2:3'), + + /// Landscape (3:2) aspect ratio. + landscape3x2('3:2'), + + /// Portrait (4:5) aspect ratio. + portrait4x5('4:5'), + + /// Landscape (5:4) aspect ratio. + landscape5x4('5:4'), + + /// Portrait (1:4) aspect ratio. + portrait1x4('1:4'), + + /// Landscape (4:1) aspect ratio. + landscape4x1('4:1'), + + /// Portrait (1:8) aspect ratio. + portrait1x8('1:8'), + + /// Landscape (8:1) aspect ratio. + landscape8x1('8:1'), + + /// Ultrawide (21:9) aspect ratio. + ultrawide21x9('21:9'); + + const ImageAspectRatio(this._jsonString); + final String _jsonString; + + /// Convert to json format. + String toJson() => _jsonString; + + @override + String toString() => name; +} + +/// The size of images to generate. +enum ImageSize { + /// 512px (0.5K) image size. + size512('512'), + + /// 1K image size. + size1K('1K'), + + /// 2K image size. + size2K('2K'), + + /// 4K image size. + size4K('4K'); + + const ImageSize(this._jsonString); + final String _jsonString; + + /// Convert to json format. + String toJson() => _jsonString; + + @override + String toString() => name; +} diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart index 20810f86690e..8bf9ef018b9a 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_api.dart @@ -16,6 +16,11 @@ import 'dart:developer'; /// Specifies the level of safety filtering for image generation. /// /// If not specified, default will be "block_medium_and_above". +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenSafetyFilterLevel { /// Strongest filtering level, most strict blocking. blockLowAndAbove('block_low_and_above'), @@ -57,6 +62,11 @@ enum ImagenSafetyFilterLevel { /// Allow generation of people by the model. /// /// If not specified, the default value is "allow_adult". +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenPersonFilterLevel { /// Disallow the inclusion of people or faces in images. blockAll('dont_allow'), @@ -92,6 +102,11 @@ enum ImagenPersonFilterLevel { /// A class representing safety settings for image generation. /// /// It includes a safety filter level and a person filter level. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSafetySettings { // ignore: public_member_api_docs ImagenSafetySettings(this.safetyFilterLevel, this.personFilterLevel); @@ -114,6 +129,11 @@ final class ImagenSafetySettings { /// The aspect ratio for the image. /// /// The default value is "1:1". +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenAspectRatio { /// Square (1:1). square1x1('1:1'), @@ -155,6 +175,11 @@ enum ImagenAspectRatio { } /// Configuration options for image generation. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenGenerationConfig { // ignore: public_member_api_docs ImagenGenerationConfig( @@ -195,6 +220,11 @@ final class ImagenGenerationConfig { } /// Represents the image format and compression quality. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenFormat { // ignore: public_member_api_docs ImagenFormat(this.mimeType, this.compressionQuality); diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart index ef8be1bb31f6..01af99045211 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_content.dart @@ -28,6 +28,11 @@ sealed class ImagenImage { } /// Represents an image stored as a base64-encoded string. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenInlineImage implements ImagenImage { // ignore: public_member_api_docs ImagenInlineImage({ diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart index 5cff20d08bdb..99c8c1064c6a 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_edit.dart @@ -18,6 +18,11 @@ import 'package:meta/meta.dart'; /// The desired outcome of the image editing. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenEditMode { /// The result of the editing will be an insertion of the prompt in the masked /// region. @@ -37,6 +42,11 @@ enum ImagenEditMode { /// The type of the subject in the image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenSubjectReferenceType { /// The subject is a person. person('SUBJECT_TYPE_PERSON'), @@ -56,6 +66,11 @@ enum ImagenSubjectReferenceType { /// The type of control image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenControlType { /// Use edge detection to ensure the new image follow the same outlines. canny('CONTROL_TYPE_CANNY'), @@ -81,6 +96,11 @@ enum ImagenControlType { /// The mode of the mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) enum ImagenMaskMode { /// The mask is user provided. userProvided('MASK_MODE_USER_PROVIDED'), @@ -108,6 +128,11 @@ sealed class ImagenReferenceConfig { /// The configuration for the mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenMaskConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenMaskConfig({ @@ -137,6 +162,11 @@ final class ImagenMaskConfig extends ImagenReferenceConfig { /// The configuration for the subject. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSubjectConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenSubjectConfig({ @@ -161,6 +191,11 @@ final class ImagenSubjectConfig extends ImagenReferenceConfig { /// The configuration for the style. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenStyleConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenStyleConfig({ @@ -179,6 +214,11 @@ final class ImagenStyleConfig extends ImagenReferenceConfig { /// The configuration for the control. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenControlConfig extends ImagenReferenceConfig { // ignore: public_member_api_docs ImagenControlConfig({ @@ -214,6 +254,11 @@ final class ImagenControlConfig extends ImagenReferenceConfig { /// The configuration for image editing. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenEditingConfig { // ignore: public_member_api_docs ImagenEditingConfig({ @@ -230,6 +275,11 @@ final class ImagenEditingConfig { /// The dimensions of an image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenDimensions { // ignore: public_member_api_docs ImagenDimensions({ @@ -246,6 +296,11 @@ final class ImagenDimensions { /// The placement of an image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenImagePlacement { const ImagenImagePlacement._(this.x, this.y); diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart index 54789dcfadcc..197ed5714866 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_model.dart @@ -24,6 +24,11 @@ part of '../base_model.dart'; /// > Warning: For Vertex AI in Firebase, image generation using Imagen 3 models /// is in Public Preview, which means that the feature is not subject to any SLA /// or deprecation policy and could change in backwards-incompatible ways. +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenModel extends BaseApiClientModel { ImagenModel._( {required FirebaseApp app, diff --git a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart index c05785168424..f8cb307bb456 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/imagen/imagen_reference.dart @@ -30,6 +30,11 @@ enum _ReferenceType { /// A reference image for image editing. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) sealed class ImagenReferenceImage { ImagenReferenceImage._({ this.referenceConfig, @@ -72,6 +77,11 @@ sealed class ImagenReferenceImage { /// A reference image that is a mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) sealed class ImagenMaskReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenMaskReference({ @@ -86,6 +96,11 @@ sealed class ImagenMaskReference extends ImagenReferenceImage { /// A raw image. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenRawImage extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenRawImage({ @@ -96,6 +111,11 @@ final class ImagenRawImage extends ImagenReferenceImage { /// A raw mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenRawMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenRawMask({ @@ -113,6 +133,11 @@ final class ImagenRawMask extends ImagenMaskReference { /// A semantic mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSemanticMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenSemanticMask({ @@ -130,6 +155,11 @@ final class ImagenSemanticMask extends ImagenMaskReference { /// A background mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenBackgroundMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenBackgroundMask({ @@ -145,6 +175,11 @@ final class ImagenBackgroundMask extends ImagenMaskReference { /// A foreground mask. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenForegroundMask extends ImagenMaskReference { // ignore: public_member_api_docs ImagenForegroundMask({ @@ -160,6 +195,11 @@ final class ImagenForegroundMask extends ImagenMaskReference { /// A subject reference. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenSubjectReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenSubjectReference({ @@ -179,6 +219,11 @@ final class ImagenSubjectReference extends ImagenReferenceImage { /// A style reference. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenStyleReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenStyleReference({ @@ -196,6 +241,11 @@ final class ImagenStyleReference extends ImagenReferenceImage { /// A control reference. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class ImagenControlReference extends ImagenReferenceImage { // ignore: public_member_api_docs ImagenControlReference({ diff --git a/packages/firebase_ai/firebase_ai/lib/src/live_api.dart b/packages/firebase_ai/firebase_ai/lib/src/live_api.dart index 9db6bd845476..b961d5348786 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/live_api.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/live_api.dart @@ -15,86 +15,105 @@ import 'api.dart'; import 'content.dart'; import 'error.dart'; -/// Configuration for a prebuilt voice. -/// -/// This class allows specifying a voice by its name. -class PrebuiltVoiceConfig { +/// The audio transcription configuration. +class AudioTranscriptionConfig { // ignore: public_member_api_docs - const PrebuiltVoiceConfig({this.voiceName}); + Map toJson() => {}; +} - /// The voice name to use for speech synthesis. +/// Configures the sliding window context compression mechanism. +/// +/// The SlidingWindow method operates by discarding content at the beginning of +/// the context window. The resulting context will always begin at the start of +/// a USER role turn. System instructions will always remain at the start of the +/// result. +class SlidingWindow { + /// Creates a [SlidingWindow] instance. /// - /// See https://cloud.google.com/text-to-speech/docs/chirp3-hd for names and - /// sound demos. - final String? voiceName; + /// [targetTokens] (optional): The target number of tokens to keep in the + /// context window. + SlidingWindow({this.targetTokens}); + + /// The session reduction target, i.e., how many tokens we should keep. + final int? targetTokens; // ignore: public_member_api_docs Map toJson() => - {if (voiceName case final voiceName?) 'voice_name': voiceName}; + {if (targetTokens case final targetTokens?) 'targetTokens': targetTokens}; } -/// Configuration for the voice to be used in speech synthesis. +/// Enables context window compression to manage the model's context window. /// -/// This class currently supports using a prebuilt voice configuration. -class VoiceConfig { - // ignore: public_member_api_docs - VoiceConfig({this.prebuiltVoiceConfig}); - - // ignore: public_member_api_docs - final PrebuiltVoiceConfig? prebuiltVoiceConfig; +/// This mechanism prevents the context from exceeding a given length. +class ContextWindowCompressionConfig { + /// Creates a [ContextWindowCompressionConfig] instance. + /// + /// [triggerTokens] (optional): The number of tokens that triggers the + /// compression mechanism. + /// [slidingWindow] (optional): The sliding window compression mechanism to + /// use. + ContextWindowCompressionConfig({this.triggerTokens, this.slidingWindow}); + + /// The number of tokens (before running a turn) that triggers the context + /// window compression. + final int? triggerTokens; + + /// The sliding window compression mechanism. + final SlidingWindow? slidingWindow; // ignore: public_member_api_docs Map toJson() => { - if (prebuiltVoiceConfig case final prebuiltVoiceConfig?) - 'prebuilt_voice_config': prebuiltVoiceConfig.toJson() + if (triggerTokens case final triggerTokens?) + 'triggerTokens': triggerTokens, + if (slidingWindow case final slidingWindow?) + 'slidingWindow': slidingWindow.toJson() }; } -/// Configures speech synthesis settings. +/// Configuration for the session resumption mechanism. /// -/// Allows specifying the desired voice for speech synthesis. -class SpeechConfig { - /// Creates a [SpeechConfig] instance. +/// When included in the session setup, the server will send +/// [SessionResumptionUpdate] messages. +class SessionResumptionConfig { + /// Creates a [SessionResumptionConfig] to start a new resumable session. + /// + /// When this is included in the session setup, the server will send + /// [SessionResumptionUpdate] messages with handles that can be used to + /// resume the session later. + SessionResumptionConfig() : handle = null; + + /// Creates a [SessionResumptionConfig] to resume a previous session. + /// + /// [handle] is the session resumption handle received in a previous session's + /// [SessionResumptionUpdate]. + SessionResumptionConfig.resume(String this.handle); + + /// The session resumption handle of the previous session to restore. /// - /// [voiceName] See https://cloud.google.com/text-to-speech/docs/chirp3-hd - /// for names and sound demos. - SpeechConfig({String? voiceName}) - : voiceConfig = voiceName != null - ? VoiceConfig( - prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: voiceName)) - : null; - - /// The voice config to use for speech synthesis. - final VoiceConfig? voiceConfig; + /// If null, a new session will be started (and will be resumable if this + /// config was included). + final String? handle; + // ignore: public_member_api_docs Map toJson() => { - if (voiceConfig case final voiceConfig?) - 'voice_config': voiceConfig.toJson() + if (handle case final handle?) 'handle': handle, }; } -/// The audio transcription configuration. -class AudioTranscriptionConfig { - // ignore: public_member_api_docs - Map toJson() => {}; -} - /// Configures live generation settings. final class LiveGenerationConfig extends BaseGenerationConfig { // ignore: public_member_api_docs - LiveGenerationConfig({ - this.speechConfig, - this.inputAudioTranscription, - this.outputAudioTranscription, - super.responseModalities, - super.maxOutputTokens, - super.temperature, - super.topP, - super.topK, - super.presencePenalty, - super.frequencyPenalty, - }); - - /// The speech configuration. - final SpeechConfig? speechConfig; + LiveGenerationConfig( + {super.speechConfig, + this.inputAudioTranscription, + this.outputAudioTranscription, + this.contextWindowCompression, + super.responseModalities, + super.maxOutputTokens, + super.temperature, + super.topP, + super.topK, + super.presencePenalty, + super.frequencyPenalty, + super.mediaResolution}); /// The transcription of the input aligns with the input audio language. final AudioTranscriptionConfig? inputAudioTranscription; @@ -103,11 +122,12 @@ final class LiveGenerationConfig extends BaseGenerationConfig { /// the output audio. final AudioTranscriptionConfig? outputAudioTranscription; + /// The context window compression configuration. + final ContextWindowCompressionConfig? contextWindowCompression; + @override Map toJson() => { ...super.toJson(), - if (speechConfig case final speechConfig?) - 'speechConfig': speechConfig.toJson(), }; } @@ -222,6 +242,34 @@ class GoingAwayNotice implements LiveServerMessage { final String? timeLeft; } +/// An update of the session resumption state. +/// +/// This message is only sent if [SessionResumptionConfig] was set in the +/// session setup. +class SessionResumptionUpdate implements LiveServerMessage { + /// Creates a [SessionResumptionUpdate] instance. + /// + /// [newHandle] (optional): The new handle that represents the state that can + /// be resumed. + /// [resumable] (optional): Indicates if the session can be resumed at this + /// point. + /// [lastConsumedClientMessageIndex] (optional): The index of the last client + /// message that is included in the state represented by this update. + SessionResumptionUpdate( + {this.newHandle, this.resumable, this.lastConsumedClientMessageIndex}); + + /// The new handle that represents the state that can be resumed. Empty if + /// `resumable` is false. + final String? newHandle; + + /// Indicates if the session can be resumed at this point. + final bool? resumable; + + /// The index of the last client message that is included in the state + /// represented by this update. + final int? lastConsumedClientMessageIndex; +} + /// A single response chunk received during a live content generation. /// /// It can contain generated content, function calls to be executed, or @@ -449,8 +497,17 @@ LiveServerMessage _parseServerMessage(Object jsonObject) { } else if (json.containsKey('setupComplete')) { return LiveServerSetupComplete(); } else if (json.containsKey('goAway')) { - final goAwayJson = json['goAway'] as Map; + final goAwayJson = json['goAway'] as Map; return GoingAwayNotice(timeLeft: goAwayJson['timeLeft'] as String?); + } else if (json.containsKey('sessionResumptionUpdate')) { + final sessionResumptionUpdateJson = + json['sessionResumptionUpdate'] as Map; + return SessionResumptionUpdate( + newHandle: sessionResumptionUpdateJson['newHandle'] as String?, + resumable: sessionResumptionUpdateJson['resumable'] as bool?, + lastConsumedClientMessageIndex: + sessionResumptionUpdateJson['lastConsumedClientMessageIndex'] as int?, + ); } else { throw unhandledFormat('LiveServerMessage', json); } diff --git a/packages/firebase_ai/firebase_ai/lib/src/live_model.dart b/packages/firebase_ai/firebase_ai/lib/src/live_model.dart index 3414b1376af1..19e837fcf849 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/live_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/live_model.dart @@ -90,33 +90,17 @@ final class LiveGenerativeModel extends BaseModel { /// /// This function handles the WebSocket connection setup and returns an [LiveSession] /// object that can be used to communicate with the service. + /// [sessionResumption] (optional): The configuration for session resumption, + /// such as the handle to the previous session state to restore. /// /// Returns a [Future] that resolves to an [LiveSession] object upon successful /// connection. - Future connect() async { + Future connect( + {SessionResumptionConfig? sessionResumption}) async { final uri = _useVertexBackend ? _vertexAIUri() : _googleAIUri(); final modelString = _useVertexBackend ? _vertexAIModelString() : _googleAIModelString(); - final setupJson = { - 'setup': { - 'model': modelString, - if (_systemInstruction != null) - 'system_instruction': _systemInstruction.toJson(), - if (_tools != null) 'tools': _tools.map((t) => t.toJson()).toList(), - if (_liveGenerationConfig != null) ...{ - 'generation_config': _liveGenerationConfig.toJson(), - if (_liveGenerationConfig.inputAudioTranscription != null) - 'input_audio_transcription': - _liveGenerationConfig.inputAudioTranscription!.toJson(), - if (_liveGenerationConfig.outputAudioTranscription != null) - 'output_audio_transcription': - _liveGenerationConfig.outputAudioTranscription!.toJson(), - }, - } - }; - - final request = jsonEncode(setupJson); final headers = await BaseModel.firebaseTokens( _appCheck, _auth, @@ -124,14 +108,15 @@ final class LiveGenerativeModel extends BaseModel { _useLimitedUseAppCheckTokens, )(); - var ws = kIsWeb - ? WebSocketChannel.connect(Uri.parse(uri)) - : IOWebSocketChannel.connect(Uri.parse(uri), headers: headers); - await ws.ready; - - ws.sink.add(request); - - return LiveSession(ws); + return LiveSession.create( + uri: uri, + headers: headers, + modelString: modelString, + systemInstruction: _systemInstruction, + tools: _tools, + sessionResumption: sessionResumption, + liveGenerationConfig: _liveGenerationConfig, + ); } } diff --git a/packages/firebase_ai/firebase_ai/lib/src/live_session.dart b/packages/firebase_ai/firebase_ai/lib/src/live_session.dart index f136a644d03d..d69fd7f678c2 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/live_session.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/live_session.dart @@ -16,37 +16,212 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer'; +import 'package:flutter/foundation.dart'; +import 'package:web_socket_channel/io.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'content.dart'; import 'error.dart'; import 'live_api.dart'; +import 'tool.dart'; /// Manages asynchronous communication with Gemini model over a WebSocket /// connection. class LiveSession { // ignore: public_member_api_docs - LiveSession(this._ws) { + LiveSession._( + this._ws, { + required String uri, + required Map headers, + required String modelString, + Content? systemInstruction, + List? tools, + LiveGenerationConfig? liveGenerationConfig, + }) : _uri = uri, + _headers = headers, + _modelString = modelString, + _systemInstruction = systemInstruction, + _tools = tools, + _liveGenerationConfig = liveGenerationConfig, + _messageController = StreamController.broadcast() { + _listenToWebSocket(); + } + + /// Internal constructor for testing. + @visibleForTesting + factory LiveSession.forTesting(WebSocketChannel ws) { + return LiveSession._( + ws, + uri: '', + headers: {}, + modelString: '', + ); + } + + /// Establishes a connection to a live generation service. + /// + /// This function handles the WebSocket connection setup and returns an [LiveSession] + /// object that can be used to communicate with the service. + /// + /// Returns a [Future] that resolves to an [LiveSession] object upon successful + /// connection. + @internal + static Future create({ + required String uri, + required Map headers, + required String modelString, + Content? systemInstruction, + List? tools, + SessionResumptionConfig? sessionResumption, + LiveGenerationConfig? liveGenerationConfig, + }) async { + final ws = await _performWebSocketSetup( + uri: uri, + headers: headers, + modelString: modelString, + systemInstruction: systemInstruction, + tools: tools, + sessionResumption: sessionResumption, + liveGenerationConfig: liveGenerationConfig, + ); + return LiveSession._( + ws, + uri: uri, + headers: headers, + modelString: modelString, + systemInstruction: systemInstruction, + tools: tools, + liveGenerationConfig: liveGenerationConfig, + ); + } + + // Persisted values for session resumption. + final String _uri; + final Map _headers; + final String _modelString; + final Content? _systemInstruction; + final List? _tools; + final LiveGenerationConfig? _liveGenerationConfig; + + WebSocketChannel _ws; + StreamController _messageController; + late StreamSubscription _wsSubscription; + + static Future _performWebSocketSetup({ + required String uri, + required Map headers, + required String modelString, + Content? systemInstruction, + List? tools, + SessionResumptionConfig? sessionResumption, + LiveGenerationConfig? liveGenerationConfig, + }) async { + final setupJson = { + 'setup': { + 'model': modelString, + if (systemInstruction != null) + 'system_instruction': systemInstruction.toJson(), + if (tools != null) 'tools': tools.map((t) => t.toJson()).toList(), + if (sessionResumption != null) + 'session_resumption': sessionResumption.toJson(), + if (liveGenerationConfig != null) ...{ + 'generation_config': liveGenerationConfig.toJson(), + if (liveGenerationConfig.inputAudioTranscription != null) + 'input_audio_transcription': + liveGenerationConfig.inputAudioTranscription!.toJson(), + if (liveGenerationConfig.outputAudioTranscription != null) + 'output_audio_transcription': + liveGenerationConfig.outputAudioTranscription!.toJson(), + if (liveGenerationConfig.contextWindowCompression + case final contextWindowCompression?) + 'contextWindowCompression': contextWindowCompression.toJson() + }, + } + }; + + final request = jsonEncode(setupJson); + final ws = kIsWeb + ? WebSocketChannel.connect(Uri.parse(uri)) + : IOWebSocketChannel.connect(Uri.parse(uri), headers: headers); + await ws.ready; + + ws.sink.add(request); + return ws; + } + + void _listenToWebSocket() { _wsSubscription = _ws.stream.listen( (message) { try { - var jsonString = utf8.decode(message); + final String jsonString = + message is String ? message : utf8.decode(message as List); var response = json.decode(jsonString); - _messageController.add(parseServerResponse(response)); + if (!_messageController.isClosed) { + _messageController.add(parseServerResponse(response)); + } } catch (e) { - _messageController.addError(e); + if (!_messageController.isClosed && _messageController.hasListener) { + _messageController.addError(e); + } else { + log('live_session: Dropped parse error because no listeners', + error: e); + } } }, onError: (error) { - _messageController.addError(error); + if (!_messageController.isClosed && _messageController.hasListener) { + _messageController.addError(error); + } else { + log('live_session: Dropped stream error because no listeners', + error: error); + } + }, + onDone: () { + if (!_messageController.isClosed) { + _messageController.close(); + } }, - onDone: _messageController.close, ); } - final WebSocketChannel _ws; - final _messageController = StreamController.broadcast(); - late StreamSubscription _wsSubscription; + + /// Resumes an existing live session with the server. + /// + /// This closes the current WebSocket connection and establishes a new one using + /// the same configuration (URI, headers, model, system instruction, tools, etc.) + /// as the original session. + /// + /// [sessionResumption] (optional): The configuration for session resumption, + /// such as the handle to the previous session state to restore. + Future resumeSession( + {SessionResumptionConfig? sessionResumption}) async { + try { + await _wsSubscription.cancel().timeout(const Duration(seconds: 2), + onTimeout: () { + log('live_session.resumeSession: WebSocket subscription cancel timed out.', + error: TimeoutException('Cancel timed out')); + }); + await _ws.sink.close().timeout(const Duration(seconds: 2), onTimeout: () { + log('live_session.resumeSession: WebSocket close timed out.', + error: TimeoutException('Close timed out')); + }); + + _ws = await _performWebSocketSetup( + uri: _uri, + headers: _headers, + modelString: _modelString, + systemInstruction: _systemInstruction, + tools: _tools, + sessionResumption: sessionResumption, + liveGenerationConfig: _liveGenerationConfig, + ); + } catch (e) { + log('live_session.resumeSession: WebSocket setup failed', error: e); + rethrow; + } + + _listenToWebSocket(); + } /// Sends content to the server. /// @@ -166,17 +341,27 @@ class LiveSession { await for (final result in _messageController.stream) { yield result; - if (result case LiveServerContent(turnComplete: true)) { - break; // Exit the loop when the turn is complete - } } } /// Closes the WebSocket connection. Future close() async { - await _wsSubscription.cancel(); - await _messageController.close(); - await _ws.sink.close(); + try { + await _wsSubscription.cancel().timeout(const Duration(seconds: 1), + onTimeout: () { + log('live_session.close: cancel timed out', + error: TimeoutException('Cancel timed out')); + }); + if (!_messageController.isClosed) { + await _messageController.close(); + } + await _ws.sink.close().timeout(const Duration(seconds: 1), onTimeout: () { + log('live_session.close: sink close timed out', + error: TimeoutException('Sink close timed out')); + }); + } catch (e) { + log('live_session.close: error during close', error: e); + } } void _checkWsStatus() { diff --git a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_chat.dart b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_chat.dart new file mode 100644 index 000000000000..2efa63689dba --- /dev/null +++ b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_chat.dart @@ -0,0 +1,248 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import 'dart:async'; + +import '../api.dart'; +import '../base_model.dart'; +import '../content.dart'; +import '../utils/chat_utils.dart'; +import '../utils/mutex.dart'; +import 'template_tool.dart'; + +/// A back-and-forth chat with a server template. +/// +/// Records messages sent and received in [history]. The history will always +/// record the content from the first candidate in the +/// [GenerateContentResponse], other candidates may be available on the returned +/// response. The history reflects the most current state of the chat session. +final class TemplateChatSession { + TemplateChatSession._( + this._templateHistoryGenerateContent, + this._templateHistoryGenerateContentStream, + this._templateId, + this._inputs, + this._history, + this._tools, + this._toolConfig, + this._maxTurns, + ) : _autoFunctions = _tools + ?.expand((tool) => tool.templateAutoFunctionDeclarations) + .fold({}, (map, function) { + map?[function.name] = function; + return map; + }); + + final Future Function( + Iterable content, String templateId, + {required Map inputs, + List? tools, + TemplateToolConfig? toolConfig}) _templateHistoryGenerateContent; + + final Stream Function( + Iterable content, String templateId, + {required Map inputs, + List? tools, + TemplateToolConfig? toolConfig}) _templateHistoryGenerateContentStream; + + final String _templateId; + final Map _inputs; + final List _history; + final List? _tools; + final TemplateToolConfig? _toolConfig; + final Map? _autoFunctions; + final int _maxTurns; + + final _mutex = Mutex(); + + /// The content that has been successfully sent to, or received from, the + /// generative model. + /// + /// If there are outstanding requests from calls to [sendMessage], + /// these will not be reflected in the history. + /// Messages without a candidate in the response are not recorded in history, + /// including the message sent to the model. + Iterable get history => _history.skip(0); + + /// Sends [message] to the server template as a continuation of the chat [history]. + /// + /// Prepends the history to the request and uses the provided model to + /// generate new content, providing the session's initialized inputs. + /// + /// When there are no candidates in the response, the [message] and response + /// are ignored and will not be recorded in the [history]. + Future sendMessage(Content message) async { + final lock = await _mutex.acquire(); + try { + final requestHistory = [message]; + var turn = 0; + while (turn < _maxTurns) { + final response = await _templateHistoryGenerateContent( + _history.followedBy(requestHistory), + _templateId, + inputs: _inputs, + tools: _tools, + toolConfig: _toolConfig, + ); + + final functionCalls = response.functionCalls; + final shouldAutoExecute = _autoFunctions != null && + _autoFunctions.isNotEmpty && + functionCalls.isNotEmpty && + functionCalls.every((c) => _autoFunctions.containsKey(c.name)); + + if (!shouldAutoExecute) { + // Standard handling: Update history and return the response to the user. + if (response.candidates case [final candidate, ...]) { + _history.add(message); + final normalizedContent = candidate.content.role == null + ? Content('model', candidate.content.parts) + : candidate.content; + _history.add(normalizedContent); + } + return response; + } + + // Auto function execution + requestHistory.add(response.candidates.first.content); + final functionResponses = []; + for (final functionCall in functionCalls) { + final function = _autoFunctions[functionCall.name]; + + Object? result; + try { + result = await function!.callable(functionCall.args); + } catch (e) { + result = e.toString(); + } + functionResponses + .add(FunctionResponse(functionCall.name, {'result': result})); + } + requestHistory.add(Content('function', functionResponses)); + turn++; + } + throw Exception('Max turns of $_maxTurns reached.'); + } finally { + lock.release(); + } + } + + /// Sends [message] to the server template as a continuation of the chat + /// [history]. + /// + /// Returns a stream of responses, which may be chunks of a single aggregate + /// response. + /// + /// Prepends the history to the request and uses the provided model to + /// generate new content, providing the session's initialized inputs. + /// + /// When there are no candidates in the response, the [message] and response + /// are ignored and will not be recorded in the [history]. + Stream sendMessageStream(Content message) { + final controller = StreamController(); + _mutex.acquire().then((lock) async { + try { + final requestHistory = [message]; + var turn = 0; + while (turn < _maxTurns) { + final responses = _templateHistoryGenerateContentStream( + _history.followedBy(requestHistory), + _templateId, + inputs: _inputs, + tools: _tools, + toolConfig: _toolConfig, + ); + + final turnChunks = []; + await for (final response in responses) { + turnChunks.add(response); + controller.add(response); + } + if (turnChunks.isEmpty) break; + final aggregatedContent = historyAggregate(turnChunks.map((r) { + final content = r.candidates.firstOrNull?.content; + if (content == null) { + throw Exception('No content in response candidate'); + } + return content; + }).toList()); + + final functionCalls = + aggregatedContent.parts.whereType().toList(); + + final shouldAutoExecute = _autoFunctions != null && + _autoFunctions.isNotEmpty && + functionCalls.isNotEmpty && + functionCalls.every((c) => _autoFunctions.containsKey(c.name)); + + if (!shouldAutoExecute) { + _history.addAll(requestHistory); + _history.add(aggregatedContent); + return; + } + + requestHistory.add(aggregatedContent); + final functionResponseFutures = + functionCalls.map((functionCall) async { + final function = _autoFunctions[functionCall.name]; + + Object? result; + try { + result = await function!.callable(functionCall.args); + } catch (e) { + result = e.toString(); + } + return FunctionResponse(functionCall.name, {'result': result}); + }); + final functionResponseParts = + await Future.wait(functionResponseFutures); + requestHistory.add(Content.functionResponses(functionResponseParts)); + turn++; + } + throw Exception('Max turns of $_maxTurns reached.'); + } catch (e, s) { + controller.addError(e, s); + } finally { + lock.release(); + unawaited(controller.close()); + } + }); + return controller.stream; + } +} + +/// An extension on [TemplateGenerativeModel] that provides a `startChat` method. +extension StartTemplateChatExtension on TemplateGenerativeModel { + /// Starts a [TemplateChatSession] that will use this model to respond to messages. + /// + /// ```dart + /// final chat = model.startChat('my_template', inputs: {'language': 'en'}); + /// final response = await chat.sendMessage(Content.text('Hello there.')); + /// print(response.text); + /// ``` + TemplateChatSession startChat(String templateId, + {required Map inputs, + List? history, + List? tools, + TemplateToolConfig? toolConfig, + int? maxTurns}) => + TemplateChatSession._( + templateGenerateContentWithHistory, + templateGenerateContentWithHistoryStream, + templateId, + inputs, + history ?? [], + tools ?? [], + toolConfig, + maxTurns ?? 5); +} diff --git a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_generative_model.dart b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_generative_model.dart index 75e9029f44b4..0b94fc9eaea4 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_generative_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_generative_model.dart @@ -69,9 +69,16 @@ final class TemplateGenerativeModel extends BaseTemplateApiClientModel { /// Sends a "templateGenerateContent" API request for the configured model. @experimental Future generateContent(String templateId, - {required Map inputs}) => - makeTemplateRequest(TemplateTask.templateGenerateContent, templateId, - inputs, null, _serializationStrategy.parseGenerateContentResponse); + {required Map inputs, + TemplateToolConfig? toolConfig}) => + makeTemplateRequest( + TemplateTask.templateGenerateContent, + templateId, + inputs, + null, // history + null, // tools + toolConfig, + _serializationStrategy.parseGenerateContentResponse); /// Generates a stream of content responding to [templateId] and [inputs]. /// @@ -79,12 +86,49 @@ final class TemplateGenerativeModel extends BaseTemplateApiClientModel { /// and waits for the response. @experimental Stream generateContentStream(String templateId, - {required Map inputs}) { + {required Map inputs, TemplateToolConfig? toolConfig}) { return streamTemplateRequest( TemplateTask.templateStreamGenerateContent, templateId, inputs, - null, + null, // history + null, // tools + toolConfig, + _serializationStrategy.parseGenerateContentResponse); + } + + /// Generates content from a template with the given [templateId], [inputs] and + /// [history]. + @experimental + Future templateGenerateContentWithHistory( + Iterable history, String templateId, + {required Map inputs, + List? tools, + TemplateToolConfig? toolConfig}) => + makeTemplateRequest( + TemplateTask.templateGenerateContent, + templateId, + inputs, + history, + tools, + toolConfig, + _serializationStrategy.parseGenerateContentResponse); + + /// Generates a stream of content from a template with the given [templateId], + /// [inputs] and [history]. + @experimental + Stream templateGenerateContentWithHistoryStream( + Iterable history, String templateId, + {required Map inputs, + List? tools, + TemplateToolConfig? toolConfig}) { + return streamTemplateRequest( + TemplateTask.templateStreamGenerateContent, + templateId, + inputs, + history, + tools, + toolConfig, _serializationStrategy.parseGenerateContentResponse); } } diff --git a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart index e27dd5eaaa9d..14437e5f2ec3 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_imagen_model.dart @@ -16,6 +16,11 @@ part of '../base_model.dart'; /// An image model that connects to a remote server template. @experimental +@Deprecated( + 'All Imagen models are deprecated and will shut down as early as June 2026. ' + 'As a replacement, you can migrate your apps to use Gemini Image models (the ' + '"Nano Banana" models)(https://firebase.google.com/docs/ai-logic/imagen-models-migration).', +) final class TemplateImagenModel extends BaseTemplateApiClientModel { TemplateImagenModel._testModel( {required FirebaseApp app, @@ -68,7 +73,9 @@ final class TemplateImagenModel extends BaseTemplateApiClientModel { TemplateTask.templatePredict, templateId, inputs, - null, + null, // history + null, // tools + null, // toolConfig (jsonObject) => parseImagenGenerationResponse(jsonObject), ); diff --git a/packages/firebase_ai/firebase_ai/lib/src/server_template/template_tool.dart b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_tool.dart new file mode 100644 index 000000000000..603875020c3b --- /dev/null +++ b/packages/firebase_ai/firebase_ai/lib/src/server_template/template_tool.dart @@ -0,0 +1,112 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import 'dart:async'; + +import '../schema.dart'; +import '../tool.dart'; + +/// A collection of template tools. +final class TemplateTool { + // ignore: public_member_api_docs + TemplateTool._(this._functionDeclarations); + + /// Returns a [TemplateTool] instance with list of [TemplateFunctionDeclaration]. + static TemplateTool functionDeclarations( + List functionDeclarations) { + return TemplateTool._(functionDeclarations); + } + + /// Returns a list of all [TemplateAutoFunctionDeclaration] objects + /// found within the [_functionDeclarations] list. + List get templateAutoFunctionDeclarations { + return _functionDeclarations + ?.whereType() + .toList() ?? + []; + } + + final List? _functionDeclarations; + + /// Convert to json object. + Map toJson() => { + if (_functionDeclarations case final functionDeclarations? + when functionDeclarations.isNotEmpty) + 'templateFunctions': functionDeclarations + .map((f) => f.hasSchema ? f.toJson() : null) + .where((f) => f != null) + .toList(), + }; +} + +/// A function declaration for a template tool. +class TemplateFunctionDeclaration { + // ignore: public_member_api_docs + TemplateFunctionDeclaration(this.name, + {Map? parameters, + List optionalParameters = const []}) + : _schemaObject = parameters != null + ? JSONSchema.object( + properties: parameters, optionalProperties: optionalParameters) + : null; + + /// The name of the function. + /// + /// Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum + /// length of 63. + final String name; + + final Schema? _schemaObject; + + /// Whether the function declaration has a schema override. + bool get hasSchema => _schemaObject != null; + + /// Convert to json object. + Map toJson() => { + 'name': name, + if (_schemaObject case final schemaObject?) + 'inputSchema': schemaObject.toJson(), + }; +} + +/// A function declaration for a template tool that can be called by the model. +final class TemplateAutoFunctionDeclaration + extends TemplateFunctionDeclaration { + // ignore: public_member_api_docs + TemplateAutoFunctionDeclaration( + {required String name, + required this.callable, + Map? parameters, + List optionalParameters = const []}) + : super(name, + parameters: parameters, optionalParameters: optionalParameters); + + /// The callable function that this declaration represents. + final FutureOr> Function(Map args) + callable; +} + +/// Config for template tools to use with server prompts. +final class TemplateToolConfig { + // ignore: public_member_api_docs + TemplateToolConfig({RetrievalConfig? retrievalConfig}) + : _retrievalConfig = retrievalConfig; + + final RetrievalConfig? _retrievalConfig; + + /// Convert to json object. + Map toJson() => { + if (_retrievalConfig case final retrievalConfig?) + 'retrievalConfig': retrievalConfig.toJson(), + }; +} diff --git a/packages/firebase_ai/firebase_ai/lib/src/speech_config.dart b/packages/firebase_ai/firebase_ai/lib/src/speech_config.dart new file mode 100644 index 000000000000..1196ac05b92a --- /dev/null +++ b/packages/firebase_ai/firebase_ai/lib/src/speech_config.dart @@ -0,0 +1,112 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:meta/meta.dart'; + +/// Speech configuration class for controlling the model's speech and audio generation behaviors. +class SpeechConfig { + /// Constructs a [SpeechConfig] for a single-speaker setup. + SpeechConfig({this.voiceName, this.languageCode}) + : multiSpeakerVoiceConfig = null; + + /// Constructs a [SpeechConfig] for a multi-speaker setup. + SpeechConfig.multiSpeaker( + {required this.multiSpeakerVoiceConfig, this.languageCode}) + : voiceName = null; + + /// The voice name to use for a single-speaker setup. + final String? voiceName; + + /// The multi-speaker configuration. + final MultiSpeakerVoiceConfig? multiSpeakerVoiceConfig; + + /// The optional IETF BCP-47 language code. + final String? languageCode; + + /// Convert to json format. + @internal + Map toJson() => { + if (voiceName != null) + 'voice_config': VoiceConfig( + prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: voiceName), + ).toJson(), + if (multiSpeakerVoiceConfig case final multiSpeakerVoiceConfig?) + 'multi_speaker_voice_config': multiSpeakerVoiceConfig.toJson(), + if (languageCode case final languageCode?) + 'language_code': languageCode, + }; +} + +/// Configuration for a multi-speaker audio generation setup. +class MultiSpeakerVoiceConfig { + /// Constructor + MultiSpeakerVoiceConfig({required this.speakerVoiceConfigs}); + + /// A list of voice configurations for the participating speakers. + final List speakerVoiceConfigs; + + /// Convert to json format. + Map toJson() => { + 'speaker_voice_configs': + speakerVoiceConfigs.map((e) => e.toJson()).toList(), + }; +} + +/// Configures a participating speaker within a multi-speaker setup. +class SpeakerVoiceConfig { + /// Constructor + SpeakerVoiceConfig({required this.speaker, required this.voiceName}); + + /// The unique name/identifier of the speaker. + final String speaker; + + /// The specific voice assigned to this speaker. + final String voiceName; + + /// Convert to json format. + Map toJson() => { + 'speaker': speaker, + 'voice_config': VoiceConfig( + prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: voiceName), + ).toJson(), + }; +} + +/// Configuration for a prebuilt voice. +class PrebuiltVoiceConfig { + /// Constructor + const PrebuiltVoiceConfig({this.voiceName}); + + /// The voice name to use for speech synthesis. + final String? voiceName; + + /// Convert to json format. + Map toJson() => + {if (voiceName case final voiceName?) 'voice_name': voiceName}; +} + +/// Configuration for the voice to be used in speech synthesis. +class VoiceConfig { + /// Constructor + VoiceConfig({this.prebuiltVoiceConfig}); + + /// The prebuilt voice configuration. + final PrebuiltVoiceConfig? prebuiltVoiceConfig; + + /// Convert to json format. + Map toJson() => { + if (prebuiltVoiceConfig case final prebuiltVoiceConfig?) + 'prebuilt_voice_config': prebuiltVoiceConfig.toJson() + }; +} diff --git a/packages/firebase_ai/firebase_ai/lib/src/tool.dart b/packages/firebase_ai/firebase_ai/lib/src/tool.dart index 29a7c7124c53..195db5f81836 100644 --- a/packages/firebase_ai/firebase_ai/lib/src/tool.dart +++ b/packages/firebase_ai/firebase_ai/lib/src/tool.dart @@ -24,12 +24,12 @@ import 'schema.dart'; final class Tool { // ignore: public_member_api_docs Tool._(this._functionDeclarations, this._googleSearch, this._codeExecution, - this._urlContext); + this._urlContext, this._googleMaps); /// Returns a [Tool] instance with list of [FunctionDeclaration]. static Tool functionDeclarations( List functionDeclarations) { - return Tool._(functionDeclarations, null, null, null); + return Tool._(functionDeclarations, null, null, null, null); } /// Creates a tool that allows the model to use Grounding with Google Search. @@ -50,13 +50,13 @@ final class Tool { /// /// Returns a `Tool` configured for Google Search. static Tool googleSearch({GoogleSearch googleSearch = const GoogleSearch()}) { - return Tool._(null, googleSearch, null, null); + return Tool._(null, googleSearch, null, null, null); } /// Returns a [Tool] instance that enables the model to use Code Execution. static Tool codeExecution( {CodeExecution codeExecution = const CodeExecution()}) { - return Tool._(null, null, codeExecution, null); + return Tool._(null, null, codeExecution, null, null); } /// Creates a tool that allows you to provide additional context to the models @@ -73,7 +73,27 @@ final class Tool { /// is in Public Preview, which means that the feature is not subject to any SLA /// or deprecation policy and could change in backwards-incompatible ways. static Tool urlContext({UrlContext urlContext = const UrlContext()}) { - return Tool._(null, null, null, urlContext); + return Tool._(null, null, null, urlContext, null); + } + + /// Creates a tool that allows the model to use Grounding with Google Maps. + /// + /// Grounding with Google Maps can be used to allow the model to connect to + /// Google Maps to access and incorporate location-based information into its + /// responses. + /// + /// When using this feature, you are required to comply with the + /// "Grounding with Google Maps" usage requirements for your chosen API + /// provider: + /// [Gemini Developer API](https://ai.google.dev/gemini-api/terms#grounding-with-google-maps) + /// or Vertex AI Gemini API (see [Service Terms](https://cloud.google.com/terms/service-terms) + /// section within the Service Specific Terms). + /// + /// - [googleMaps]: An empty [GoogleMaps] object. + /// + /// Returns a `Tool` configured for Google Maps. + static Tool googleMaps({GoogleMaps googleMaps = const GoogleMaps()}) { + return Tool._(null, null, null, null, googleMaps); } /// A list of `FunctionDeclarations` available to the model that can be used @@ -97,6 +117,10 @@ final class Tool { /// A tool that allows providing URL context to the model. final UrlContext? _urlContext; + /// A tool that allows the model to connect to Google Maps to access + /// location-based information. + final GoogleMaps? _googleMaps; + /// Returns a list of all [AutoFunctionDeclaration] objects /// found within the [_functionDeclarations] list. List get autoFunctionDeclarations { @@ -117,6 +141,8 @@ final class Tool { 'codeExecution': _codeExecution.toJson(), if (_urlContext case final _urlContext?) 'urlContext': _urlContext.toJson(), + if (_googleMaps case final _googleMaps?) + 'googleMaps': _googleMaps.toJson(), }; } @@ -138,6 +164,23 @@ final class GoogleSearch { Map toJson() => {}; } +/// A tool that allows a Gemini model to connect to Google Maps to access and +/// incorporate location-based information into its responses. +/// +/// Important: If using Grounding with Google Maps, you are required to comply +/// with the "Grounding with Google Maps" usage requirements for your chosen API +/// provider: +/// [Gemini Developer API](https://ai.google.dev/gemini-api/terms#grounding-with-google-maps) +/// or Vertex AI Gemini API (see [Service Terms](https://cloud.google.com/terms/service-terms) +/// section within the Service Specific Terms). +final class GoogleMaps { + // ignore: public_member_api_docs + const GoogleMaps(); + + /// Convert to json object. + Map toJson() => {}; +} + /// A tool that allows you to provide additional context to the models in the /// form of public web URLs. By including URLs in your request, the Gemini /// model will access the content from those pages to inform and enhance its @@ -229,15 +272,57 @@ final class AutoFunctionDeclaration extends FunctionDeclaration { /// Config for tools to use with model. final class ToolConfig { // ignore: public_member_api_docs - ToolConfig({this.functionCallingConfig}); + ToolConfig({this.functionCallingConfig, this.retrievalConfig}); /// Config for function calling. final FunctionCallingConfig? functionCallingConfig; + /// Config that specifies information which can be used by tools during inference calls. + final RetrievalConfig? retrievalConfig; + /// Convert to json object. Map toJson() => { if (functionCallingConfig case final config?) 'functionCallingConfig': config.toJson(), + if (retrievalConfig case final config?) + 'retrievalConfig': config.toJson(), + }; +} + +/// An object that represents a latitude/longitude pair. +final class LatLng { + // ignore: public_member_api_docs + LatLng({required this.latitude, required this.longitude}); + + /// The latitude in degrees. It must be in the range [-90.0, +90.0]. + final double latitude; + + /// The longitude in degrees. It must be in the range [-180.0, +180.0]. + final double longitude; + + /// Convert to json object. + Map toJson() => { + 'latitude': latitude, + 'longitude': longitude, + }; +} + +/// The configuration that specifies information which can be used by tools +/// during inference calls. +final class RetrievalConfig { + // ignore: public_member_api_docs + RetrievalConfig({this.latLng, this.languageCode}); + + /// A latitude/longitude pair. + final LatLng? latLng; + + /// The language code. + final String? languageCode; + + /// Convert to json object. + Map toJson() => { + if (latLng case final latLng?) 'latLng': latLng.toJson(), + if (languageCode case final languageCode?) 'languageCode': languageCode, }; } diff --git a/packages/firebase_ai/firebase_ai/macos/firebase_ai/Package.swift b/packages/firebase_ai/firebase_ai/macos/firebase_ai/Package.swift new file mode 100644 index 000000000000..c551b27e41a6 --- /dev/null +++ b/packages/firebase_ai/firebase_ai/macos/firebase_ai/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import PackageDescription + +let package = Package( + name: "firebase_ai", + platforms: [ + .macOS("10.15") + ], + products: [ + .library(name: "firebase-ai", targets: ["firebase_ai"]) + ], + dependencies: [], + targets: [ + .target( + name: "firebase_ai", + dependencies: [], + resources: [ + .process("Resources") + ] + ) + ] +) diff --git a/packages/firebase_ai/firebase_ai/pubspec.yaml b/packages/firebase_ai/firebase_ai/pubspec.yaml index 9726f04b2316..92d03c96661c 100644 --- a/packages/firebase_ai/firebase_ai/pubspec.yaml +++ b/packages/firebase_ai/firebase_ai/pubspec.yaml @@ -1,6 +1,7 @@ name: firebase_ai description: Firebase AI Logic SDK. -version: 3.10.0 +version: 3.13.1 +resolution: workspace homepage: https://firebase.google.com/docs/vertex-ai/get-started?platform=flutter topics: - firebase @@ -16,14 +17,14 @@ platforms: web: environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' flutter: ">=3.16.0" dependencies: - firebase_app_check: ^0.4.2 - firebase_auth: ^6.3.0 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_app_check: ^0.4.5 + firebase_auth: ^6.5.4 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter http: ^1.1.0 @@ -31,7 +32,7 @@ dependencies: web_socket_channel: ^3.0.1 dev_dependencies: - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 flutter_test: sdk: flutter matcher: ^0.12.16 diff --git a/packages/firebase_ai/firebase_ai/test/api_test.dart b/packages/firebase_ai/firebase_ai/test/api_test.dart index 156e22dfe154..a177abe5e374 100644 --- a/packages/firebase_ai/firebase_ai/test/api_test.dart +++ b/packages/firebase_ai/firebase_ai/test/api_test.dart @@ -18,6 +18,7 @@ import 'dart:convert'; import 'package:firebase_ai/src/api.dart'; import 'package:firebase_ai/src/content.dart'; import 'package:firebase_ai/src/error.dart'; +import 'package:firebase_ai/src/image_config.dart'; import 'package:firebase_ai/src/schema.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -289,6 +290,13 @@ void main() { 'HARM_CATEGORY_SEXUALLY_EXPLICIT'); expect(HarmCategory.dangerousContent.toJson(), 'HARM_CATEGORY_DANGEROUS_CONTENT'); + expect(HarmCategory.imageHate.toJson(), 'HARM_CATEGORY_IMAGE_HATE'); + expect(HarmCategory.imageDangerousContent.toJson(), + 'HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT'); + expect(HarmCategory.imageHarassment.toJson(), + 'HARM_CATEGORY_IMAGE_HARASSMENT'); + expect(HarmCategory.imageSexuallyExplicit.toJson(), + 'HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT'); }); test('HarmProbability toJson and toString', () { @@ -315,9 +323,55 @@ void main() { expect(FinishReason.recitation.toJson(), 'RECITATION'); expect(FinishReason.malformedFunctionCall.toJson(), 'MALFORMED_FUNCTION_CALL'); + expect(FinishReason.blocklist.toJson(), 'BLOCKLIST'); + expect(FinishReason.prohibitedContent.toJson(), 'PROHIBITED_CONTENT'); + expect(FinishReason.spii.toJson(), 'SPII'); + expect(FinishReason.imageSafety.toJson(), 'IMAGE_SAFETY'); + expect(FinishReason.imageProhibitedContent.toJson(), + 'IMAGE_PROHIBITED_CONTENT'); + expect(FinishReason.imageOther.toJson(), 'IMAGE_OTHER'); + expect(FinishReason.noImage.toJson(), 'NO_IMAGE'); + expect(FinishReason.imageRecitation.toJson(), 'IMAGE_RECITATION'); + expect(FinishReason.language.toJson(), 'LANGUAGE'); + expect(FinishReason.unexpectedToolCall.toJson(), 'UNEXPECTED_TOOL_CALL'); + expect(FinishReason.tooManyToolCalls.toJson(), 'TOO_MANY_TOOL_CALLS'); + expect(FinishReason.missingThoughtSignature.toJson(), + 'MISSING_THOUGHT_SIGNATURE'); + expect(FinishReason.malformedResponse.toJson(), 'MALFORMED_RESPONSE'); expect(FinishReason.other.toJson(), 'OTHER'); }); + test('FinishReason parseValue', () { + expect(FinishReason.parseValue('STOP'), FinishReason.stop); + expect(FinishReason.parseValue('MAX_TOKENS'), FinishReason.maxTokens); + expect(FinishReason.parseValue('SAFETY'), FinishReason.safety); + expect(FinishReason.parseValue('RECITATION'), FinishReason.recitation); + expect(FinishReason.parseValue('MALFORMED_FUNCTION_CALL'), + FinishReason.malformedFunctionCall); + expect(FinishReason.parseValue('BLOCKLIST'), FinishReason.blocklist); + expect(FinishReason.parseValue('PROHIBITED_CONTENT'), + FinishReason.prohibitedContent); + expect(FinishReason.parseValue('SPII'), FinishReason.spii); + expect(FinishReason.parseValue('IMAGE_SAFETY'), FinishReason.imageSafety); + expect(FinishReason.parseValue('IMAGE_PROHIBITED_CONTENT'), + FinishReason.imageProhibitedContent); + expect(FinishReason.parseValue('IMAGE_OTHER'), FinishReason.imageOther); + expect(FinishReason.parseValue('NO_IMAGE'), FinishReason.noImage); + expect(FinishReason.parseValue('IMAGE_RECITATION'), + FinishReason.imageRecitation); + expect(FinishReason.parseValue('LANGUAGE'), FinishReason.language); + expect(FinishReason.parseValue('UNEXPECTED_TOOL_CALL'), + FinishReason.unexpectedToolCall); + expect(FinishReason.parseValue('TOO_MANY_TOOL_CALLS'), + FinishReason.tooManyToolCalls); + expect(FinishReason.parseValue('MISSING_THOUGHT_SIGNATURE'), + FinishReason.missingThoughtSignature); + expect(FinishReason.parseValue('MALFORMED_RESPONSE'), + FinishReason.malformedResponse); + expect(FinishReason.parseValue('OTHER'), FinishReason.other); + expect(FinishReason.parseValue('UNSPECIFIED'), FinishReason.unknown); + }); + test('ContentModality toJson and toString', () { expect(ContentModality.unspecified.toJson(), 'MODALITY_UNSPECIFIED'); expect(ContentModality.text.toJson(), 'TEXT'); @@ -439,10 +493,51 @@ void main() { }); }); + group('ImageConfig', () { + test('toJson with all fields', () { + const config = ImageConfig( + aspectRatio: ImageAspectRatio.portrait9x16, + imageSize: ImageSize.size2K, + ); + expect(config.toJson(), { + 'aspectRatio': '9:16', + 'imageSize': '2K', + }); + }); + + test('toJson with some fields null', () { + const config = ImageConfig( + aspectRatio: ImageAspectRatio.landscape16x9, + ); + expect(config.toJson(), { + 'aspectRatio': '16:9', + }); + }); + }); + group('GenerationConfig & BaseGenerationConfig', () { + test('GenerationConfig serializes mediaResolution', () { + final config = GenerationConfig( + mediaResolution: MediaResolution.high, + ); + + expect(config.toJson(), { + 'mediaResolution': 'MEDIA_RESOLUTION_HIGH', + }); + }); + + test('GenerationConfig rejects ultraHigh mediaResolution', () { + expect( + () => GenerationConfig(mediaResolution: MediaResolution.ultraHigh), + throwsA(isA()), + ); + }); + test('GenerationConfig toJson with all fields', () { final schema = Schema.object(properties: {}); final thinkingConfig = ThinkingConfig(thinkingBudget: 100); + const imageConfig = ImageConfig( + aspectRatio: ImageAspectRatio.square1x1, imageSize: ImageSize.size1K); final config = GenerationConfig( candidateCount: 1, stopSequences: ['\n', 'stop'], @@ -454,7 +549,9 @@ void main() { frequencyPenalty: 0.4, responseMimeType: 'application/json', responseSchema: schema, + mediaResolution: MediaResolution.medium, thinkingConfig: thinkingConfig, + imageConfig: imageConfig, ); expect(config.toJson(), { 'candidateCount': 1, @@ -467,7 +564,12 @@ void main() { 'stopSequences': ['\n', 'stop'], 'responseMimeType': 'application/json', 'responseSchema': schema.toJson(), + 'mediaResolution': 'MEDIA_RESOLUTION_MEDIUM', 'thinkingConfig': {'thinkingBudget': 100}, + 'imageConfig': { + 'aspectRatio': '1:1', + 'imageSize': '1K', + }, }); }); @@ -735,6 +837,75 @@ void main() { expect(response.usageMetadata!.candidatesTokensDetails, hasLength(1)); }); + test('parses image harm categories in safetyRatings', () { + final json = { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': ''} + ] + }, + 'finishReason': 'STOP', + 'safetyRatings': [ + { + 'category': 'HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT', + 'probability': 'NEGLIGIBLE' + }, + { + 'category': 'HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT', + 'probability': 'NEGLIGIBLE' + }, + { + 'category': 'HARM_CATEGORY_IMAGE_HATE', + 'probability': 'NEGLIGIBLE' + }, + { + 'category': 'HARM_CATEGORY_IMAGE_HARASSMENT', + 'probability': 'NEGLIGIBLE' + }, + ] + } + ] + }; + final response = + VertexSerialization().parseGenerateContentResponse(json); + final ratings = response.candidates.first.safetyRatings!; + expect(ratings.map((r) => r.category), [ + HarmCategory.imageDangerousContent, + HarmCategory.imageSexuallyExplicit, + HarmCategory.imageHate, + HarmCategory.imageHarassment, + ]); + }); + + test('falls back to HarmCategory.unknown for unrecognized values', () { + final json = { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': ''} + ] + }, + 'finishReason': 'STOP', + 'safetyRatings': [ + { + 'category': 'HARM_CATEGORY_SOMETHING_NEW', + 'probability': 'NEGLIGIBLE' + } + ] + } + ] + }; + final response = + VertexSerialization().parseGenerateContentResponse(json); + expect(response.candidates.first.safetyRatings!.first.category, + HarmCategory.unknown); + }); + group('usageMetadata parsing', () { test('parses usageMetadata when thoughtsTokenCount is set', () { final json = { @@ -1048,6 +1219,18 @@ void main() { FinishReason.malformedFunctionCall); }); + test('parses unexpectedToolCall finishReason', () { + final jsonResponse = { + 'candidates': [ + {'finishReason': 'UNEXPECTED_TOOL_CALL'} + ] + }; + final response = + VertexSerialization().parseGenerateContentResponse(jsonResponse); + expect(response.candidates.first.finishReason, + FinishReason.unexpectedToolCall); + }); + test( 'parses groundingSupports and filters out entries without a segment', () { diff --git a/packages/firebase_ai/firebase_ai/test/base_model_test.dart b/packages/firebase_ai/firebase_ai/test/base_model_test.dart index 089ef6fe2382..f4aba609e2dd 100644 --- a/packages/firebase_ai/firebase_ai/test/base_model_test.dart +++ b/packages/firebase_ai/firebase_ai/test/base_model_test.dart @@ -31,6 +31,20 @@ class MockFirebaseApp extends Mock implements FirebaseApp { @override bool get isAutomaticDataCollectionEnabled => true; + + FirebaseAppCheck? mockAppCheck; + FirebaseAuth? mockAuth; + + @override + T? getService() { + if (T == FirebaseAppCheck) { + return mockAppCheck as T?; + } + if (T == FirebaseAuth) { + return mockAuth as T?; + } + return null; + } } // Mock FirebaseOptions @@ -131,6 +145,84 @@ void main() { expect(headers.length, 2); }); + test('firebaseTokens discovers App Check token dynamically at request time', + () async { + final mockApp = MockFirebaseApp(); + final mockAppCheck = MockFirebaseAppCheck(); + when(mockAppCheck.getToken()) + .thenAnswer((_) async => 'dynamic-app-check-token'); + mockApp.mockAppCheck = mockAppCheck; + + final tokenFunction = + BaseModel.firebaseTokens(null, null, mockApp, false); + final headers = await tokenFunction(); + + expect(headers['X-Firebase-AppCheck'], 'dynamic-app-check-token'); + expect(headers['X-Firebase-AppId'], 'test-app-id'); + expect(headers.length, 3); + }); + + test('firebaseTokens discovers Auth ID token dynamically at request time', + () async { + final mockApp = MockFirebaseApp(); + final mockAuth = MockFirebaseAuth(); + final mockUser = MockUser(); + when(mockUser.getIdToken()).thenAnswer((_) async => 'dynamic-id-token'); + when(mockAuth.currentUser).thenReturn(mockUser); + mockApp.mockAuth = mockAuth; + + final tokenFunction = + BaseModel.firebaseTokens(null, null, mockApp, false); + final headers = await tokenFunction(); + + expect(headers['Authorization'], 'Firebase dynamic-id-token'); + expect(headers['X-Firebase-AppId'], 'test-app-id'); + expect(headers.length, 3); + }); + + test('firebaseTokens discovers both tokens dynamically at request time', + () async { + final mockApp = MockFirebaseApp(); + final mockAppCheck = MockFirebaseAppCheck(); + final mockAuth = MockFirebaseAuth(); + final mockUser = MockUser(); + + when(mockAppCheck.getToken()) + .thenAnswer((_) async => 'dynamic-app-check-token'); + when(mockUser.getIdToken()).thenAnswer((_) async => 'dynamic-id-token'); + when(mockAuth.currentUser).thenReturn(mockUser); + + mockApp.mockAppCheck = mockAppCheck; + mockApp.mockAuth = mockAuth; + + final tokenFunction = + BaseModel.firebaseTokens(null, null, mockApp, false); + final headers = await tokenFunction(); + + expect(headers['X-Firebase-AppCheck'], 'dynamic-app-check-token'); + expect(headers['Authorization'], 'Firebase dynamic-id-token'); + expect(headers['X-Firebase-AppId'], 'test-app-id'); + expect(headers.length, 4); + }); + + test( + 'firebaseTokens discovers App Check token dynamically with limited use', + () async { + final mockApp = MockFirebaseApp(); + final mockAppCheck = MockFirebaseAppCheck(); + + when(mockAppCheck.getLimitedUseToken()) + .thenAnswer((_) async => 'dynamic-limited-use-token'); + mockApp.mockAppCheck = mockAppCheck; + + final tokenFunction = BaseModel.firebaseTokens(null, null, mockApp, true); + final headers = await tokenFunction(); + + expect(headers['X-Firebase-AppCheck'], 'dynamic-limited-use-token'); + expect(headers['X-Firebase-AppId'], 'test-app-id'); + expect(headers.length, 3); + }); + test('firebaseTokens includes all tokens if available', () async { final mockAppCheck = MockFirebaseAppCheck(); when(mockAppCheck.getToken()) diff --git a/packages/firebase_ai/firebase_ai/test/content_test.dart b/packages/firebase_ai/firebase_ai/test/content_test.dart index d9c50082b1dd..281c420d1acf 100644 --- a/packages/firebase_ai/firebase_ai/test/content_test.dart +++ b/packages/firebase_ai/firebase_ai/test/content_test.dart @@ -15,6 +15,7 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:firebase_ai/src/api.dart'; import 'package:firebase_ai/src/content.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -46,6 +47,29 @@ void main() { expect(content.parts[0], isA()); }); + test('inlineData() accepts mediaResolution', () { + final content = Content.inlineData( + 'image/png', + Uint8List(0), + mediaResolution: MediaResolution.high, + ); + + expect(content.toJson(), { + 'role': 'user', + 'parts': [ + { + 'inlineData': { + 'mimeType': 'image/png', + 'data': '', + }, + 'mediaResolution': { + 'level': 'MEDIA_RESOLUTION_HIGH', + }, + } + ], + }); + }); + test('multi()', () { final content = Content('user', [const TextPart('Test'), InlineDataPart('image/png', Uint8List(0))]); @@ -125,6 +149,24 @@ void main() { expect(inlineData['willContinue'], true); }); + test('InlineDataPart serializes mediaResolution', () { + final part = InlineDataPart( + 'image/png', + Uint8List(0), + mediaResolution: MediaResolution.ultraHigh, + ); + + expect(part.toJson(), { + 'inlineData': { + 'mimeType': 'image/png', + 'data': '', + }, + 'mediaResolution': { + 'level': 'MEDIA_RESOLUTION_ULTRA_HIGH', + }, + }); + }); + test('FunctionCall with isThought and thoughtSignature toJson', () { const part = FunctionCall.forTest( 'myFunction', @@ -182,6 +224,24 @@ void main() { expect(fileData['file_uri'], 'gs://bucket-name/path'); expect(json['thought'], true); }); + + test('FileData serializes mediaResolution', () { + const part = FileData( + 'image/png', + 'gs://bucket-name/path', + mediaResolution: MediaResolution.high, + ); + + expect(part.toJson(), { + 'file_data': { + 'mime_type': 'image/png', + 'file_uri': 'gs://bucket-name/path', + }, + 'mediaResolution': { + 'level': 'MEDIA_RESOLUTION_HIGH', + }, + }); + }); }); group('parsePart', () { diff --git a/packages/firebase_ai/firebase_ai/test/developer_api_test.dart b/packages/firebase_ai/firebase_ai/test/developer_api_test.dart index 40ce3739e1b9..85116e454646 100644 --- a/packages/firebase_ai/firebase_ai/test/developer_api_test.dart +++ b/packages/firebase_ai/firebase_ai/test/developer_api_test.dart @@ -219,6 +219,43 @@ void main() { expect(groundingSupports.groundingChunkIndices, [0]); }); + test('parses json with google maps grounding chunk', () { + final jsonResponse = { + 'candidates': [ + { + 'content': { + 'parts': [ + {'text': 'This is a maps response.'} + ] + }, + 'finishReason': 'STOP', + 'groundingMetadata': { + 'groundingChunks': [ + { + 'maps': { + 'uri': 'https://maps.google.com/?cid=123', + 'title': 'Google HQ', + 'placeId': 'ChIJS5dFe_cZzosR26ZvwqWaMAM', + } + } + ], + } + } + ] + }; + + final response = DeveloperSerialization() + .parseGenerateContentResponse(jsonResponse); + final groundingMetadata = response.candidates.first.groundingMetadata; + + expect(groundingMetadata, isNotNull); + final groundingChunk = groundingMetadata!.groundingChunks.first; + expect(groundingChunk.maps?.uri, 'https://maps.google.com/?cid=123'); + expect(groundingChunk.maps?.title, 'Google HQ'); + expect(groundingChunk.maps?.placeId, 'ChIJS5dFe_cZzosR26ZvwqWaMAM'); + expect(groundingChunk.web, isNull); + }); + test( 'parses groundingMetadata with all optional fields null/missing and empty lists', () { diff --git a/packages/firebase_ai/firebase_ai/test/firebase_vertexai_test.dart b/packages/firebase_ai/firebase_ai/test/firebase_vertexai_test.dart index ace2fa4d673c..52fab592b2f5 100644 --- a/packages/firebase_ai/firebase_ai/test/firebase_vertexai_test.dart +++ b/packages/firebase_ai/firebase_ai/test/firebase_vertexai_test.dart @@ -12,10 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:convert'; + import 'package:firebase_ai/firebase_ai.dart'; import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; import 'mock.dart'; @@ -28,6 +33,7 @@ void main() { late FirebaseApp customApp; late FirebaseApp limitTokenApp; late FirebaseAppCheck customAppCheck; + late FirebaseAuth customAuth; late FirebaseAppCheck limitTokenAppCheck; group('FirebaseAI Tests', () { @@ -47,6 +53,17 @@ void main() { appCheck = FirebaseAppCheck.instance; customAppCheck = FirebaseAppCheck.instanceFor(app: customApp); limitTokenAppCheck = FirebaseAppCheck.instanceFor(app: limitTokenApp); + customAuth = FirebaseAuth.instanceFor(app: customApp); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.' + 'FirebaseAppCheckHostApi.getToken', + (_) async { + return const StandardMessageCodec().encodeMessage( + ['app-check-token'], + ); + }, + ); }); test('Singleton behavior', () { @@ -96,6 +113,97 @@ void main() { expect(vertexAIAppCheck.useLimitedUseAppCheckTokens, true); }); - // ... other tests (e.g., with different parameters) + test('Instance creation with auto-injected AppCheck', () { + final vertexAI = FirebaseAI.vertexAI(app: customApp); + + expect(vertexAI.app, equals(customApp)); + expect(vertexAI.appCheck, equals(customAppCheck)); + }); + + test('Instance creation with auto-injected Auth', () { + final vertexAI = FirebaseAI.vertexAI(app: customApp); + + expect(vertexAI.app, equals(customApp)); + expect(vertexAI.auth, equals(customAuth)); + }); + + test('generativeModel creation with Grounding tools', () { + final ai = FirebaseAI.googleAI(); + + final model = ai.generativeModel( + model: 'gemini-2.5-flash', + tools: [Tool.googleMaps()], + toolConfig: ToolConfig( + retrievalConfig: RetrievalConfig( + latLng: LatLng(latitude: 37.42, longitude: -122.08), + languageCode: 'en-US', + ), + ), + ); + + expect(model, isA()); + }); + + test('generativeModel uses provided HTTP client', () async { + final requests = []; + final client = _RecordingClient((request) { + requests.add(request); + + if (request.url.path.endsWith(':streamGenerateContent')) { + return http.StreamedResponse( + Stream.value(utf8.encode('data: ${jsonEncode(_response)}\n\n')), + 200, + ); + } + + return http.StreamedResponse( + Stream.value(utf8.encode(jsonEncode(_response))), + 200, + ); + }); + final ai = FirebaseAI.googleAI(app: app); + + final model = ai.generativeModel( + model: 'gemini-pro', + httpClient: client, + ); + + await model.generateContent([Content.text('prompt')]); + await model.generateContentStream([Content.text('prompt')]).drain(); + + expect(requests, hasLength(2)); + expect( + requests.first.url.path, + endsWith('/models/gemini-pro:generateContent'), + ); + expect( + requests.last.url.path, + endsWith('/models/gemini-pro:streamGenerateContent'), + ); + }); }); } + +class _RecordingClient extends http.BaseClient { + _RecordingClient(this._handler); + + final http.StreamedResponse Function(http.BaseRequest request) _handler; + + @override + Future send(http.BaseRequest request) async { + return _handler(request); + } +} + +const _response = { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': 'Some Response'}, + ], + }, + }, + ], +}; diff --git a/packages/firebase_ai/firebase_ai/test/live_session_test.dart b/packages/firebase_ai/firebase_ai/test/live_session_test.dart new file mode 100644 index 000000000000..693aba00b26a --- /dev/null +++ b/packages/firebase_ai/firebase_ai/test/live_session_test.dart @@ -0,0 +1,99 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; +import 'dart:convert'; + +import 'package:firebase_ai/src/live_api.dart'; +import 'package:firebase_ai/src/live_session.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +class FakeWebSocketChannel implements WebSocketChannel { + final StreamController _controller = StreamController(); + + @override + Stream get stream => _controller.stream; + + @override + WebSocketSink get sink => throw UnimplementedError(); + + void emit(dynamic message) => _controller.add(message); + + void close() => _controller.close(); + + @override + int? get closeCode => null; + + @override + String? get closeReason => null; + + @override + Future get ready => Future.value(); + + @override + String? get protocol => null; + + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +void main() { + group('LiveSession Tests', () { + test('processes String messages from WebSocket on Web', () async { + final fakeWs = FakeWebSocketChannel(); + final session = LiveSession.forTesting(fakeWs); + + const jsonMessage = '{"setupComplete": {}}'; + + final completer = Completer(); + final subscription = session.receive().listen((response) { + if (response.message is LiveServerSetupComplete) { + completer.complete(true); + } + }); + + fakeWs.emit(jsonMessage); + + final result = await completer.future.timeout(const Duration(seconds: 5)); + expect(result, isTrue); + + await subscription.cancel(); + fakeWs.close(); + }); + + test('processes List messages from WebSocket', () async { + final fakeWs = FakeWebSocketChannel(); + final session = LiveSession.forTesting(fakeWs); + + const jsonMessage = '{"setupComplete": {}}'; + final bytes = utf8.encode(jsonMessage); + + final completer = Completer(); + final subscription = session.receive().listen((response) { + if (response.message is LiveServerSetupComplete) { + completer.complete(true); + } + }); + + fakeWs.emit(bytes); + + final result = await completer.future.timeout(const Duration(seconds: 5)); + expect(result, isTrue); + + await subscription.cancel(); + fakeWs.close(); + }); + }); +} diff --git a/packages/firebase_ai/firebase_ai/test/live_test.dart b/packages/firebase_ai/firebase_ai/test/live_test.dart index 8eb09d308476..9965c59ca46b 100644 --- a/packages/firebase_ai/firebase_ai/test/live_test.dart +++ b/packages/firebase_ai/firebase_ai/test/live_test.dart @@ -18,6 +18,7 @@ import 'package:firebase_ai/src/api.dart'; import 'package:firebase_ai/src/content.dart'; import 'package:firebase_ai/src/error.dart'; import 'package:firebase_ai/src/live_api.dart'; +import 'package:firebase_ai/src/speech_config.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -34,6 +35,54 @@ void main() { expect(speechConfigWithoutVoice.toJson(), {}); }); + test('SpeechConfig with languageCode toJson() returns correct JSON', () { + final speechConfigWithLanguage = + SpeechConfig(voiceName: 'Aoede', languageCode: 'en-US'); + expect(speechConfigWithLanguage.toJson(), { + 'voice_config': { + 'prebuilt_voice_config': {'voice_name': 'Aoede'} + }, + 'language_code': 'en-US', + }); + + final speechConfigLanguageOnly = SpeechConfig(languageCode: 'fr-FR'); + expect(speechConfigLanguageOnly.toJson(), { + 'language_code': 'fr-FR', + }); + }); + + test('SpeechConfig.multiSpeaker toJson() returns correct JSON', () { + final multiSpeechConfig = SpeechConfig.multiSpeaker( + multiSpeakerVoiceConfig: MultiSpeakerVoiceConfig( + speakerVoiceConfigs: [ + SpeakerVoiceConfig(speaker: 'Joe', voiceName: 'Kore'), + SpeakerVoiceConfig(speaker: 'Jane', voiceName: 'Puck'), + ], + ), + languageCode: 'en-US', + ); + + expect(multiSpeechConfig.toJson(), { + 'multi_speaker_voice_config': { + 'speaker_voice_configs': [ + { + 'speaker': 'Joe', + 'voice_config': { + 'prebuilt_voice_config': {'voice_name': 'Kore'} + } + }, + { + 'speaker': 'Jane', + 'voice_config': { + 'prebuilt_voice_config': {'voice_name': 'Puck'} + } + } + ] + }, + 'language_code': 'en-US', + }); + }); + test('ResponseModalities enum toJson() returns correct value', () { expect(ResponseModalities.text.toJson(), 'TEXT'); expect(ResponseModalities.image.toJson(), 'IMAGE'); @@ -48,6 +97,7 @@ void main() { temperature: 0.8, topP: 0.95, topK: 40, + mediaResolution: MediaResolution.low, ); expect(liveGenerationConfig.toJson(), { @@ -61,12 +111,62 @@ void main() { } }, 'responseModalities': ['TEXT', 'AUDIO'], + 'mediaResolution': 'MEDIA_RESOLUTION_LOW', }); final liveGenerationConfigWithoutOptionals = LiveGenerationConfig(); expect(liveGenerationConfigWithoutOptionals.toJson(), {}); }); + test('GenerationConfig with SpeechConfig toJson() returns correct JSON', + () { + final config = GenerationConfig( + speechConfig: SpeechConfig(voiceName: 'Aoede', languageCode: 'en-US'), + ); + + expect(config.toJson(), { + 'speechConfig': { + 'voice_config': { + 'prebuilt_voice_config': {'voice_name': 'Aoede'} + }, + 'language_code': 'en-US', + } + }); + + final multiConfig = GenerationConfig( + speechConfig: SpeechConfig.multiSpeaker( + multiSpeakerVoiceConfig: MultiSpeakerVoiceConfig( + speakerVoiceConfigs: [ + SpeakerVoiceConfig(speaker: 'Joe', voiceName: 'Kore'), + ], + ), + ), + ); + + expect(multiConfig.toJson(), { + 'speechConfig': { + 'multi_speaker_voice_config': { + 'speaker_voice_configs': [ + { + 'speaker': 'Joe', + 'voice_config': { + 'prebuilt_voice_config': {'voice_name': 'Kore'} + } + } + ] + } + } + }); + }); + + test('SessionResumptionConfig toJson() returns correct JSON', () { + final resumableConfig = SessionResumptionConfig(); + expect(resumableConfig.toJson(), {}); + + final resumeConfig = SessionResumptionConfig.resume('some_handle'); + expect(resumeConfig.toJson(), {'handle': 'some_handle'}); + }); + test('LiveServerContent constructor and properties', () { final content = Content.text('Hello, world!'); final message = LiveServerContent( diff --git a/packages/firebase_ai/firebase_ai/test/response_parsing_test.dart b/packages/firebase_ai/firebase_ai/test/response_parsing_test.dart index 5aa1809d2023..7ad3aebe6572 100644 --- a/packages/firebase_ai/firebase_ai/test/response_parsing_test.dart +++ b/packages/firebase_ai/firebase_ai/test/response_parsing_test.dart @@ -1115,6 +1115,187 @@ void main() { expect(urlContextMetadata.urlMetadata[0].urlRetrievalStatus, UrlRetrievalStatus.error); }); + + test('parses json with google maps grounding chunk', () { + final jsonResponse = { + 'candidates': [ + { + 'content': { + 'parts': [ + {'text': 'This is a maps response.'} + ] + }, + 'finishReason': 'STOP', + 'groundingMetadata': { + 'groundingChunks': [ + { + 'maps': { + 'uri': 'https://maps.google.com/?cid=123', + 'title': 'Google HQ', + 'placeId': 'ChIJS5dFe_cZzosR26ZvwqWaMAM', + } + } + ], + } + } + ] + }; + + final response = + VertexSerialization().parseGenerateContentResponse(jsonResponse); + final groundingMetadata = response.candidates.first.groundingMetadata; + + expect(groundingMetadata, isNotNull); + final groundingChunk = groundingMetadata!.groundingChunks.first; + expect(groundingChunk.maps?.uri, 'https://maps.google.com/?cid=123'); + expect(groundingChunk.maps?.title, 'Google HQ'); + expect(groundingChunk.maps?.placeId, 'ChIJS5dFe_cZzosR26ZvwqWaMAM'); + expect(groundingChunk.web, isNull); + }); + + test('with unknown safety ratings', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "text": "Some text" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "MEDIUM" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "FAKE_NEW_HARM_PROBABILITY" + }, + { + "category": "FAKE_NEW_HARM_CATEGORY", + "probability": "HIGH" + } + ] + } + ], + "promptFeedback": { + "safetyRatings": [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "MEDIUM" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "FAKE_NEW_HARM_PROBABILITY" + }, + { + "category": "FAKE_NEW_HARM_CATEGORY", + "probability": "HIGH" + } + ] + } +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([ + const TextPart('Some text'), + ]), + [ + SafetyRating( + HarmCategory.harassment, + HarmProbability.medium, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.unknown, + ), + SafetyRating( + HarmCategory.unknown, + HarmProbability.high, + ), + ], + null, + FinishReason.stop, + null, + ), + ], + PromptFeedback(null, null, [ + SafetyRating( + HarmCategory.harassment, + HarmProbability.medium, + ), + SafetyRating( + HarmCategory.dangerousContent, + HarmProbability.unknown, + ), + SafetyRating( + HarmCategory.unknown, + HarmProbability.high, + ), + ]), + ), + ), + ); + }); + + test('with an empty function call', () async { + const response = ''' +{ + "candidates": [ + { + "content": { + "parts": [ + { + "functionCall": { + "name": "current_time" + } + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0 + } + ] +} +'''; + final decoded = jsonDecode(response) as Object; + final generateContentResponse = + VertexSerialization().parseGenerateContentResponse(decoded); + expect( + generateContentResponse, + matchesGenerateContentResponse( + GenerateContentResponse( + [ + Candidate( + Content.model([ + const FunctionCall('current_time', {}), + ]), + null, + null, + FinishReason.stop, + null, + ), + ], + null, + ), + ), + ); + }); }); group('parses and throws error responses', () { diff --git a/packages/firebase_ai/firebase_ai/test/server_template_test.dart b/packages/firebase_ai/firebase_ai/test/server_template_test.dart index ebc2170e7037..1dbde35afc0c 100644 --- a/packages/firebase_ai/firebase_ai/test/server_template_test.dart +++ b/packages/firebase_ai/firebase_ai/test/server_template_test.dart @@ -13,6 +13,7 @@ // limitations under the License. import 'dart:convert'; +import 'dart:typed_data'; import 'package:firebase_ai/firebase_ai.dart'; import 'package:firebase_ai/src/base_model.dart'; @@ -86,6 +87,64 @@ void main() { expect(response.text, 'Some response'); }); + test('generateContent serializes inline image inputs', () async { + final mockHttp = MockClient((request) async { + final body = jsonDecode(request.body) as Map; + expect(body['inputs'], { + 'screenshot': { + 'isInline': true, + 'mimeType': 'image/jpeg', + 'contents': base64Encode([1, 2, 3]), + }, + }); + return http.Response(jsonEncode(_arbitraryGenerateContentResponse), 200, + headers: {'content-type': 'application/json'}); + }); + + final model = createModel(mockHttp); + final response = await model.generateContent( + templateId, + inputs: { + 'screenshot': InlineDataPart( + 'image/jpeg', + Uint8List.fromList([1, 2, 3]), + ), + }, + ); + expect(response.text, 'Some response'); + }); + + test('generateContent with TemplateToolConfig passes retrievalConfig', + () async { + final mockHttp = MockClient((request) async { + final body = jsonDecode(request.body) as Map; + expect(request.url.path, + endsWith('/templates/$templateId:templateGenerateContent')); + expect(body['inputs'], {'prompt': 'Some prompt'}); + expect(body['toolConfig'], { + 'retrievalConfig': { + 'latLng': {'latitude': 1.0, 'longitude': 2.0}, + 'languageCode': 'en' + } + }); + return http.Response(jsonEncode(_arbitraryGenerateContentResponse), 200, + headers: {'content-type': 'application/json'}); + }); + + final model = createModel(mockHttp); + final response = await model.generateContent( + templateId, + inputs: {'prompt': 'Some prompt'}, + toolConfig: TemplateToolConfig( + retrievalConfig: RetrievalConfig( + latLng: LatLng(latitude: 1, longitude: 2), + languageCode: 'en', + ), + ), + ); + expect(response.text, 'Some response'); + }); + test('generateContentStream can make successful request', () async { final mockHttp = MockClient((request) async { final body = jsonDecode(request.body) as Map; @@ -105,6 +164,41 @@ void main() { final response = await responseStream.first; expect(response.text, 'Some response'); }); + + test('generateContentStream with TemplateToolConfig passes retrievalConfig', + () async { + final mockHttp = MockClient((request) async { + final body = jsonDecode(request.body) as Map; + expect(request.url.path, + endsWith('/templates/$templateId:templateStreamGenerateContent')); + expect(body['inputs'], {'prompt': 'Some prompt'}); + expect(body['toolConfig'], { + 'retrievalConfig': { + 'latLng': {'latitude': 1.0, 'longitude': 2.0}, + 'languageCode': 'en' + } + }); + final responsePayload = jsonEncode(_arbitraryGenerateContentResponse); + final stream = Stream.value(utf8.encode('data: $responsePayload')); + final streamedResponse = http.StreamedResponse(stream, 200, + headers: {'content-type': 'application/json'}); + return http.Response.fromStream(streamedResponse); + }); + + final model = createModel(mockHttp); + final responseStream = model.generateContentStream( + templateId, + inputs: {'prompt': 'Some prompt'}, + toolConfig: TemplateToolConfig( + retrievalConfig: RetrievalConfig( + latLng: LatLng(latitude: 1, longitude: 2), + languageCode: 'en', + ), + ), + ); + final response = await responseStream.first; + expect(response.text, 'Some response'); + }); }); group('TemplateImagenModel', () { diff --git a/packages/firebase_ai/firebase_ai/test/tool_test.dart b/packages/firebase_ai/firebase_ai/test/tool_test.dart index 8ac727e3a963..ff5b06152612 100644 --- a/packages/firebase_ai/firebase_ai/test/tool_test.dart +++ b/packages/firebase_ai/firebase_ai/test/tool_test.dart @@ -314,6 +314,14 @@ void main() { }); }); + // Test Tool.googleMaps() + test('Tool.googleMaps()', () { + final tool = Tool.googleMaps(); + expect(tool.toJson(), { + 'googleMaps': {}, + }); + }); + // Test ToolConfig test('ToolConfig with FunctionCallingConfig', () { final config = ToolConfig( @@ -329,7 +337,50 @@ void main() { expect(config.toJson(), {}); }); - // Test GoogleSearch, CodeExecution, UrlContext toJson() + test('ToolConfig with RetrievalConfig', () { + final config = ToolConfig( + retrievalConfig: RetrievalConfig( + latLng: LatLng(latitude: 37.422, longitude: -122.084), + languageCode: 'en-US', + ), + ); + expect(config.toJson(), { + 'retrievalConfig': { + 'latLng': {'latitude': 37.422, 'longitude': -122.084}, + 'languageCode': 'en-US', + }, + }); + }); + + // Test LatLng and RetrievalConfig + test('LatLng.toJson()', () { + final latLng = LatLng(latitude: 37.42, longitude: -122.08); + expect(latLng.toJson(), {'latitude': 37.42, 'longitude': -122.08}); + }); + + test('RetrievalConfig.toJson() with all fields', () { + final config = RetrievalConfig( + latLng: LatLng(latitude: 1.2, longitude: 2.1), + languageCode: 'fr', + ); + expect(config.toJson(), { + 'latLng': {'latitude': 1.2, 'longitude': 2.1}, + 'languageCode': 'fr', + }); + }); + + test('RetrievalConfig.toJson() with partial fields', () { + final config1 = + RetrievalConfig(latLng: LatLng(latitude: 1.2, longitude: 2.1)); + expect(config1.toJson(), { + 'latLng': {'latitude': 1.2, 'longitude': 2.1} + }); + + final config2 = RetrievalConfig(languageCode: 'fr'); + expect(config2.toJson(), {'languageCode': 'fr'}); + }); + + // Test GoogleSearch, CodeExecution, UrlContext, GoogleMaps toJson() test('GoogleSearch.toJson()', () { const search = GoogleSearch(); expect(search.toJson(), {}); @@ -344,5 +395,10 @@ void main() { const context = UrlContext(); expect(context.toJson(), {}); }); + + test('GoogleMaps.toJson()', () { + const maps = GoogleMaps(); + expect(maps.toJson(), {}); + }); }); } diff --git a/packages/firebase_analytics/analysis_options.yaml b/packages/firebase_analytics/analysis_options.yaml index 478fde4d9c56..f67ba3d72398 100644 --- a/packages/firebase_analytics/analysis_options.yaml +++ b/packages/firebase_analytics/analysis_options.yaml @@ -8,3 +8,4 @@ analyzer: exclude: - firebase_analytics_platform_interface/lib/src/pigeon/messages.pigeon.dart - firebase_analytics_platform_interface/test/pigeon/test_api.dart + - firebase_analytics_platform_interface/pigeons/messages.dart diff --git a/packages/firebase_analytics/firebase_analytics/CHANGELOG.md b/packages/firebase_analytics/firebase_analytics/CHANGELOG.md index 042386e13f25..ea4beafd4ce1 100644 --- a/packages/firebase_analytics/firebase_analytics/CHANGELOG.md +++ b/packages/firebase_analytics/firebase_analytics/CHANGELOG.md @@ -1,3 +1,24 @@ +## 12.4.3 + + - **FIX**(analytics,iOS): update iOS dependency instructions for IDFA-free usage ([#18337](https://github.com/firebase/flutterfire/issues/18337)). ([c21fc77b](https://github.com/firebase/flutterfire/commit/c21fc77b68a87b9691fc1615454c5dac39dd4ed4)) + +## 12.4.2 + + - Update a dependency to the latest release. + +## 12.4.1 + + - Update a dependency to the latest release. + +## 12.4.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 12.3.0 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 12.2.0 - **FIX**(analytics,iOS): Update hashedEmailAddress handling to use hex string conversion ([#18060](https://github.com/firebase/flutterfire/issues/18060)). ([80c6cff2](https://github.com/firebase/flutterfire/commit/80c6cff2836ef102c716d1e54eda8114b8ee629b)) diff --git a/packages/firebase_analytics/firebase_analytics/android/build.gradle b/packages/firebase_analytics/firebase_analytics/android/build.gradle index 183ea4605c04..189e57e43906 100755 --- a/packages/firebase_analytics/firebase_analytics/android/build.gradle +++ b/packages/firebase_analytics/firebase_analytics/android/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'com.android.library' apply from: file("local-config.gradle") buildscript { - ext.kotlin_version = "1.8.22" + ext.kotlin_version = "2.0.0" repositories { google() mavenCentral() @@ -26,9 +26,13 @@ rootProject.allprojects { apply plugin: 'com.android.library' -// AGP 9+ has built-in Kotlin support; older versions need the plugin explicitly. +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int -if (agpMajor < 9) { +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { apply plugin: 'kotlin-android' } @@ -58,12 +62,6 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - if (agpMajor < 9) { - kotlinOptions { - jvmTarget = project.ext.javaVersion - } - } - compileOptions { sourceCompatibility project.ext.javaVersion targetCompatibility project.ext.javaVersion @@ -90,4 +88,12 @@ android { } } +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } + } +} + apply from: file("./user-agent.gradle") \ No newline at end of file diff --git a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/Constants.kt b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/Constants.kt index bb544b5b274b..9ba614b48b43 100644 --- a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/Constants.kt +++ b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/Constants.kt @@ -8,7 +8,7 @@ object Constants { const val AD_STORAGE_CONSENT_GRANTED: String = "adStorageConsentGranted" const val ANALYTICS_STORAGE_CONSENT_GRANTED: String = "analyticsStorageConsentGranted" const val AD_PERSONALIZATION_SIGNALS_CONSENT_GRANTED: String = - "adPersonalizationSignalsConsentGranted" + "adPersonalizationSignalsConsentGranted" const val AD_USER_DATA_CONSENT_GRANTED: String = "adUserDataConsentGranted" const val USER_ID: String = "userId" const val EVENT_NAME: String = "eventName" diff --git a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.kt b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.kt index e70e6812fef8..cadc75c84548 100644 --- a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.kt +++ b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAnalyticsPlugin.kt @@ -22,14 +22,13 @@ import io.flutter.plugins.firebase.core.FlutterFirebasePlugin.cachedThreadPool import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry import java.util.Objects -class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, - FlutterPlugin, FirebaseAnalyticsHostApi { +class FlutterFirebaseAnalyticsPlugin : + FlutterFirebasePlugin, FlutterPlugin, FirebaseAnalyticsHostApi { private lateinit var analytics: FirebaseAnalytics private var channel: MethodChannel? = null private var messenger: BinaryMessenger? = null - private fun initInstance(messenger: BinaryMessenger, context: Context) { analytics = FirebaseAnalytics.getInstance(context) val channelName = "plugins.flutter.io/firebase_analytics" @@ -39,7 +38,9 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, this.messenger = messenger } - override fun getPluginConstantsForFirebaseApp(firebaseApp: FirebaseApp?): Task> { + override fun getPluginConstantsForFirebaseApp( + firebaseApp: FirebaseApp? + ): Task> { val taskCompletionSource = TaskCompletionSource>() cachedThreadPool.execute { @@ -86,11 +87,7 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, cachedThreadPool.execute { try { - taskCompletionSource.setResult( - Tasks.await( - analytics.sessionId - ) - ) + taskCompletionSource.setResult(Tasks.await(analytics.sessionId)) } catch (e: java.lang.Exception) { taskCompletionSource.setException(e) } @@ -104,14 +101,9 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, cachedThreadPool.execute { try { - val eventName = - Objects.requireNonNull(arguments[Constants.EVENT_NAME]) as String - val map = - arguments[Constants.PARAMETERS] as Map? - val parameterBundle: Bundle? = - createBundleFromMap( - map - ) + val eventName = Objects.requireNonNull(arguments[Constants.EVENT_NAME]) as String + val map = arguments[Constants.PARAMETERS] as Map? + val parameterBundle: Bundle? = createBundleFromMap(map) analytics.logEvent(eventName, parameterBundle) taskCompletionSource.setResult(null) } catch (e: java.lang.Exception) { @@ -202,44 +194,31 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, cachedThreadPool.execute { try { - val adStorageGranted = - arguments[Constants.AD_STORAGE_CONSENT_GRANTED] - val analyticsStorageGranted = - arguments[Constants.ANALYTICS_STORAGE_CONSENT_GRANTED] + val adStorageGranted = arguments[Constants.AD_STORAGE_CONSENT_GRANTED] + val analyticsStorageGranted = arguments[Constants.ANALYTICS_STORAGE_CONSENT_GRANTED] val adPersonalizationSignalsGranted = - arguments[Constants.AD_PERSONALIZATION_SIGNALS_CONSENT_GRANTED] - val adUserDataGranted = - arguments[Constants.AD_USER_DATA_CONSENT_GRANTED] - val parameters = - java.util.HashMap() + arguments[Constants.AD_PERSONALIZATION_SIGNALS_CONSENT_GRANTED] + val adUserDataGranted = arguments[Constants.AD_USER_DATA_CONSENT_GRANTED] + val parameters = java.util.HashMap() if (adStorageGranted != null) { - parameters[ConsentType.AD_STORAGE] = if (adStorageGranted) - ConsentStatus.GRANTED - else - ConsentStatus.DENIED + parameters[ConsentType.AD_STORAGE] = + if (adStorageGranted) ConsentStatus.GRANTED else ConsentStatus.DENIED } if (analyticsStorageGranted != null) { - parameters[ConsentType.ANALYTICS_STORAGE] = if (analyticsStorageGranted) - ConsentStatus.GRANTED - else - ConsentStatus.DENIED + parameters[ConsentType.ANALYTICS_STORAGE] = + if (analyticsStorageGranted) ConsentStatus.GRANTED else ConsentStatus.DENIED } if (adPersonalizationSignalsGranted != null) { parameters[ConsentType.AD_PERSONALIZATION] = - if (adPersonalizationSignalsGranted) - ConsentStatus.GRANTED - else - ConsentStatus.DENIED + if (adPersonalizationSignalsGranted) ConsentStatus.GRANTED else ConsentStatus.DENIED } if (adUserDataGranted != null) { - parameters[ConsentType.AD_USER_DATA] = if (adUserDataGranted) - ConsentStatus.GRANTED - else - ConsentStatus.DENIED + parameters[ConsentType.AD_USER_DATA] = + if (adUserDataGranted) ConsentStatus.GRANTED else ConsentStatus.DENIED } analytics.setConsent(parameters) @@ -257,11 +236,7 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, cachedThreadPool.execute { try { - analytics.setDefaultEventParameters( - createBundleFromMap( - parameters - ) - ) + analytics.setDefaultEventParameters(createBundleFromMap(parameters)) taskCompletionSource.setResult(null) } catch (e: java.lang.Exception) { taskCompletionSource.setException(e) @@ -276,11 +251,7 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, cachedThreadPool.execute { try { - taskCompletionSource.setResult( - Tasks.await( - analytics.appInstanceId - ) - ) + taskCompletionSource.setResult(Tasks.await(analytics.appInstanceId)) } catch (e: java.lang.Exception) { taskCompletionSource.setException(e) } @@ -299,7 +270,8 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, if (value is String) { bundle.putString(key, value) } else if (value is Int) { - // FirebaseAnalytics default event parameters only support long and double types, so we convert the int to a long. + // FirebaseAnalytics default event parameters only support long and double types, so we + // convert the int to a long. bundle.putLong(key, value.toLong()) } else if (value is Long) { bundle.putLong(key, value) @@ -318,11 +290,10 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, } else { if (item != null) { throw IllegalArgumentException( - ("Unsupported value type: " - + item.javaClass.canonicalName - + " in list at key " - + key) - ) + ("Unsupported value type: " + + item.javaClass.canonicalName + + " in list at key " + + key)) } } } @@ -331,18 +302,13 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, } else if (value is Map<*, *>) { bundle.putParcelable(key, createBundleFromMap(value as Map)) } else { - throw IllegalArgumentException( - "Unsupported value type: " + value.javaClass.canonicalName - ) + throw IllegalArgumentException("Unsupported value type: " + value.javaClass.canonicalName) } } return bundle } - private fun handleVoidTaskResult( - task: Task, - callback: (Result) -> Unit - ) { + private fun handleVoidTaskResult(task: Task, callback: (Result) -> Unit) { if (task.isSuccessful) { callback(Result.success(Unit)) } else { @@ -351,10 +317,7 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, } } - private fun handleTypedTaskResult( - task: Task, - callback: (Result) -> Unit - ) { + private fun handleTypedTaskResult(task: Task, callback: (Result) -> Unit) { if (task.isSuccessful) { callback(Result.success(task.result)) } else { @@ -364,16 +327,11 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, } override fun logEvent(event: Map, callback: (Result) -> Unit) { - handleLogEvent(event).addOnCompleteListener { task -> - handleVoidTaskResult(task, callback) - } + handleLogEvent(event).addOnCompleteListener { task -> handleVoidTaskResult(task, callback) } } - override fun setUserId(userId: String?, callback: (Result) -> Unit) { - handleSetUserId(userId).addOnCompleteListener { task -> - handleVoidTaskResult(task, callback) - } + handleSetUserId(userId).addOnCompleteListener { task -> handleVoidTaskResult(task, callback) } } override fun setUserProperty(name: String, value: String?, callback: (Result) -> Unit) { @@ -401,58 +359,41 @@ class FlutterFirebaseAnalyticsPlugin : FlutterFirebasePlugin, } override fun setConsent(consent: Map, callback: (Result) -> Unit) { - handleSetConsent(consent).addOnCompleteListener { task -> - handleVoidTaskResult(task, callback) - } - + handleSetConsent(consent).addOnCompleteListener { task -> handleVoidTaskResult(task, callback) } } override fun setDefaultEventParameters( - parameters: Map?, - callback: (Result) -> Unit + parameters: Map?, + callback: (Result) -> Unit ) { handleSetDefaultEventParameters(parameters).addOnCompleteListener { task -> handleVoidTaskResult(task, callback) } } - override fun getAppInstanceId(callback: (Result) -> Unit) { - handleGetAppInstanceId().addOnCompleteListener { task -> - handleTypedTaskResult(task, callback) - } + handleGetAppInstanceId().addOnCompleteListener { task -> handleTypedTaskResult(task, callback) } } override fun getSessionId(callback: (Result) -> Unit) { - handleGetSessionId().addOnCompleteListener { task -> - handleTypedTaskResult(task, callback) - } + handleGetSessionId().addOnCompleteListener { task -> handleTypedTaskResult(task, callback) } } override fun initiateOnDeviceConversionMeasurement( - arguments: Map, - callback: (Result) -> Unit + arguments: Map, + callback: (Result) -> Unit ) { callback( - Result.failure( - FlutterError( - "unimplemented", - "initiateOnDeviceConversionMeasurement is only available on iOS.", - null - ) - ) - ) + Result.failure( + FlutterError( + "unimplemented", + "initiateOnDeviceConversionMeasurement is only available on iOS.", + null))) } override fun logTransaction(transactionId: String, callback: (Result) -> Unit) { callback( - Result.failure( - FlutterError( - "unimplemented", - "logTransaction is only available on iOS.", - null - ) - ) - ) + Result.failure( + FlutterError("unimplemented", "logTransaction is only available on iOS.", null))) } } diff --git a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAppRegistrar.kt b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAppRegistrar.kt index 2a14d576ae89..42f5356cce24 100644 --- a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAppRegistrar.kt +++ b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/FlutterFirebaseAppRegistrar.kt @@ -9,12 +9,10 @@ import com.google.firebase.components.Component import com.google.firebase.components.ComponentRegistrar import com.google.firebase.platforminfo.LibraryVersionComponent - @Keep class FlutterFirebaseAppRegistrar : ComponentRegistrar { override fun getComponents(): List> { return listOf( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION) - ) + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION)) } } diff --git a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/GeneratedAndroidFirebaseAnalytics.g.kt b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/GeneratedAndroidFirebaseAnalytics.g.kt index fdfc88dc8f42..c556353c8f91 100644 --- a/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/GeneratedAndroidFirebaseAnalytics.g.kt +++ b/packages/firebase_analytics/firebase_analytics/android/src/main/kotlin/io/flutter/plugins/firebase/analytics/GeneratedAndroidFirebaseAnalytics.g.kt @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -10,12 +10,11 @@ package io.flutter.plugins.firebase.analytics import android.util.Log import io.flutter.plugin.common.BasicMessageChannel import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec import io.flutter.plugin.common.StandardMessageCodec import java.io.ByteArrayOutputStream import java.nio.ByteBuffer + private object GeneratedAndroidFirebaseAnalyticsPigeonUtils { fun wrapResult(result: Any?): List { @@ -24,69 +23,175 @@ private object GeneratedAndroidFirebaseAnalyticsPigeonUtils { fun wrapError(exception: Throwable): List { return if (exception is FlutterError) { - listOf( - exception.code, - exception.message, - exception.details - ) + listOf(exception.code, exception.message, exception.details) } else { listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) } } + + fun doubleEquals(a: Double, b: Double): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN()) + } + + fun floatEquals(a: Float, b: Float): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN()) + } + + fun doubleHash(d: Double): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (d == 0.0) 0.0 else d + val bits = java.lang.Double.doubleToLongBits(normalized) + return (bits xor (bits ushr 32)).toInt() + } + + fun floatHash(f: Float): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (f == 0.0f) 0.0f else f + return java.lang.Float.floatToIntBits(normalized) + } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a === b) { + return true + } + if (a == null || b == null) { + return false + } if (a is ByteArray && b is ByteArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is IntArray && b is IntArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is LongArray && b is LongArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is DoubleArray && b is DoubleArray) { - return a.contentEquals(b) + if (a.size != b.size) return false + for (i in a.indices) { + if (!doubleEquals(a[i], b[i])) return false + } + return true + } + if (a is FloatArray && b is FloatArray) { + if (a.size != b.size) return false + for (i in a.indices) { + if (!floatEquals(a[i], b[i])) return false + } + return true } if (a is Array<*> && b is Array<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + for (i in a.indices) { + if (!deepEquals(a[i], b[i])) return false + } + return true } if (a is List<*> && b is List<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + val iterA = a.iterator() + val iterB = b.iterator() + while (iterA.hasNext() && iterB.hasNext()) { + if (!deepEquals(iterA.next(), iterB.next())) return false + } + return true } if (a is Map<*, *> && b is Map<*, *>) { - return a.size == b.size && a.all { - (b as Map).containsKey(it.key) && - deepEquals(it.value, b[it.key]) + if (a.size != b.size) return false + for (entry in a) { + val key = entry.key + var found = false + for (bEntry in b) { + if (deepEquals(key, bEntry.key)) { + if (deepEquals(entry.value, bEntry.value)) { + found = true + break + } else { + return false + } + } + } + if (!found) return false } + return true + } + if (a is Double && b is Double) { + return doubleEquals(a, b) + } + if (a is Float && b is Float) { + return floatEquals(a, b) } return a == b } - + + fun deepHash(value: Any?): Int { + return when (value) { + null -> 0 + is ByteArray -> value.contentHashCode() + is IntArray -> value.contentHashCode() + is LongArray -> value.contentHashCode() + is DoubleArray -> { + var result = 1 + for (item in value) { + result = 31 * result + doubleHash(item) + } + result + } + is FloatArray -> { + var result = 1 + for (item in value) { + result = 31 * result + floatHash(item) + } + result + } + is Array<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is List<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is Map<*, *> -> { + var result = 0 + for (entry in value) { + result += ((deepHash(entry.key) * 31) xor deepHash(entry.value)) + } + result + } + is Double -> doubleHash(value) + is Float -> floatHash(value) + else -> value.hashCode() + } + } } /** * 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 FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() /** Generated class from Pigeon that represents data sent in messages. */ -data class AnalyticsEvent ( - val name: String, - val parameters: Map? = null -) - { +data class AnalyticsEvent(val name: String, val parameters: Map? = null) { companion object { fun fromList(pigeonVar_list: List): AnalyticsEvent { val name = pigeonVar_list[0] as String @@ -94,35 +199,45 @@ data class AnalyticsEvent ( return AnalyticsEvent(name, parameters) } } + fun toList(): List { return listOf( - name, - parameters, + name, + parameters, ) } + override fun equals(other: Any?): Boolean { - if (other !is AnalyticsEvent) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseAnalyticsPigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as AnalyticsEvent + return GeneratedAndroidFirebaseAnalyticsPigeonUtils.deepEquals(this.name, other.name) && + GeneratedAndroidFirebaseAnalyticsPigeonUtils.deepEquals(this.parameters, other.parameters) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseAnalyticsPigeonUtils.deepHash(this.name) + result = 31 * result + GeneratedAndroidFirebaseAnalyticsPigeonUtils.deepHash(this.parameters) + return result + } } + private open class GeneratedAndroidFirebaseAnalyticsPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - AnalyticsEvent.fromList(it) - } + return (readValue(buffer) as? List)?.let { AnalyticsEvent.fromList(it) } } else -> super.readValueOfType(type, buffer) } } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { is AnalyticsEvent -> { stream.write(129) @@ -133,33 +248,56 @@ private open class GeneratedAndroidFirebaseAnalyticsPigeonCodec : StandardMessag } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface FirebaseAnalyticsHostApi { fun logEvent(event: Map, callback: (Result) -> Unit) + fun setUserId(userId: String?, callback: (Result) -> Unit) + fun setUserProperty(name: String, value: String?, callback: (Result) -> Unit) + fun setAnalyticsCollectionEnabled(enabled: Boolean, callback: (Result) -> Unit) + fun resetAnalyticsData(callback: (Result) -> Unit) + fun setSessionTimeoutDuration(timeout: Long, callback: (Result) -> Unit) + fun setConsent(consent: Map, callback: (Result) -> Unit) + fun setDefaultEventParameters(parameters: Map?, callback: (Result) -> Unit) + fun getAppInstanceId(callback: (Result) -> Unit) + fun getSessionId(callback: (Result) -> Unit) - fun initiateOnDeviceConversionMeasurement(arguments: Map, callback: (Result) -> Unit) + + fun initiateOnDeviceConversionMeasurement( + arguments: Map, + callback: (Result) -> Unit + ) + fun logTransaction(transactionId: String, callback: (Result) -> Unit) companion object { /** The codec used by FirebaseAnalyticsHostApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidFirebaseAnalyticsPigeonCodec() - } - /** Sets up an instance of `FirebaseAnalyticsHostApi` to handle messages through the `binaryMessenger`. */ + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseAnalyticsPigeonCodec() } + /** + * Sets up an instance of `FirebaseAnalyticsHostApi` to handle messages through the + * `binaryMessenger`. + */ @JvmOverloads - fun setUp(binaryMessenger: BinaryMessenger, api: FirebaseAnalyticsHostApi?, messageChannelSuffix: String = "") { - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebaseAnalyticsHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -178,7 +316,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -197,7 +339,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -217,7 +363,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -236,10 +386,14 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.resetAnalyticsData$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.resetAnalyticsData$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.resetAnalyticsData{ result: Result -> + api.resetAnalyticsData { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebaseAnalyticsPigeonUtils.wrapError(error)) @@ -253,7 +407,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -272,7 +430,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -291,7 +453,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -310,10 +476,14 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getAppInstanceId$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getAppInstanceId$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.getAppInstanceId{ result: Result -> + api.getAppInstanceId { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebaseAnalyticsPigeonUtils.wrapError(error)) @@ -328,10 +498,14 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getSessionId$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getSessionId$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.getSessionId{ result: Result -> + api.getSessionId { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebaseAnalyticsPigeonUtils.wrapError(error)) @@ -346,7 +520,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -365,7 +543,11 @@ interface FirebaseAnalyticsHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List diff --git a/packages/firebase_analytics/firebase_analytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/analytics/example/MainActivity.kt b/packages/firebase_analytics/firebase_analytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/analytics/example/MainActivity.kt index 807a758cd9cc..00752be819a0 100644 --- a/packages/firebase_analytics/firebase_analytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/analytics/example/MainActivity.kt +++ b/packages/firebase_analytics/firebase_analytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/analytics/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.analytics.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_analytics/firebase_analytics/example/pubspec.yaml b/packages/firebase_analytics/firebase_analytics/example/pubspec.yaml index fd27b92c5700..9a7913f98582 100755 --- a/packages/firebase_analytics/firebase_analytics/example/pubspec.yaml +++ b/packages/firebase_analytics/firebase_analytics/example/pubspec.yaml @@ -1,13 +1,14 @@ name: firebase_analytics_example description: Demonstrates how to use the firebase_analytics plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_analytics: ^12.2.0 - firebase_core: ^4.6.0 + firebase_analytics: ^12.4.3 + firebase_core: ^4.11.0 flutter: sdk: flutter in_app_purchase: ^3.2.3 diff --git a/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Package.swift b/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Package.swift index 22d9bd5c030f..65aee5313d06 100644 --- a/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Package.swift +++ b/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Package.swift @@ -8,23 +8,23 @@ import Foundation import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" -// Set FIREBASE_ANALYTICS_WITHOUT_ADID=true to use FirebaseAnalyticsWithoutAdIdSupport +// Set FIREBASE_ANALYTICS_WITHOUT_ADID=true to use FirebaseAnalyticsCore. // e.g. FIREBASE_ANALYTICS_WITHOUT_ADID=true flutter build ios let useWithoutAdId = ProcessInfo.processInfo.environment["FIREBASE_ANALYTICS_WITHOUT_ADID"] != nil -let analyticsProduct = useWithoutAdId ? "FirebaseAnalyticsWithoutAdIdSupport" : "FirebaseAnalytics" +let analyticsProduct = useWithoutAdId ? "FirebaseAnalyticsCore" : "FirebaseAnalytics" let package = Package( name: "firebase_analytics", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-analytics", targets: ["firebase_analytics"]), + .library(name: "firebase-analytics", targets: ["firebase_analytics"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -35,8 +35,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsMessages.g.swift b/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsMessages.g.swift index d8b175ba6e20..c3a557049cc0 100644 --- a/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsMessages.g.swift +++ b/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsMessages.g.swift @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -52,7 +52,7 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } @@ -66,6 +66,19 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +private func doubleEqualsFirebaseAnalyticsMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirebaseAnalyticsMessages(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8_0000_0000_0000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } +} + func deepEqualsFirebaseAnalyticsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? @@ -76,59 +89,90 @@ func deepEqualsFirebaseAnalyticsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { case (nil, _), (_, nil): return false - case is (Void, Void): + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: return true - case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): - return cleanLhsHashable == cleanRhsHashable + case is (Void, Void): + return true - case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): - guard cleanLhsArray.count == cleanRhsArray.count else { return false } - for (index, element) in cleanLhsArray.enumerated() { - if !deepEqualsFirebaseAnalyticsMessages(element, cleanRhsArray[index]) { + case let (lhsArray, rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsFirebaseAnalyticsMessages(element, rhsArray[index]) { return false } } return true - case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): - guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } - for (key, cleanLhsValue) in cleanLhsDictionary { - guard cleanRhsDictionary.index(forKey: key) != nil else { return false } - if !deepEqualsFirebaseAnalyticsMessages(cleanLhsValue, cleanRhsDictionary[key]!) { + case let (lhsArray, rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsFirebaseAnalyticsMessages(element, rhsArray[index]) { return false } } return true + case let (lhsDictionary, rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEqualsFirebaseAnalyticsMessages(lhsKey, rhsKey) { + if deepEqualsFirebaseAnalyticsMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirebaseAnalyticsMessages(lhs, rhs) + + case let (lhsHashable, rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + default: - // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be - // untrue. return false } } func deepHashFirebaseAnalyticsMessages(value: Any?, hasher: inout Hasher) { - if let valueList = value as? [AnyHashable] { - for item in valueList { - deepHashFirebaseAnalyticsMessages(value: item, hasher: &hasher) - } - return - } - - if let valueDict = value as? [AnyHashable: AnyHashable] { - for key in valueDict.keys { - hasher.combine(key) - deepHashFirebaseAnalyticsMessages(value: valueDict[key]!, hasher: &hasher) + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirebaseAnalyticsMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirebaseAnalyticsMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirebaseAnalyticsMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirebaseAnalyticsMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirebaseAnalyticsMessages(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) } - return - } - - if let hashableValue = value as? AnyHashable { - hasher.combine(hashableValue.hashValue) + } else { + hasher.combine(0) } - - return hasher.combine(String(describing: value)) } /// Generated class from Pigeon that represents data sent in messages. @@ -155,11 +199,20 @@ struct AnalyticsEvent: Hashable { } static func == (lhs: AnalyticsEvent, rhs: AnalyticsEvent) -> Bool { - deepEqualsFirebaseAnalyticsMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseAnalyticsMessages(lhs.name, rhs.name) + && deepEqualsFirebaseAnalyticsMessages( + lhs.parameters, + rhs.parameters + ) } func hash(into hasher: inout Hasher) { - deepHashFirebaseAnalyticsMessages(value: toList(), hasher: &hasher) + hasher.combine("AnalyticsEvent") + deepHashFirebaseAnalyticsMessages(value: name, hasher: &hasher) + deepHashFirebaseAnalyticsMessages(value: parameters, hasher: &hasher) } } @@ -206,20 +259,25 @@ class FirebaseAnalyticsMessagesPigeonCodec: FlutterStandardMessageCodec, @unchec protocol FirebaseAnalyticsHostApi { func logEvent(event: [String: Any?], completion: @escaping (Result) -> Void) func setUserId(userId: String?, completion: @escaping (Result) -> Void) - func setUserProperty(name: String, value: String?, - completion: @escaping (Result) -> Void) - func setAnalyticsCollectionEnabled(enabled: Bool, - completion: @escaping (Result) -> Void) + func setUserProperty( + name: String, value: String?, + completion: @escaping (Result) -> Void) + func setAnalyticsCollectionEnabled( + enabled: Bool, + completion: @escaping (Result) -> Void) func resetAnalyticsData(completion: @escaping (Result) -> Void) - func setSessionTimeoutDuration(timeout: Int64, - completion: @escaping (Result) -> Void) + func setSessionTimeoutDuration( + timeout: Int64, + completion: @escaping (Result) -> Void) func setConsent(consent: [String: Bool?], completion: @escaping (Result) -> Void) - func setDefaultEventParameters(parameters: [String: Any?]?, - completion: @escaping (Result) -> Void) + func setDefaultEventParameters( + parameters: [String: Any?]?, + completion: @escaping (Result) -> Void) func getAppInstanceId(completion: @escaping (Result) -> Void) func getSessionId(completion: @escaping (Result) -> Void) - func initiateOnDeviceConversionMeasurement(arguments: [String: String?], - completion: @escaping (Result) -> Void) + func initiateOnDeviceConversionMeasurement( + arguments: [String: String?], + completion: @escaping (Result) -> Void) func logTransaction(transactionId: String, completion: @escaping (Result) -> Void) } @@ -231,11 +289,14 @@ class FirebaseAnalyticsHostApiSetup { /// Sets up an instance of `FirebaseAnalyticsHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseAnalyticsHostApi?, - messageChannelSuffix: String = "") { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseAnalyticsHostApi?, + messageChannelSuffix: String = "" + ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let logEventChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -247,7 +308,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -256,7 +317,8 @@ class FirebaseAnalyticsHostApiSetup { logEventChannel.setMessageHandler(nil) } let setUserIdChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -268,7 +330,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -277,7 +339,8 @@ class FirebaseAnalyticsHostApiSetup { setUserIdChannel.setMessageHandler(nil) } let setUserPropertyChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -290,7 +353,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -299,7 +362,8 @@ class FirebaseAnalyticsHostApiSetup { setUserPropertyChannel.setMessageHandler(nil) } let setAnalyticsCollectionEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -311,7 +375,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -320,7 +384,8 @@ class FirebaseAnalyticsHostApiSetup { setAnalyticsCollectionEnabledChannel.setMessageHandler(nil) } let resetAnalyticsDataChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.resetAnalyticsData\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.resetAnalyticsData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -330,7 +395,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -339,7 +404,8 @@ class FirebaseAnalyticsHostApiSetup { resetAnalyticsDataChannel.setMessageHandler(nil) } let setSessionTimeoutDurationChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -351,7 +417,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -360,7 +426,8 @@ class FirebaseAnalyticsHostApiSetup { setSessionTimeoutDurationChannel.setMessageHandler(nil) } let setConsentChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -372,7 +439,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -381,7 +448,8 @@ class FirebaseAnalyticsHostApiSetup { setConsentChannel.setMessageHandler(nil) } let setDefaultEventParametersChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -393,7 +461,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -402,7 +470,8 @@ class FirebaseAnalyticsHostApiSetup { setDefaultEventParametersChannel.setMessageHandler(nil) } let getAppInstanceIdChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getAppInstanceId\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getAppInstanceId\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -410,9 +479,9 @@ class FirebaseAnalyticsHostApiSetup { getAppInstanceIdChannel.setMessageHandler { _, reply in api.getAppInstanceId { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -421,7 +490,8 @@ class FirebaseAnalyticsHostApiSetup { getAppInstanceIdChannel.setMessageHandler(nil) } let getSessionIdChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getSessionId\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getSessionId\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -429,9 +499,9 @@ class FirebaseAnalyticsHostApiSetup { getSessionIdChannel.setMessageHandler { _, reply in api.getSessionId { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -440,7 +510,8 @@ class FirebaseAnalyticsHostApiSetup { getSessionIdChannel.setMessageHandler(nil) } let initiateOnDeviceConversionMeasurementChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -452,7 +523,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -461,7 +532,8 @@ class FirebaseAnalyticsHostApiSetup { initiateOnDeviceConversionMeasurementChannel.setMessageHandler(nil) } let logTransactionChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -473,7 +545,7 @@ class FirebaseAnalyticsHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } diff --git a/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsPlugin.swift b/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsPlugin.swift index 41651b2ace45..96b5921f88b0 100644 --- a/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsPlugin.swift +++ b/packages/firebase_analytics/firebase_analytics/ios/firebase_analytics/Sources/firebase_analytics/FirebaseAnalyticsPlugin.swift @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebaseAnalytics +import StoreKit + #if canImport(FlutterMacOS) import FlutterMacOS #else @@ -13,8 +16,6 @@ #else import firebase_core_shared #endif -import FirebaseAnalytics -import StoreKit let kFLTFirebaseAnalyticsName = "name" let kFLTFirebaseAnalyticsValue = "value" @@ -27,12 +28,14 @@ let kFLTFirebaseAdPersonalizationSignalsConsentGranted = "adPersonalizationSigna let kFLTFirebaseAdUserDataConsentGranted = "adUserDataConsentGranted" let kFLTFirebaseAnalyticsUserId = "userId" +// swift-format-ignore: AlwaysUseLowerCamelCase let FLTFirebaseAnalyticsChannelName = "plugins.flutter.io/firebase_analytics" extension FlutterError: Error {} public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, FlutterPlugin, - FirebaseAnalyticsHostApi { + FirebaseAnalyticsHostApi +{ public static func register(with registrar: any FlutterPluginRegistrar) { let binaryMessenger: FlutterBinaryMessenger @@ -61,14 +64,18 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt completion(.success(())) } - func setUserProperty(name: String, value: String?, - completion: @escaping (Result) -> Void) { + func setUserProperty( + name: String, value: String?, + completion: @escaping (Result) -> Void + ) { Analytics.setUserProperty(value, forName: name) completion(.success(())) } - func setAnalyticsCollectionEnabled(enabled: Bool, - completion: @escaping (Result) -> Void) { + func setAnalyticsCollectionEnabled( + enabled: Bool, + completion: @escaping (Result) -> Void + ) { Analytics.setAnalyticsCollectionEnabled(enabled) completion(.success(())) } @@ -78,14 +85,18 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt completion(.success(())) } - func setSessionTimeoutDuration(timeout: Int64, - completion: @escaping (Result) -> Void) { + func setSessionTimeoutDuration( + timeout: Int64, + completion: @escaping (Result) -> Void + ) { Analytics.setSessionTimeoutInterval(TimeInterval(timeout)) completion(.success(())) } - func setConsent(consent: [String: Bool?], - completion: @escaping (Result) -> Void) { + func setConsent( + consent: [String: Bool?], + completion: @escaping (Result) -> Void + ) { var parameters: [ConsentType: ConsentStatus] = [:] if let adStorage = consent[kFLTFirebaseAnalyticsAdStorageConsentGranted] as? Bool { parameters[.adStorage] = adStorage ? .granted : .denied @@ -94,7 +105,8 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt parameters[.analyticsStorage] = analyticsStorage ? .granted : .denied } if let adPersonalization = - consent[kFLTFirebaseAdPersonalizationSignalsConsentGranted] as? Bool { + consent[kFLTFirebaseAdPersonalizationSignalsConsentGranted] as? Bool + { parameters[.adPersonalization] = adPersonalization ? .granted : .denied } if let adUserData = consent[kFLTFirebaseAdUserDataConsentGranted] as? Bool { @@ -104,8 +116,10 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt completion(.success(())) } - func setDefaultEventParameters(parameters: [String: Any?]?, - completion: @escaping (Result) -> Void) { + func setDefaultEventParameters( + parameters: [String: Any?]?, + completion: @escaping (Result) -> Void + ) { Analytics.setDefaultEventParameters(parameters) completion(.success(())) } @@ -125,9 +139,12 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt } } - func initiateOnDeviceConversionMeasurement(arguments: [String: String?], - completion: @escaping (Result) - -> Void) { + func initiateOnDeviceConversionMeasurement( + arguments: [String: String?], + completion: + @escaping (Result) + -> Void + ) { if let emailAddress = arguments["emailAddress"] as? String { Analytics.initiateOnDeviceConversionMeasurement(emailAddress: emailAddress) } @@ -135,37 +152,49 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt Analytics.initiateOnDeviceConversionMeasurement(phoneNumber: phoneNumber) } if let hashedEmailAddress = arguments["hashedEmailAddress"] as? String, - let data = hexStringToData(hashedEmailAddress) { + let data = hexStringToData(hashedEmailAddress) + { Analytics.initiateOnDeviceConversionMeasurement(hashedEmailAddress: data) } if let hashedPhoneNumber = arguments["hashedPhoneNumber"] as? String, - let data = hexStringToData(hashedPhoneNumber) { + let data = hexStringToData(hashedPhoneNumber) + { Analytics.initiateOnDeviceConversionMeasurement(hashedPhoneNumber: data) } completion(.success(())) } - func logTransaction(transactionId: String, - completion: @escaping (Result) -> Void) { + func logTransaction( + transactionId: String, + completion: @escaping (Result) -> Void + ) { #if os(macOS) if #available(macOS 12.0, *) { logTransactionWithStoreKit(transactionId: transactionId, completion: completion) } else { - completion(.failure(FlutterError( - code: "firebase_analytics", - message: "logTransaction() is only supported on macOS 12.0 or newer", - details: nil - ))) + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "logTransaction() is only supported on macOS 12.0 or newer", + details: nil + ) + ) + ) } #else if #available(iOS 15.0, *) { logTransactionWithStoreKit(transactionId: transactionId, completion: completion) } else { - completion(.failure(FlutterError( - code: "firebase_analytics", - message: "logTransaction() is only supported on iOS 15.0 or newer", - details: nil - ))) + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "logTransaction() is only supported on iOS 15.0 or newer", + details: nil + ) + ) + ) } #endif } @@ -175,23 +204,29 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt #else @available(iOS 15.0, *) #endif - private func logTransactionWithStoreKit(transactionId: String, - completion: @escaping (Result) -> Void) { + private func logTransactionWithStoreKit( + transactionId: String, + completion: @escaping (Result) -> Void + ) { Task { do { guard let id = UInt64(transactionId) else { - completion(.failure(FlutterError( - code: "firebase_analytics", - message: "Invalid transactionId", - details: nil - ))) + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "Invalid transactionId", + details: nil + ) + ) + ) return } var foundTransaction: Transaction? for await result in Transaction.all { switch result { - case let .verified(transaction): + case .verified(let transaction): if transaction.id == id { foundTransaction = transaction break @@ -202,11 +237,15 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt } guard let transaction = foundTransaction else { - completion(.failure(FlutterError( - code: "firebase_analytics", - message: "Transaction not found", - details: nil - ))) + completion( + .failure( + FlutterError( + code: "firebase_analytics", + message: "Transaction not found", + details: nil + ) + ) + ) return } @@ -225,9 +264,9 @@ public class FirebaseAnalyticsPlugin: NSObject, FLTFirebasePluginProtocol, Flutt var data = Data(capacity: length / 2) var index = hexString.startIndex - for _ in 0 ..< (length / 2) { + for _ in 0..<(length / 2) { let nextIndex = hexString.index(index, offsetBy: 2) - guard let byte = UInt8(hexString[index ..< nextIndex], radix: 16) else { + guard let byte = UInt8(hexString[index..=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_analytics_platform_interface: ^5.1.0 - firebase_analytics_web: ^0.6.1+4 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_analytics_platform_interface: ^6.0.3 + firebase_analytics_web: ^0.6.1+9 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter diff --git a/packages/firebase_analytics/firebase_analytics/windows/messages.g.cpp b/packages/firebase_analytics/firebase_analytics/windows/messages.g.cpp index 14a92edf1452..8da8a92f2b39 100644 --- a/packages/firebase_analytics/firebase_analytics/windows/messages.g.cpp +++ b/packages/firebase_analytics/firebase_analytics/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace firebase_analytics_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,6 +33,212 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace // AnalyticsEvent AnalyticsEvent::AnalyticsEvent(const std::string& name) : name_(name) {} @@ -75,22 +283,40 @@ AnalyticsEvent AnalyticsEvent::FromEncodableList(const EncodableList& list) { return decoded; } +bool AnalyticsEvent::operator==(const AnalyticsEvent& other) const { + return PigeonInternalDeepEquals(name_, other.name_) && + PigeonInternalDeepEquals(parameters_, other.parameters_); +} + +bool AnalyticsEvent::operator!=(const AnalyticsEvent& other) const { + return !(*this == other); +} + +size_t AnalyticsEvent::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(name_); + result = result * 31 + PigeonInternalDeepHash(parameters_); + return result; +} + +size_t PigeonInternalDeepHash(const AnalyticsEvent& v) { return v.Hash(); } + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { case 129: { return CustomEncodableValue(AnalyticsEvent::FromEncodableList( std::get(ReadValue(stream)))); } default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { if (custom_value->type() == typeid(AnalyticsEvent)) { @@ -102,24 +328,25 @@ void PigeonInternalCodecSerializer::WriteValue( return; } } - flutter::StandardCodecSerializer::WriteValue(value, stream); + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebaseAnalyticsHostApi. -const flutter::StandardMessageCodec& FirebaseAnalyticsHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebaseAnalyticsHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseAnalyticsHostApi` to handle messages through // the `binary_messenger`. -void FirebaseAnalyticsHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseAnalyticsHostApi* api) { +void FirebaseAnalyticsHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseAnalyticsHostApi* api) { FirebaseAnalyticsHostApi::SetUp(binary_messenger, api, ""); } void FirebaseAnalyticsHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, FirebaseAnalyticsHostApi* api, + ::flutter::BinaryMessenger* binary_messenger, FirebaseAnalyticsHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = message_channel_suffix.length() > 0 @@ -135,7 +362,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_event_arg = args.at(0); @@ -173,7 +400,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_user_id_arg = args.at(0); @@ -207,7 +434,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_name_arg = args.at(0); @@ -248,7 +475,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_enabled_arg = args.at(0); @@ -285,7 +512,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { api->ResetAnalyticsData( [reply](std::optional&& output) { @@ -315,7 +542,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_timeout_arg = args.at(0); @@ -352,7 +579,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_consent_arg = args.at(0); @@ -390,7 +617,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_parameters_arg = args.at(0); @@ -425,7 +652,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { api->GetAppInstanceId( [reply](ErrorOr>&& output) { @@ -461,7 +688,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { api->GetSessionId( [reply](ErrorOr>&& output) { @@ -497,7 +724,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_arguments_arg = args.at(0); @@ -535,7 +762,7 @@ void FirebaseAnalyticsHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_transaction_id_arg = args.at(0); diff --git a/packages/firebase_analytics/firebase_analytics/windows/messages.g.h b/packages/firebase_analytics/firebase_analytics/windows/messages.g.h index b755d9ef6045..7c22489d9c28 100644 --- a/packages/firebase_analytics/firebase_analytics/windows/messages.g.h +++ b/packages/firebase_analytics/firebase_analytics/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -66,25 +66,38 @@ class AnalyticsEvent { // Constructs an object setting all fields. explicit AnalyticsEvent(const std::string& name, - const flutter::EncodableMap* parameters); + const ::flutter::EncodableMap* parameters); const std::string& name() const; void set_name(std::string_view value_arg); - const flutter::EncodableMap* parameters() const; - void set_parameters(const flutter::EncodableMap* value_arg); - void set_parameters(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* parameters() const; + void set_parameters(const ::flutter::EncodableMap* value_arg); + void set_parameters(const ::flutter::EncodableMap& value_arg); + bool operator==(const AnalyticsEvent& other) const; + bool operator!=(const AnalyticsEvent& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static AnalyticsEvent FromEncodableList(const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static AnalyticsEvent FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAnalyticsHostApi; friend class PigeonInternalCodecSerializer; std::string name_; - std::optional parameters_; + std::optional<::flutter::EncodableMap> parameters_; }; -class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -92,12 +105,12 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -108,7 +121,7 @@ class FirebaseAnalyticsHostApi { FirebaseAnalyticsHostApi& operator=(const FirebaseAnalyticsHostApi&) = delete; virtual ~FirebaseAnalyticsHostApi() {} virtual void LogEvent( - const flutter::EncodableMap& event, + const ::flutter::EncodableMap& event, std::function reply)> result) = 0; virtual void SetUserId( const std::string* user_id, @@ -125,10 +138,10 @@ class FirebaseAnalyticsHostApi { int64_t timeout, std::function reply)> result) = 0; virtual void SetConsent( - const flutter::EncodableMap& consent, + const ::flutter::EncodableMap& consent, std::function reply)> result) = 0; virtual void SetDefaultEventParameters( - const flutter::EncodableMap* parameters, + const ::flutter::EncodableMap* parameters, std::function reply)> result) = 0; virtual void GetAppInstanceId( std::function> reply)> @@ -136,23 +149,23 @@ class FirebaseAnalyticsHostApi { virtual void GetSessionId( std::function> reply)> result) = 0; virtual void InitiateOnDeviceConversionMeasurement( - const flutter::EncodableMap& arguments, + const ::flutter::EncodableMap& arguments, std::function reply)> result) = 0; virtual void LogTransaction( const std::string& transaction_id, std::function reply)> result) = 0; // The codec used by FirebaseAnalyticsHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseAnalyticsHostApi` to handle messages // through the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAnalyticsHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAnalyticsHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseAnalyticsHostApi() = default; diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/CHANGELOG.md b/packages/firebase_analytics/firebase_analytics_platform_interface/CHANGELOG.md index a248363ff5e0..5a7a0c8acac8 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/CHANGELOG.md +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/CHANGELOG.md @@ -1,3 +1,26 @@ +## 6.0.3 + + - Update a dependency to the latest release. + +## 6.0.2 + + - Update a dependency to the latest release. + +## 6.0.1 + + - Update a dependency to the latest release. + +## 6.0.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 5.1.1 + + - Update a dependency to the latest release. + ## 5.1.0 - **FEAT**(analytics,iOS): add support for `logTransaction` ([#17995](https://github.com/firebase/flutterfire/issues/17995)). ([103d7ffa](https://github.com/firebase/flutterfire/commit/103d7ffa9343c654ec23c782a802b929dbf37d01)) diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/pigeon/messages.pigeon.dart index 003dd773639e..b61f5de91985 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,21 +1,40 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.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, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } List wrapResponse( @@ -30,20 +49,67 @@ List wrapResponse( } bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } if (a is List && b is List) { return a.length == b.length && a.indexed .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; } return a == b; } +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + class AnalyticsEvent { AnalyticsEvent({ required this.name, @@ -83,12 +149,13 @@ class AnalyticsEvent { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(name, other.name) && + _deepEquals(parameters, other.parameters); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class _PigeonCodec extends StandardMessageCodec { @@ -133,313 +200,231 @@ class FirebaseAnalyticsHostApi { final String pigeonVar_messageChannelSuffix; Future logEvent(Map event) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([event]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setUserId(String? userId) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([userId]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setUserProperty(String name, String? value) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([name, value]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setAnalyticsCollectionEnabled(bool enabled) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([enabled]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future resetAnalyticsData() async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.resetAnalyticsData$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setSessionTimeoutDuration(int timeout) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([timeout]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setConsent(Map consent) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([consent]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setDefaultEventParameters( Map? parameters) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([parameters]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future getAppInstanceId() async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getAppInstanceId$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 (pigeonVar_replyList[0] as String?); - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + return pigeonVar_replyValue as String?; } Future getSessionId() async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getSessionId$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 (pigeonVar_replyList[0] as int?); - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + return pigeonVar_replyValue as int?; } Future initiateOnDeviceConversionMeasurement( Map arguments) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([arguments]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future logTransaction(String transactionId) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([transactionId]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/pubspec.yaml b/packages/firebase_analytics/firebase_analytics_platform_interface/pubspec.yaml index 91b7279d0fb6..e4e6b4346487 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/pubspec.yaml +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/pubspec.yaml @@ -2,22 +2,23 @@ name: firebase_analytics_platform_interface description: A common platform interface for the firebase_analytics plugin. homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_analytics/firebase_analytics_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_analytics/firebase_analytics_platform_interface -version: 5.1.0 +version: 6.0.3 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter - pigeon: 25.3.2 + pigeon: 26.3.4 diff --git a/packages/firebase_analytics/firebase_analytics_platform_interface/test/pigeon/test_api.dart b/packages/firebase_analytics/firebase_analytics_platform_interface/test/pigeon/test_api.dart index 591cce9f19e2..830f97b9696d 100644 --- a/packages/firebase_analytics/firebase_analytics_platform_interface/test/pigeon/test_api.dart +++ b/packages/firebase_analytics/firebase_analytics_platform_interface/test/pigeon/test_api.dart @@ -1,9 +1,9 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers +// 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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -77,9 +77,7 @@ abstract class TestFirebaseAnalyticsHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -90,15 +88,11 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent was null.'); - final List args = (message as List?)!; - final Map? arg_event = - (args[0] as Map?)?.cast(); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logEvent was null, expected non-null Map.'); + final List args = message! as List; + final Map arg_event = + (args[0]! as Map).cast(); try { - await api.logEvent(arg_event!); + await api.logEvent(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -110,9 +104,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -123,10 +115,8 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserId was null.'); - final List args = (message as List?)!; - final String? arg_userId = (args[0] as String?); + final List args = message! as List; + final String? arg_userId = args[0] as String?; try { await api.setUserId(arg_userId); return wrapResponse(empty: true); @@ -140,9 +130,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -153,15 +141,11 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty was null.'); - final List args = (message as List?)!; - final String? arg_name = (args[0] as String?); - assert(arg_name != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setUserProperty was null, expected non-null String.'); - final String? arg_value = (args[1] as String?); + final List args = message! as List; + final String arg_name = args[0]! as String; + final String? arg_value = args[1] as String?; try { - await api.setUserProperty(arg_name!, arg_value); + await api.setUserProperty(arg_name, arg_value); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -173,9 +157,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -186,14 +168,10 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled was null.'); - final List args = (message as List?)!; - final bool? arg_enabled = (args[0] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setAnalyticsCollectionEnabled was null, expected non-null bool.'); + final List args = message! as List; + final bool arg_enabled = args[0]! as bool; try { - await api.setAnalyticsCollectionEnabled(arg_enabled!); + await api.setAnalyticsCollectionEnabled(arg_enabled); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -205,9 +183,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.resetAnalyticsData$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -231,9 +207,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -244,14 +218,10 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration was null.'); - final List args = (message as List?)!; - final int? arg_timeout = (args[0] as int?); - assert(arg_timeout != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setSessionTimeoutDuration was null, expected non-null int.'); + final List args = message! as List; + final int arg_timeout = args[0]! as int; try { - await api.setSessionTimeoutDuration(arg_timeout!); + await api.setSessionTimeoutDuration(arg_timeout); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -263,9 +233,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -276,15 +244,11 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent was null.'); - final List args = (message as List?)!; - final Map? arg_consent = - (args[0] as Map?)?.cast(); - assert(arg_consent != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setConsent was null, expected non-null Map.'); + final List args = message! as List; + final Map arg_consent = + (args[0]! as Map).cast(); try { - await api.setConsent(arg_consent!); + await api.setConsent(arg_consent); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -296,9 +260,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -309,9 +271,7 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.setDefaultEventParameters was null.'); - final List args = (message as List?)!; + final List args = message! as List; final Map? arg_parameters = (args[0] as Map?)?.cast(); try { @@ -327,9 +287,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getAppInstanceId$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -353,9 +311,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.getSessionId$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -379,9 +335,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -392,15 +346,11 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement was null.'); - final List args = (message as List?)!; - final Map? arg_arguments = - (args[0] as Map?)?.cast(); - assert(arg_arguments != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.initiateOnDeviceConversionMeasurement was null, expected non-null Map.'); + final List args = message! as List; + final Map arg_arguments = + (args[0]! as Map).cast(); try { - await api.initiateOnDeviceConversionMeasurement(arg_arguments!); + await api.initiateOnDeviceConversionMeasurement(arg_arguments); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -412,9 +362,7 @@ abstract class TestFirebaseAnalyticsHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -425,14 +373,10 @@ abstract class TestFirebaseAnalyticsHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction was null.'); - final List args = (message as List?)!; - final String? arg_transactionId = (args[0] as String?); - assert(arg_transactionId != null, - 'Argument for dev.flutter.pigeon.firebase_analytics_platform_interface.FirebaseAnalyticsHostApi.logTransaction was null, expected non-null String.'); + final List args = message! as List; + final String arg_transactionId = args[0]! as String; try { - await api.logTransaction(arg_transactionId!); + await api.logTransaction(arg_transactionId); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/firebase_analytics/firebase_analytics_web/CHANGELOG.md b/packages/firebase_analytics/firebase_analytics_web/CHANGELOG.md index e1cda268ba44..1eff940b91ce 100644 --- a/packages/firebase_analytics/firebase_analytics_web/CHANGELOG.md +++ b/packages/firebase_analytics/firebase_analytics_web/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.6.1+9 + + - Update a dependency to the latest release. + +## 0.6.1+8 + + - Update a dependency to the latest release. + +## 0.6.1+7 + + - Update a dependency to the latest release. + +## 0.6.1+6 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.6.1+5 + + - Update a dependency to the latest release. + ## 0.6.1+4 - Update a dependency to the latest release. diff --git a/packages/firebase_analytics/firebase_analytics_web/lib/src/firebase_analytics_version.dart b/packages/firebase_analytics/firebase_analytics_web/lib/src/firebase_analytics_version.dart index 26c81d959d07..5ae950bfa0b5 100644 --- a/packages/firebase_analytics/firebase_analytics_web/lib/src/firebase_analytics_version.dart +++ b/packages/firebase_analytics/firebase_analytics_web/lib/src/firebase_analytics_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '12.2.0'; +const packageVersion = '12.4.3'; diff --git a/packages/firebase_analytics/firebase_analytics_web/pubspec.yaml b/packages/firebase_analytics/firebase_analytics_web/pubspec.yaml index 583b3cc365a0..fb7a006cfb7b 100644 --- a/packages/firebase_analytics/firebase_analytics_web/pubspec.yaml +++ b/packages/firebase_analytics/firebase_analytics_web/pubspec.yaml @@ -2,17 +2,18 @@ name: firebase_analytics_web description: The web implementation of firebase_analytics homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_analytics/firebase_analytics_web repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_analytics/firebase_analytics_web -version: 0.6.1+4 +version: 0.6.1+9 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_analytics_platform_interface: ^5.1.0 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 + _flutterfire_internals: ^1.3.73 + firebase_analytics_platform_interface: ^6.0.3 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/firebase_app_check/analysis_options.yaml b/packages/firebase_app_check/analysis_options.yaml new file mode 100644 index 000000000000..0a5c260b3115 --- /dev/null +++ b/packages/firebase_app_check/analysis_options.yaml @@ -0,0 +1,10 @@ +# Copyright 2026 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# in the LICENSE file. + +include: ../../analysis_options.yaml + +analyzer: + exclude: + - firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart + - firebase_app_check_platform_interface/pigeons/messages.dart diff --git a/packages/firebase_app_check/firebase_app_check/CHANGELOG.md b/packages/firebase_app_check/firebase_app_check/CHANGELOG.md index 44fec0c8068e..ee988ff330aa 100644 --- a/packages/firebase_app_check/firebase_app_check/CHANGELOG.md +++ b/packages/firebase_app_check/firebase_app_check/CHANGELOG.md @@ -1,3 +1,26 @@ +## 0.4.5 + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + +## 0.4.4+2 + + - Update a dependency to the latest release. + +## 0.4.4+1 + + - Update a dependency to the latest release. + +## 0.4.4 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + +## 0.4.3 + + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 0.4.2 - **FEAT**(messaging,web): add support for debug tokens on Web ([#18057](https://github.com/firebase/flutterfire/issues/18057)). ([b853386e](https://github.com/firebase/flutterfire/commit/b853386e987d686eab4b8fd9b8dad14eda97479c)) diff --git a/packages/firebase_app_check/firebase_app_check/android/build.gradle b/packages/firebase_app_check/firebase_app_check/android/build.gradle index 099e28aab3c1..cc7c342a5ca7 100644 --- a/packages/firebase_app_check/firebase_app_check/android/build.gradle +++ b/packages/firebase_app_check/firebase_app_check/android/build.gradle @@ -5,14 +5,11 @@ apply plugin: 'com.android.library' apply from: file("local-config.gradle") buildscript { + ext.kotlin_version = "2.0.0" repositories { google() mavenCentral() } - - dependencies { - classpath 'com.android.tools.build:gradle:8.3.0' - } } rootProject.allprojects { @@ -22,6 +19,16 @@ rootProject.allprojects { } } +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. +def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { + apply plugin: 'kotlin-android' +} + def firebaseCoreProject = findProject(':firebase_core') if (firebaseCoreProject == null) { throw new GradleException('Could not find the firebase_core FlutterFire plugin, have you added it as a dependency in your pubspec?') @@ -53,6 +60,11 @@ android { targetCompatibility project.ext.javaVersion } + sourceSets { + main.java.srcDirs += "src/main/kotlin" + test.java.srcDirs += "src/test/kotlin" + } + buildFeatures { buildConfig true } @@ -66,8 +78,16 @@ android { implementation platform("com.google.firebase:firebase-bom:${getRootProjectExtOrCoreProperty("FirebaseSDKVersion", firebaseCoreProject)}") implementation 'com.google.firebase:firebase-appcheck-debug' implementation 'com.google.firebase:firebase-appcheck-playintegrity' + implementation 'com.google.firebase:firebase-appcheck-recaptcha' implementation 'androidx.annotation:annotation:1.7.0' + } +} +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } } } diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppCheckPlugin.java b/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppCheckPlugin.java deleted file mode 100644 index a41d916f2db8..000000000000 --- a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppCheckPlugin.java +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2021 The Chromium 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.firebase.appcheck; - -import static io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry.registerPlugin; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.google.android.gms.tasks.Task; -import com.google.android.gms.tasks.TaskCompletionSource; -import com.google.android.gms.tasks.Tasks; -import com.google.firebase.FirebaseApp; -import com.google.firebase.appcheck.AppCheckToken; -import com.google.firebase.appcheck.FirebaseAppCheck; -import com.google.firebase.appcheck.debug.DebugAppCheckProviderFactory; -import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.EventChannel; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugins.firebase.core.FlutterFirebasePlugin; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class FlutterFirebaseAppCheckPlugin - implements FlutterFirebasePlugin, FlutterPlugin, MethodCallHandler { - - private static final String METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_app_check"; - private final Map streamHandlers = new HashMap<>(); - - private final String debugProvider = "debug"; - private final String playIntegrity = "playIntegrity"; - - @Nullable private BinaryMessenger messenger; - - private MethodChannel channel; - - private void initInstance(BinaryMessenger messenger) { - registerPlugin(METHOD_CHANNEL_NAME, this); - channel = new MethodChannel(messenger, METHOD_CHANNEL_NAME); - channel.setMethodCallHandler(this); - - this.messenger = messenger; - } - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { - initInstance(binding.getBinaryMessenger()); - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - channel = null; - messenger = null; - - removeEventListeners(); - } - - private FirebaseAppCheck getAppCheck(Map arguments) { - String appName = (String) Objects.requireNonNull(arguments.get("appName")); - FirebaseApp app = FirebaseApp.getInstance(appName); - return FirebaseAppCheck.getInstance(app); - } - - private Task getLimitedUseAppCheckToken(Map arguments) { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - FirebaseAppCheck firebaseAppCheck = getAppCheck(arguments); - AppCheckToken tokenResult = Tasks.await(firebaseAppCheck.getLimitedUseAppCheckToken()); - taskCompletionSource.setResult(tokenResult.getToken()); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - private Task activate(Map arguments) { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - String provider = (String) Objects.requireNonNull(arguments.get("androidProvider")); - - switch (provider) { - case debugProvider: - { - FirebaseAppCheck firebaseAppCheck = getAppCheck(arguments); - FlutterFirebaseAppRegistrar.debugToken = - (String) arguments.get("androidDebugToken"); - firebaseAppCheck.installAppCheckProviderFactory( - DebugAppCheckProviderFactory.getInstance()); - break; - } - case playIntegrity: - { - FirebaseAppCheck firebaseAppCheck = getAppCheck(arguments); - firebaseAppCheck.installAppCheckProviderFactory( - PlayIntegrityAppCheckProviderFactory.getInstance()); - break; - } - } - taskCompletionSource.setResult(null); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - private Task getToken(Map arguments) { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - FirebaseAppCheck firebaseAppCheck = getAppCheck(arguments); - Boolean forceRefresh = (Boolean) Objects.requireNonNull(arguments.get("forceRefresh")); - AppCheckToken tokenResult = - Tasks.await(firebaseAppCheck.getAppCheckToken(forceRefresh)); - - taskCompletionSource.setResult(tokenResult.getToken()); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - private Task setTokenAutoRefreshEnabled(Map arguments) { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - FirebaseAppCheck firebaseAppCheck = getAppCheck(arguments); - Boolean isTokenAutoRefreshEnabled = - (Boolean) Objects.requireNonNull(arguments.get("isTokenAutoRefreshEnabled")); - firebaseAppCheck.setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled); - - taskCompletionSource.setResult(null); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - private Task registerTokenListener(Map arguments) { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - String appName = (String) Objects.requireNonNull(arguments.get("appName")); - FirebaseAppCheck firebaseAppCheck = getAppCheck(arguments); - - final TokenChannelStreamHandler handler = - new TokenChannelStreamHandler(firebaseAppCheck); - final String name = METHOD_CHANNEL_NAME + "/token/" + appName; - final EventChannel channel = new EventChannel(messenger, name); - channel.setStreamHandler(handler); - streamHandlers.put(channel, handler); - - taskCompletionSource.setResult(name); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - @Override - public void onMethodCall(MethodCall call, @NonNull final Result result) { - Task methodCallTask; - - switch (call.method) { - case "FirebaseAppCheck#activate": - methodCallTask = activate(call.arguments()); - break; - case "FirebaseAppCheck#getToken": - methodCallTask = getToken(call.arguments()); - break; - case "FirebaseAppCheck#setTokenAutoRefreshEnabled": - methodCallTask = setTokenAutoRefreshEnabled(call.arguments()); - break; - case "FirebaseAppCheck#registerTokenListener": - methodCallTask = registerTokenListener(call.arguments()); - break; - case "FirebaseAppCheck#getLimitedUseAppCheckToken": - methodCallTask = getLimitedUseAppCheckToken(call.arguments()); - break; - default: - result.notImplemented(); - return; - } - - methodCallTask.addOnCompleteListener( - task -> { - if (task.isSuccessful()) { - result.success(task.getResult()); - } else { - Exception exception = task.getException(); - result.error( - "firebase_app_check", - exception != null ? exception.getMessage() : null, - getExceptionDetails(exception)); - } - }); - } - - private Map getExceptionDetails(@Nullable Exception exception) { - Map details = new HashMap<>(); - details.put("code", "unknown"); - if (exception != null) { - details.put("message", exception.getMessage()); - } else { - details.put("message", "An unknown error has occurred."); - } - return details; - } - - @Override - public Task> getPluginConstantsForFirebaseApp(FirebaseApp firebaseApp) { - TaskCompletionSource> taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - taskCompletionSource.setResult(null); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - @Override - public Task didReinitializeFirebaseCore() { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - taskCompletionSource.setResult(null); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - private void removeEventListeners() { - for (EventChannel eventChannel : streamHandlers.keySet()) { - EventChannel.StreamHandler streamHandler = streamHandlers.get(eventChannel); - assert streamHandler != null; - streamHandler.onCancel(null); - eventChannel.setStreamHandler(null); - } - streamHandlers.clear(); - } -} diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.java b/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.java deleted file mode 100644 index 2355ec819c0c..000000000000 --- a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 The Chromium 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.firebase.appcheck; - -import androidx.annotation.Keep; -import androidx.annotation.Nullable; -import com.google.firebase.appcheck.debug.InternalDebugSecretProvider; -import com.google.firebase.components.Component; -import com.google.firebase.components.ComponentRegistrar; -import com.google.firebase.platforminfo.LibraryVersionComponent; -import java.util.Arrays; -import java.util.List; - -@Keep -public class FlutterFirebaseAppRegistrar - implements ComponentRegistrar, InternalDebugSecretProvider { - - private static final String DEBUG_SECRET_NAME = "fire-app-check-debug-secret"; - public static String debugToken; - - @Override - public List> getComponents() { - Component library = - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION); - - Component debugSecretProvider = - Component.builder(InternalDebugSecretProvider.class) - .name(DEBUG_SECRET_NAME) - .factory(container -> this) - .build(); - - return Arrays.asList(library, debugSecretProvider); - } - - @Nullable - @Override - public String getDebugSecret() { - return debugToken; - } -} diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/TokenChannelStreamHandler.java b/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/TokenChannelStreamHandler.java deleted file mode 100644 index 3f1367002c70..000000000000 --- a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/TokenChannelStreamHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2022, the Chromium project authors. Please see the AUTHORS file - * for details. 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.firebase.appcheck; - -import com.google.firebase.appcheck.FirebaseAppCheck; -import io.flutter.plugin.common.EventChannel; -import java.util.HashMap; -import java.util.Map; - -public class TokenChannelStreamHandler implements EventChannel.StreamHandler { - - private final FirebaseAppCheck firebaseAppCheck; - private FirebaseAppCheck.AppCheckListener listener; - - public TokenChannelStreamHandler(FirebaseAppCheck firebaseAppCheck) { - this.firebaseAppCheck = firebaseAppCheck; - } - - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - - listener = - result -> { - Map event = new HashMap<>(); - event.put("token", result.getToken()); - events.success(event); - }; - - firebaseAppCheck.addAppCheckListener(listener); - } - - @Override - public void onCancel(Object arguments) { - if (listener != null) { - firebaseAppCheck.removeAppCheckListener(listener); - listener = null; - } - } -} diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FirebaseAppCheckPlugin.kt b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FirebaseAppCheckPlugin.kt new file mode 100644 index 000000000000..99aab95ea256 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FirebaseAppCheckPlugin.kt @@ -0,0 +1,161 @@ +// Copyright 2025 The Chromium 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.firebase.appcheck + +import android.os.Handler +import android.os.Looper +import com.google.android.gms.tasks.Task +import com.google.android.gms.tasks.TaskCompletionSource +import com.google.firebase.FirebaseApp +import com.google.firebase.appcheck.FirebaseAppCheck +import com.google.firebase.appcheck.debug.DebugAppCheckProviderFactory +import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory +import com.google.firebase.appcheck.recaptcha.RecaptchaAppCheckProviderFactory +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugins.firebase.core.FlutterFirebasePlugin +import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry + +class FirebaseAppCheckPlugin : FlutterFirebasePlugin, FlutterPlugin, FirebaseAppCheckHostApi { + + private val streamHandlers: MutableMap = HashMap() + private val eventChannels: MutableMap = HashMap() + private val mainThreadHandler = Handler(Looper.getMainLooper()) + private var messenger: BinaryMessenger? = null + + companion object { + const val METHOD_CHANNEL = "plugins.flutter.io/firebase_app_check" + const val EVENT_CHANNEL_PREFIX = "plugins.flutter.io/firebase_app_check/token/" + } + + override fun onAttachedToEngine(binding: FlutterPluginBinding) { + messenger = binding.binaryMessenger + FirebaseAppCheckHostApi.setUp(binding.binaryMessenger, this) + FlutterFirebasePluginRegistry.registerPlugin(METHOD_CHANNEL, this) + } + + override fun onDetachedFromEngine(binding: FlutterPluginBinding) { + FirebaseAppCheckHostApi.setUp(binding.binaryMessenger, null) + messenger = null + removeEventListeners() + } + + private fun getAppCheck(appName: String): FirebaseAppCheck { + val app = FirebaseApp.getInstance(appName) + return FirebaseAppCheck.getInstance(app) + } + + override fun activate( + appName: String, + androidProvider: String?, + appleProvider: String?, + debugToken: String?, + callback: (Result) -> Unit + ) { + try { + val firebaseAppCheck = getAppCheck(appName) + when (androidProvider) { + "debug" -> { + FlutterFirebaseAppRegistrar.debugToken = debugToken + firebaseAppCheck.installAppCheckProviderFactory( + DebugAppCheckProviderFactory.getInstance()) + } + "recaptcha" -> { + firebaseAppCheck.installAppCheckProviderFactory( + RecaptchaAppCheckProviderFactory.getInstance()) + } + else -> { + firebaseAppCheck.installAppCheckProviderFactory( + PlayIntegrityAppCheckProviderFactory.getInstance()) + } + } + callback(Result.success(Unit)) + } catch (e: Exception) { + callback(Result.failure(FlutterError("unknown", e.message, null))) + } + } + + override fun getToken( + appName: String, + forceRefresh: Boolean, + callback: (Result) -> Unit + ) { + val firebaseAppCheck = getAppCheck(appName) + firebaseAppCheck.getAppCheckToken(forceRefresh).addOnCompleteListener { task -> + if (task.isSuccessful) { + callback(Result.success(task.result?.token)) + } else { + callback(Result.failure(FlutterError("firebase_app_check", task.exception?.message, null))) + } + } + } + + override fun setTokenAutoRefreshEnabled( + appName: String, + isTokenAutoRefreshEnabled: Boolean, + callback: (Result) -> Unit + ) { + try { + val firebaseAppCheck = getAppCheck(appName) + firebaseAppCheck.setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled) + callback(Result.success(Unit)) + } catch (e: Exception) { + callback(Result.failure(FlutterError("unknown", e.message, null))) + } + } + + override fun registerTokenListener(appName: String, callback: (Result) -> Unit) { + try { + val firebaseAppCheck = getAppCheck(appName) + val name = EVENT_CHANNEL_PREFIX + appName + + val handler = TokenChannelStreamHandler(firebaseAppCheck) + val channel = EventChannel(messenger, name) + channel.setStreamHandler(handler) + eventChannels[name] = channel + streamHandlers[name] = handler + + callback(Result.success(name)) + } catch (e: Exception) { + callback(Result.failure(FlutterError("unknown", e.message, null))) + } + } + + override fun getLimitedUseAppCheckToken(appName: String, callback: (Result) -> Unit) { + val firebaseAppCheck = getAppCheck(appName) + firebaseAppCheck.limitedUseAppCheckToken.addOnCompleteListener { task -> + if (task.isSuccessful) { + callback(Result.success(task.result?.token ?: "")) + } else { + callback(Result.failure(FlutterError("firebase_app_check", task.exception?.message, null))) + } + } + } + + override fun getPluginConstantsForFirebaseApp(firebaseApp: FirebaseApp): Task> { + val taskCompletionSource = TaskCompletionSource>() + taskCompletionSource.setResult(HashMap()) + return taskCompletionSource.task + } + + override fun didReinitializeFirebaseCore(): Task { + val taskCompletionSource = TaskCompletionSource() + removeEventListeners() + taskCompletionSource.setResult(null) + return taskCompletionSource.task + } + + private fun removeEventListeners() { + for ((name, channel) in eventChannels) { + channel.setStreamHandler(null) + } + for ((name, handler) in streamHandlers) { + handler.onCancel(null) + } + eventChannels.clear() + streamHandlers.clear() + } +} diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.kt b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.kt new file mode 100644 index 000000000000..1f1087b14698 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.kt @@ -0,0 +1,37 @@ +// Copyright 2025 The Chromium 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.firebase.appcheck + +import androidx.annotation.Keep +import com.google.firebase.appcheck.debug.InternalDebugSecretProvider +import com.google.firebase.components.Component +import com.google.firebase.components.ComponentRegistrar +import com.google.firebase.platforminfo.LibraryVersionComponent + +@Keep +class FlutterFirebaseAppRegistrar : ComponentRegistrar, InternalDebugSecretProvider { + + companion object { + private const val DEBUG_SECRET_NAME = "fire-app-check-debug-secret" + + @JvmStatic var debugToken: String? = null + } + + override fun getComponents(): List> { + val library = + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION) + + val debugSecretProvider = + Component.builder(InternalDebugSecretProvider::class.java) + .name(DEBUG_SECRET_NAME) + .factory { this } + .build() + + return listOf(library, debugSecretProvider) + } + + override fun getDebugSecret(): String? { + return debugToken + } +} diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt new file mode 100644 index 000000000000..4cd7a39bc1b4 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt @@ -0,0 +1,223 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. 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 (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package io.flutter.plugins.firebase.appcheck + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private object GeneratedAndroidFirebaseAppCheckPigeonUtils { + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf(exception.code, exception.message, exception.details) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) + } + } +} + +/** + * 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 FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() + +private open class GeneratedAndroidFirebaseAppCheckPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return super.readValueOfType(type, buffer) + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + super.writeValue(stream, value) + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface FirebaseAppCheckHostApi { + fun activate( + appName: String, + androidProvider: String?, + appleProvider: String?, + debugToken: String?, + callback: (Result) -> Unit + ) + + fun getToken(appName: String, forceRefresh: Boolean, callback: (Result) -> Unit) + + fun setTokenAutoRefreshEnabled( + appName: String, + isTokenAutoRefreshEnabled: Boolean, + callback: (Result) -> Unit + ) + + fun registerTokenListener(appName: String, callback: (Result) -> Unit) + + fun getLimitedUseAppCheckToken(appName: String, callback: (Result) -> Unit) + + companion object { + /** The codec used by FirebaseAppCheckHostApi. */ + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseAppCheckPigeonCodec() } + /** + * Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the + * `binaryMessenger`. + */ + @JvmOverloads + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebaseAppCheckHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + val androidProviderArg = args[1] as String? + val appleProviderArg = args[2] as String? + val debugTokenArg = args[3] as String? + api.activate(appNameArg, androidProviderArg, appleProviderArg, debugTokenArg) { + result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + val forceRefreshArg = args[1] as Boolean + api.getToken(appNameArg, forceRefreshArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + val isTokenAutoRefreshEnabledArg = args[1] as Boolean + api.setTokenAutoRefreshEnabled(appNameArg, isTokenAutoRefreshEnabledArg) { + result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.registerTokenListener(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.getLimitedUseAppCheckToken(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/TokenChannelStreamHandler.kt b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/TokenChannelStreamHandler.kt new file mode 100644 index 000000000000..81d1b83e188a --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/TokenChannelStreamHandler.kt @@ -0,0 +1,30 @@ +// Copyright 2025 The Chromium 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.firebase.appcheck + +import com.google.firebase.appcheck.FirebaseAppCheck +import io.flutter.plugin.common.EventChannel + +class TokenChannelStreamHandler(private val firebaseAppCheck: FirebaseAppCheck) : + EventChannel.StreamHandler { + + private var listener: FirebaseAppCheck.AppCheckListener? = null + + override fun onListen(arguments: Any?, events: EventChannel.EventSink) { + listener = + FirebaseAppCheck.AppCheckListener { result -> + val event = HashMap() + event["token"] = result.token + events.success(event) + } + firebaseAppCheck.addAppCheckListener(listener!!) + } + + override fun onCancel(arguments: Any?) { + listener?.let { + firebaseAppCheck.removeAppCheckListener(it) + listener = null + } + } +} diff --git a/packages/firebase_app_check/firebase_app_check/example/.metadata b/packages/firebase_app_check/firebase_app_check/example/.metadata index 784ce1298249..827d9a16d24a 100644 --- a/packages/firebase_app_check/firebase_app_check/example/.metadata +++ b/packages/firebase_app_check/firebase_app_check/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + revision: "90673a4eef275d1a6692c26ac80d6d746d41a73a" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - - platform: web - create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 - base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + create_revision: 90673a4eef275d1a6692c26ac80d6d746d41a73a + base_revision: 90673a4eef275d1a6692c26ac80d6d746d41a73a + - platform: windows + create_revision: 90673a4eef275d1a6692c26ac80d6d746d41a73a + base_revision: 90673a4eef275d1a6692c26ac80d6d746d41a73a # User provided section diff --git a/packages/firebase_app_check/firebase_app_check/example/android/app/build.gradle b/packages/firebase_app_check/firebase_app_check/example/android/app/build.gradle index ef73138ff489..92298e7270e5 100644 --- a/packages/firebase_app_check/firebase_app_check/example/android/app/build.gradle +++ b/packages/firebase_app_check/firebase_app_check/example/android/app/build.gradle @@ -45,7 +45,7 @@ android { applicationId = "io.flutter.plugins.firebase.appcheck.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = 23 + minSdkVersion = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName diff --git a/packages/firebase_app_check/firebase_app_check/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/appcheck/example/MainActivity.kt b/packages/firebase_app_check/firebase_app_check/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/appcheck/example/MainActivity.kt index 23c1224eea5c..fd3526f15954 100644 --- a/packages/firebase_app_check/firebase_app_check/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/appcheck/example/MainActivity.kt +++ b/packages/firebase_app_check/firebase_app_check/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/appcheck/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.appcheck.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle b/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle index 30463c1cf2f2..4fb566e9929e 100644 --- a/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle +++ b/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle @@ -22,7 +22,7 @@ plugins { // START: FlutterFire Configuration id "com.google.gms.google-services" version "4.3.15" apply false // END: FlutterFire Configuration - id "org.jetbrains.kotlin.android" version "1.9.22" apply false + id "org.jetbrains.kotlin.android" version "2.1.0" apply false } include ":app" diff --git a/packages/firebase_app_check/firebase_app_check/example/ios/Flutter/AppFrameworkInfo.plist b/packages/firebase_app_check/firebase_app_check/example/ios/Flutter/AppFrameworkInfo.plist index 7c5696400627..391a902b2beb 100644 --- a/packages/firebase_app_check/firebase_app_check/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/firebase_app_check/firebase_app_check/example/ios/Flutter/AppFrameworkInfo.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1.0 - MinimumOSVersion - 12.0 diff --git a/packages/firebase_app_check/firebase_app_check/example/ios/Podfile b/packages/firebase_app_check/firebase_app_check/example/ios/Podfile index bc779113f7ca..620e46eba607 100644 --- a/packages/firebase_app_check/firebase_app_check/example/ios/Podfile +++ b/packages/firebase_app_check/firebase_app_check/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '15.0' +# platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -29,22 +29,15 @@ flutter_ios_podfile_setup target 'Runner' do use_frameworks! - use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end - - installer.generated_projects.each do |project| - project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' - end - end - end end - diff --git a/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/project.pbxproj b/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/project.pbxproj index 7d86b897a846..1ef35c770b2f 100644 --- a/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 4632D5BC275CD47A0059DC83 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 46A64A032996811C003FC4F3 /* RunnerRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerRelease.entitlements; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -66,6 +67,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, diff --git a/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 6f83bdc750b0..8a6c683e3d89 100644 --- a/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/firebase_app_check/firebase_app_check/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -44,6 +44,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/packages/firebase_app_check/firebase_app_check/example/lib/firebase_options.dart b/packages/firebase_app_check/firebase_app_check/example/lib/firebase_options.dart index 2a78f25ed1b0..8bbd98affc15 100644 --- a/packages/firebase_app_check/firebase_app_check/example/lib/firebase_options.dart +++ b/packages/firebase_app_check/firebase_app_check/example/lib/firebase_options.dart @@ -31,10 +31,7 @@ class DefaultFirebaseOptions { case TargetPlatform.macOS: return macos; case TargetPlatform.windows: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for windows - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); + return web; case TargetPlatform.linux: throw UnsupportedError( 'DefaultFirebaseOptions have not been configured for linux - ' diff --git a/packages/firebase_app_check/firebase_app_check/example/lib/main.dart b/packages/firebase_app_check/firebase_app_check/example/lib/main.dart index 7a1e778d35a0..8f26c6439a83 100644 --- a/packages/firebase_app_check/firebase_app_check/example/lib/main.dart +++ b/packages/firebase_app_check/firebase_app_check/example/lib/main.dart @@ -2,6 +2,8 @@ // for details. 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: do_not_use_environment + import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_core/firebase_core.dart'; @@ -12,6 +14,15 @@ import 'firebase_options.dart'; const kWebRecaptchaSiteKey = '6Lemcn0dAAAAABLkf6aiiHvpGD6x-zF3nOSDU2M8'; +// Windows: create a debug token in the Firebase Console +// (App Check > Apps > Manage debug tokens), then paste it here +// or set the APP_CHECK_DEBUG_TOKEN environment variable. +const kWindowsDebugToken = String.fromEnvironment( + 'APP_CHECK_DEBUG_TOKEN', + // ignore: avoid_redundant_argument_values + defaultValue: '', +); + Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( @@ -20,14 +31,21 @@ Future main() async { // Activate app check after initialization, but before // usage of any Firebase services. - await FirebaseAppCheck.instance - // Your personal reCaptcha public key goes here: - .activate( + await FirebaseAppCheck.instance.activate( providerWeb: kDebugMode ? WebDebugProvider() : ReCaptchaV3Provider(kWebRecaptchaSiteKey), providerAndroid: const AndroidDebugProvider(), providerApple: const AppleDebugProvider(), + // On Windows, only the debug provider is available. + // You must supply a debug token — the desktop C++ SDK does not + // auto-generate one. Create one in the Firebase Console under + // App Check > Apps > Manage debug tokens, then either: + // - pass it via --dart-define=APP_CHECK_DEBUG_TOKEN= + // - or set the APP_CHECK_DEBUG_TOKEN environment variable + providerWindows: WindowsDebugProvider( + debugToken: kWindowsDebugToken.isNotEmpty ? kWindowsDebugToken : null, + ), ); runApp(MyApp()); @@ -36,7 +54,6 @@ Future main() async { class MyApp extends StatelessWidget { final String title = 'Firebase App Check'; - // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( @@ -81,65 +98,136 @@ class _FirebaseAppCheck extends State { }); } + Future _activate({ + AndroidAppCheckProvider? android, + AppleAppCheckProvider? apple, + WindowsAppCheckProvider? windows, + }) async { + try { + await appCheck.activate( + providerAndroid: android ?? const AndroidPlayIntegrityProvider(), + providerApple: apple ?? const AppleDeviceCheckProvider(), + providerWeb: ReCaptchaV3Provider(kWebRecaptchaSiteKey), + providerWindows: windows ?? const WindowsDebugProvider(), + ); + final providerName = windows?.runtimeType.toString() ?? + apple?.runtimeType.toString() ?? + android?.runtimeType.toString() ?? + 'default'; + setMessage('Activated with $providerName'); + } catch (e) { + setMessage('activate error: $e'); + } + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), - body: Center( + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + const Text( + 'Providers', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + ElevatedButton( + onPressed: () => _activate( + android: const AndroidDebugProvider(), + apple: const AppleDebugProvider(), + windows: WindowsDebugProvider( + debugToken: + kWindowsDebugToken.isNotEmpty ? kWindowsDebugToken : null, + ), + ), + child: const Text('activate(Debug)'), + ), + ElevatedButton( + onPressed: () => _activate( + android: const AndroidPlayIntegrityProvider(), + apple: const AppleDeviceCheckProvider(), + ), + child: const Text('activate(PlayIntegrity / DeviceCheck)'), + ), + if (!kIsWeb) + ElevatedButton( + onPressed: () => _activate( + apple: const AppleAppAttestProvider(), + ), + child: const Text('activate(AppAttest)'), + ), + if (!kIsWeb) + ElevatedButton( + onPressed: () => _activate( + apple: const AppleAppAttestWithDeviceCheckFallbackProvider(), + ), + child: const Text( + 'activate(AppAttest + DeviceCheck fallback)', + ), + ), + const SizedBox(height: 16), + const Text( + 'Actions', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), ElevatedButton( onPressed: () async { - // Use this button to check whether the request was validated on the Firebase console - // Gets first document in collection - final result = await FirebaseFirestore.instance - .collection('flutter-tests') - .limit(1) - .get(); - - if (result.docs.isNotEmpty) { - setMessage('Document found'); - } else { - setMessage( - 'Document not found, please add a document to the collection', - ); + try { + final token = await appCheck.getToken(true); + setMessage('Token: ${token?.substring(0, 20)}...'); + } catch (e) { + setMessage('getToken error: $e'); } }, - child: const Text('Test App Check validates requests'), + child: const Text('getToken(forceRefresh: true)'), ), ElevatedButton( onPressed: () async { - if (kIsWeb) { - print( - 'Pass in your "webRecaptchaSiteKey" key found on you Firebase Console to activate if using on the web platform.', + try { + final token = await appCheck.getLimitedUseToken(); + setMessage( + 'Limited use token: ${token.substring(0, 20)}...', ); + } catch (e) { + setMessage('getLimitedUseToken error: $e'); } - await appCheck.activate( - providerWeb: ReCaptchaV3Provider(kWebRecaptchaSiteKey), - ); - setMessage('activated!!'); }, - child: const Text('activate()'), + child: const Text('getLimitedUseToken()'), ), ElevatedButton( onPressed: () async { - // Token will be passed to `onTokenChange()` event handler - await appCheck.getToken(true); + await appCheck.setTokenAutoRefreshEnabled(true); + setMessage('Token auto-refresh enabled'); }, - child: const Text('getToken()'), + child: const Text('setTokenAutoRefreshEnabled(true)'), ), ElevatedButton( onPressed: () async { - await appCheck.setTokenAutoRefreshEnabled(true); - setMessage('successfully set auto token refresh!!'); + try { + final result = await FirebaseFirestore.instance + .collection('flutter-tests') + .limit(1) + .get(); + setMessage( + result.docs.isNotEmpty + ? 'Firestore: Document found' + : 'Firestore: No documents', + ); + } catch (e) { + setMessage('Firestore error: $e'); + } }, - child: const Text('setTokenAutoRefreshEnabled()'), + child: const Text('Test Firestore with App Check'), ), const SizedBox(height: 20), Text( - _message, //#007bff + _message, style: const TextStyle( color: Color.fromRGBO(47, 79, 79, 1), fontSize: 16, @@ -147,10 +235,10 @@ class _FirebaseAppCheck extends State { ), const SizedBox(height: 20), Text( - 'Token received from tokenChanges() API: $_eventToken', //#007bff + 'Token from onTokenChange: $_eventToken', style: const TextStyle( color: Color.fromRGBO(128, 0, 128, 1), - fontSize: 16, + fontSize: 14, ), ), ], diff --git a/packages/firebase_app_check/firebase_app_check/example/macos/Podfile b/packages/firebase_app_check/firebase_app_check/example/macos/Podfile index 9ec46f8cd53c..ff5ddb3b8bdc 100644 --- a/packages/firebase_app_check/firebase_app_check/example/macos/Podfile +++ b/packages/firebase_app_check/firebase_app_check/example/macos/Podfile @@ -28,9 +28,11 @@ flutter_macos_podfile_setup target 'Runner' do use_frameworks! - use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| diff --git a/packages/firebase_app_check/firebase_app_check/example/macos/Runner.xcodeproj/project.pbxproj b/packages/firebase_app_check/firebase_app_check/example/macos/Runner.xcodeproj/project.pbxproj index 7c41affca666..c0dc38604806 100644 --- a/packages/firebase_app_check/firebase_app_check/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_app_check/firebase_app_check/example/macos/Runner.xcodeproj/project.pbxproj @@ -28,7 +28,6 @@ 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 */; }; - BB0DE7CB0DF8ACBEFD8915B8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 292B95E8595C74EC66477907 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -56,10 +55,7 @@ /* Begin PBXFileReference section */ 0DC934EE60634F0D37DD0EC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; - 24274BFFB66F90649AACE273 /* 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 = ""; }; 25624AEB275E1E7900B1E491 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - 292B95E8595C74EC66477907 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 3206A1D9CA5124387AA36F3A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* firebase_app_check_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = firebase_app_check_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -76,7 +72,6 @@ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - B06DD14F985336F6BE0D10BF /* 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 */ @@ -85,7 +80,6 @@ buildActionMask = 2147483647; files = ( 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, - BB0DE7CB0DF8ACBEFD8915B8 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -109,7 +103,6 @@ 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, 74CB5F55BD11E25520F6FF45 /* Pods */, 0DC934EE60634F0D37DD0EC3 /* GoogleService-Info.plist */, ); @@ -162,21 +155,10 @@ 74CB5F55BD11E25520F6FF45 /* Pods */ = { isa = PBXGroup; children = ( - 24274BFFB66F90649AACE273 /* Pods-Runner.debug.xcconfig */, - 3206A1D9CA5124387AA36F3A /* Pods-Runner.release.xcconfig */, - B06DD14F985336F6BE0D10BF /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 292B95E8595C74EC66477907 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -184,7 +166,6 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 8FAF6BECF1FF5E1A559C7452 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, @@ -304,28 +285,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 8FAF6BECF1FF5E1A559C7452 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/packages/firebase_app_check/firebase_app_check/example/pubspec.yaml b/packages/firebase_app_check/firebase_app_check/example/pubspec.yaml index a7a9f49a900f..c652ec309fe8 100644 --- a/packages/firebase_app_check/firebase_app_check/example/pubspec.yaml +++ b/packages/firebase_app_check/firebase_app_check/example/pubspec.yaml @@ -4,14 +4,16 @@ description: Firebase App Check example application. publish_to: 'none' version: 1.0.0+1 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - cloud_firestore: ^6.2.0 - firebase_app_check: ^0.4.2 - firebase_core: ^4.6.0 + cloud_firestore: ^6.6.0 + firebase_app_check: ^0.4.5 + firebase_core: ^4.11.0 flutter: sdk: flutter diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/.gitignore b/packages/firebase_app_check/firebase_app_check/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/CMakeLists.txt b/packages/firebase_app_check/firebase_app_check/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..ffc5f0c5bd79 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(firebase_app_check_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "firebase_app_check_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/flutter/CMakeLists.txt b/packages/firebase_app_check/firebase_app_check/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..903f4899d6fc --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/CMakeLists.txt b/packages/firebase_app_check/firebase_app_check/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..394917c053a0 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/Runner.rc b/packages/firebase_app_check/firebase_app_check/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..26d198ffd138 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.flutter.plugins.firebase.appcheck" "\0" + VALUE "FileDescription", "firebase_app_check_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "firebase_app_check_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 io.flutter.plugins.firebase.appcheck. All rights reserved." "\0" + VALUE "OriginalFilename", "firebase_app_check_example.exe" "\0" + VALUE "ProductName", "firebase_app_check_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/flutter_window.cpp b/packages/firebase_app_check/firebase_app_check/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..d1bd86f4967a --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/flutter_window.cpp @@ -0,0 +1,73 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { this->Show(); }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/flutter_window.h b/packages/firebase_app_check/firebase_app_check/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..243c83529a77 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/flutter_window.h @@ -0,0 +1,37 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/main.cpp b/packages/firebase_app_check/firebase_app_check/example/windows/runner/main.cpp new file mode 100644 index 000000000000..129ed2c35f81 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"firebase_app_check_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/resource.h b/packages/firebase_app_check/firebase_app_check/example/windows/runner/resource.h new file mode 100644 index 000000000000..91d70fa37f5d --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/resource.h @@ -0,0 +1,20 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/resources/app_icon.ico b/packages/firebase_app_check/firebase_app_check/example/windows/runner/resources/app_icon.ico new file mode 100644 index 000000000000..c04e20caf637 Binary files /dev/null and b/packages/firebase_app_check/firebase_app_check/example/windows/runner/resources/app_icon.ico differ diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/runner.exe.manifest b/packages/firebase_app_check/firebase_app_check/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..153653e8d67f --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/utils.cpp b/packages/firebase_app_check/firebase_app_check/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..3b1344754669 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/utils.cpp @@ -0,0 +1,69 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr) - + 1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, input_length, + utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/utils.h b/packages/firebase_app_check/firebase_app_check/example/windows/runner/utils.h new file mode 100644 index 000000000000..8ec0c44d5bfe --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/win32_window.cpp b/packages/firebase_app_check/firebase_app_check/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..82754b04cd09 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/win32_window.cpp @@ -0,0 +1,284 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: +/// https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = + L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { return ShowWindow(window_handle_, SW_SHOWNORMAL); } + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = + RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD, nullptr, + &light_mode, &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/packages/firebase_app_check/firebase_app_check/example/windows/runner/win32_window.h b/packages/firebase_app_check/firebase_app_check/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..dd2425483126 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/example/windows/runner/win32_window.h @@ -0,0 +1,104 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check.podspec b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check.podspec index a1a5161f0e7f..b4d81500ec2a 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check.podspec +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check.podspec @@ -24,8 +24,7 @@ Pod::Spec.new do |s| s.license = { :file => '../LICENSE' } s.authors = 'The Chromium Authors' s.source = { :path => '.' } - s.source_files = 'firebase_app_check/Sources/firebase_app_check/**/*.{h,m}' - s.public_header_files = 'firebase_app_check/Sources/firebase_app_check/include/*.h' + s.source_files = 'firebase_app_check/Sources/firebase_app_check/**/*.swift' s.ios.deployment_target = '15.0' # Flutter dependencies diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Package.swift b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Package.swift index 55b8a05577b1..14de1a767958 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Package.swift +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Package.swift @@ -7,19 +7,22 @@ import PackageDescription -let library_version = "0.4.1-5" -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_app_check", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-app-check", targets: ["firebase_app_check"]), + .library(name: "firebase-app-check", targets: ["firebase_app_check"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), + .package( + url: "https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk.git", + from: "18.0.0" + ), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -27,16 +30,12 @@ let package = Package( name: "firebase_app_check", dependencies: [ .product(name: "FirebaseAppCheck", package: "firebase-ios-sdk"), + .product(name: "RecaptchaEnterprise", package: "recaptcha-enterprise-mobile-sdk"), .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), - ], - cSettings: [ - .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), - .define("LIBRARY_NAME", to: "\"flutter-fire-appcheck\""), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/Constants.swift b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/Constants.swift new file mode 100644 index 000000000000..28a2ec57bc1b --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/Constants.swift @@ -0,0 +1,6 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Auto-generated file. Do not edit. +public let versionNumber = "0.4.5" diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m deleted file mode 100644 index aacddaf48f2f..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTAppCheckProvider.h" - -@implementation FLTAppCheckProvider - -- (id)initWithApp:app { - self = [super init]; - if (self) { - self.app = app; - } - return self; -} - -- (void)configure:(FIRApp *)app - providerName:(NSString *)providerName - debugToken:(NSString *)debugToken { - if ([providerName isEqualToString:@"debug"]) { - if (debugToken != nil) { - // We have a debug token, so just need to stuff it in the environment and it will hook up - char *key = "FIRAAppCheckDebugToken", *value = (char *)[debugToken UTF8String]; - int overwrite = 1; - setenv(key, value, overwrite); - } - FIRAppCheckDebugProvider *provider = [[FIRAppCheckDebugProvider alloc] initWithApp:app]; - if (debugToken == nil) NSLog(@"Firebase App Check Debug Token: %@", [provider localDebugToken]); - self.delegateProvider = provider; - } - - if ([providerName isEqualToString:@"deviceCheck"]) { - self.delegateProvider = [[FIRDeviceCheckProvider alloc] initWithApp:app]; - } - - if ([providerName isEqualToString:@"appAttest"]) { - if (@available(iOS 14.0, macCatalyst 14.0, tvOS 15.0, watchOS 9.0, *)) { - self.delegateProvider = [[FIRAppAttestProvider alloc] initWithApp:app]; - } else { - // This is not a valid environment, setup debug provider. - self.delegateProvider = [[FIRAppCheckDebugProvider alloc] initWithApp:app]; - } - } - - if ([providerName isEqualToString:@"appAttestWithDeviceCheckFallback"]) { - if (@available(iOS 14.0, *)) { - self.delegateProvider = [[FIRAppAttestProvider alloc] initWithApp:app]; - } else { - self.delegateProvider = [[FIRDeviceCheckProvider alloc] initWithApp:app]; - } - } -} - -- (void)getTokenWithCompletion:(nonnull void (^)(FIRAppCheckToken *_Nullable, - NSError *_Nullable))handler { - // Proxying to delegateProvider - [self.delegateProvider getTokenWithCompletion:handler]; -} - -@end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m deleted file mode 100644 index 3eee91a08fc8..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@import FirebaseAppCheck; -@import FirebaseCore; - -#import "FLTAppCheckProviderFactory.h" - -#import "FLTAppCheckProvider.h" - -@implementation FLTAppCheckProviderFactory - -- (nullable id)createProviderWithApp:(FIRApp *)app { - // The SDK may try to call this before we have been configured, - // so we will configure ourselves and set the provider up as a default to start - // pre-configure - if (self.providers == nil) { - self.providers = [NSMutableDictionary new]; - } - - if (self.providers[app.name] == nil) { - self.providers[app.name] = [FLTAppCheckProvider new]; - FLTAppCheckProvider *provider = self.providers[app.name]; - // We set "deviceCheck" as this is currently what is default. Backward compatible. - [provider configure:app providerName:@"deviceCheck" debugToken:nil]; - } - - return self.providers[app.name]; -} - -- (void)configure:(FIRApp *)app - providerName:(NSString *)providerName - debugToken:(NSString *)debugToken { - if (self.providers == nil) { - self.providers = [NSMutableDictionary new]; - } - - if (self.providers[app.name] == nil) { - self.providers[app.name] = [FLTAppCheckProvider new]; - } - - FLTAppCheckProvider *provider = self.providers[app.name]; - [provider configure:app providerName:providerName debugToken:debugToken]; -} - -@end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m deleted file mode 100644 index 7c47283e60b9..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTFirebaseAppCheckPlugin.h" -#import "FLTTokenRefreshStreamHandler.h" - -@import FirebaseAppCheck; - -#if __has_include() -#import -#else -#import -#endif - -#import "FLTAppCheckProviderFactory.h" - -NSString *const kFLTFirebaseAppCheckChannelName = @"plugins.flutter.io/firebase_app_check"; - -@interface FLTFirebaseAppCheckPlugin () -@end - -@implementation FLTFirebaseAppCheckPlugin { - NSMutableDictionary *_eventChannels; - NSMutableDictionary *> *_streamHandlers; - NSObject *_binaryMessenger; - FLTAppCheckProviderFactory *_Nullable providerFactory; -} - -#pragma mark - FlutterPlugin - -- (instancetype)init:(NSObject *)messenger { - self = [super init]; - if (self) { - self->providerFactory = [[FLTAppCheckProviderFactory alloc] init]; - [FIRAppCheck setAppCheckProviderFactory:self->providerFactory]; - - [[FLTFirebasePluginRegistry sharedInstance] registerFirebasePlugin:self]; - _binaryMessenger = messenger; - _eventChannels = [NSMutableDictionary dictionary]; - _streamHandlers = [NSMutableDictionary dictionary]; - } - return self; -} - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:kFLTFirebaseAppCheckChannelName - binaryMessenger:[registrar messenger]]; - FLTFirebaseAppCheckPlugin *instance = - [[FLTFirebaseAppCheckPlugin alloc] init:registrar.messenger]; - [registrar addMethodCallDelegate:instance channel:channel]; -} - -- (void)cleanupWithCompletion:(void (^)(void))completion { - for (FlutterEventChannel *channel in self->_eventChannels.allValues) { - [channel setStreamHandler:nil]; - } - [self->_eventChannels removeAllObjects]; - for (NSObject *handler in self->_streamHandlers.allValues) { - [handler onCancelWithArguments:nil]; - } - [self->_streamHandlers removeAllObjects]; - - if (completion != nil) completion(); -} - -- (void)detachFromEngineForRegistrar:(NSObject *)registrar { - [self cleanupWithCompletion:nil]; -} - -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)flutterResult { - FLTFirebaseMethodCallErrorBlock errorBlock = ^( - NSString *_Nullable code, NSString *_Nullable message, NSDictionary *_Nullable details, - NSError *_Nullable error) { - NSMutableDictionary *errorDetails = [NSMutableDictionary dictionary]; - NSString *errorCode; - - switch (error.code) { - case FIRAppCheckErrorCodeServerUnreachable: - errorCode = @"server-unreachable"; - break; - case FIRAppCheckErrorCodeInvalidConfiguration: - errorCode = @"invalid-configuration"; - break; - case FIRAppCheckErrorCodeKeychain: - errorCode = @"code-keychain"; - break; - case FIRAppCheckErrorCodeUnsupported: - errorCode = @"code-unsupported"; - break; - case FIRAppCheckErrorCodeUnknown: - default: - errorCode = @"unknown"; - } - - NSString *errorMessage = error.localizedDescription; - errorDetails[@"code"] = errorCode; - errorDetails[@"message"] = errorMessage; - flutterResult([FlutterError errorWithCode:errorCode message:errorMessage details:errorDetails]); - }; - - FLTFirebaseMethodCallResult *methodCallResult = - [FLTFirebaseMethodCallResult createWithSuccess:flutterResult andErrorBlock:errorBlock]; - - if ([@"FirebaseAppCheck#activate" isEqualToString:call.method]) { - [self activate:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"FirebaseAppCheck#getToken" isEqualToString:call.method]) { - [self getToken:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"FirebaseAppCheck#setTokenAutoRefreshEnabled" isEqualToString:call.method]) { - [self setTokenAutoRefreshEnabled:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"FirebaseAppCheck#registerTokenListener" isEqualToString:call.method]) { - [self registerTokenListener:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"FirebaseAppCheck#getLimitedUseAppCheckToken" isEqualToString:call.method]) { - [self getLimitedUseAppCheckToken:call.arguments withMethodCallResult:methodCallResult]; - } else { - flutterResult(FlutterMethodNotImplemented); - } -} - -#pragma mark - Firebase App Check API - -- (void)activate:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - NSString *appNameDart = arguments[@"appName"]; - NSString *providerName = arguments[@"appleProvider"]; - NSString *debugToken = arguments[@"appleDebugToken"]; - - FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:appNameDart]; - [self->providerFactory configure:app providerName:providerName debugToken:debugToken]; - result.success(nil); -} - -- (void)registerTokenListener:(id)arguments - withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - NSString *appName = arguments[@"appName"]; - NSString *name = - [NSString stringWithFormat:@"%@/token/%@", kFLTFirebaseAppCheckChannelName, appName]; - - FlutterEventChannel *channel = [FlutterEventChannel eventChannelWithName:name - binaryMessenger:_binaryMessenger]; - - FLTTokenRefreshStreamHandler *handler = [[FLTTokenRefreshStreamHandler alloc] init]; - [channel setStreamHandler:handler]; - - [_eventChannels setObject:channel forKey:name]; - [_streamHandlers setObject:handler forKey:name]; - result.success(name); -} - -- (void)getToken:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRAppCheck *appCheck = [self getFIRAppCheckFromArguments:arguments]; - bool forceRefresh = [arguments[@"forceRefresh"] boolValue]; - - [appCheck tokenForcingRefresh:forceRefresh - completion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) { - if (error != nil) { - result.error(nil, nil, nil, error); - } else { - result.success(token.token); - } - }]; -} - -- (void)getLimitedUseAppCheckToken:(id)arguments - withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRAppCheck *appCheck = [self getFIRAppCheckFromArguments:arguments]; - [appCheck - limitedUseTokenWithCompletion:^(FIRAppCheckToken *_Nullable token, NSError *_Nullable error) { - if (error != nil) { - result.error(nil, nil, nil, error); - } else { - result.success(token.token); - } - }]; -} - -- (void)setTokenAutoRefreshEnabled:(id)arguments - withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRAppCheck *appCheck = [self getFIRAppCheckFromArguments:arguments]; - bool isTokenAutoRefreshEnabled = arguments[@"isTokenAutoRefreshEnabled"]; - appCheck.isTokenAutoRefreshEnabled = isTokenAutoRefreshEnabled; - result.success(nil); -} - -#pragma mark - FLTFirebasePlugin - -- (void)didReinitializeFirebaseCore:(void (^)(void))completion { - [self cleanupWithCompletion:completion]; -} - -- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *)firebase_app { - return @{}; -} - -- (NSString *_Nonnull)firebaseLibraryName { - return @LIBRARY_NAME; -} - -- (NSString *_Nonnull)firebaseLibraryVersion { - return @LIBRARY_VERSION; -} - -- (NSString *_Nonnull)flutterChannelName { - return kFLTFirebaseAppCheckChannelName; -} - -#pragma mark - Utilities - -- (FIRAppCheck *_Nullable)getFIRAppCheckFromArguments:(NSDictionary *)arguments { - NSString *appNameDart = arguments[@"appName"]; - FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:appNameDart]; - FIRAppCheck *appCheck = [FIRAppCheck appCheckWithApp:app]; - - return appCheck; -} - -@end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTTokenRefreshStreamHandler.m b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTTokenRefreshStreamHandler.m deleted file mode 100644 index 4b411926d616..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTTokenRefreshStreamHandler.m +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTTokenRefreshStreamHandler.h" -#import "FLTFirebaseAppCheckPlugin.h" - -const NSNotificationName kNotififactionEvent = @"FIRAppCheckAppCheckTokenDidChangeNotification"; - -NSString *const kTokenKey = @"FIRAppCheckTokenNotificationKey"; - -@implementation FLTTokenRefreshStreamHandler { - id _observer; -} - -- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)events { - _observer = - [NSNotificationCenter.defaultCenter addObserverForName:kNotififactionEvent - object:nil - queue:nil - usingBlock:^(NSNotification *_Nonnull note) { - NSString *token = note.userInfo[kTokenKey]; - - events(@{@"token" : token}); - }]; - - return nil; -} - -- (FlutterError *)onCancelWithArguments:(id)arguments { - [NSNotificationCenter.defaultCenter removeObserver:_observer]; - return nil; -} - -@end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift new file mode 100644 index 000000000000..d8f3a100c89b --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift @@ -0,0 +1,236 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. 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 (v26.3.4), 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 PigeonError: Error { + let code: String + let message: String? + let details: Sendable? + + init(code: String, message: String?, details: Sendable?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + [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, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(Swift.type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func isNullish(_ value: Any?) -> Bool { + value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +private class FirebaseAppCheckMessagesPigeonCodecReader: FlutterStandardReader {} + +private class FirebaseAppCheckMessagesPigeonCodecWriter: FlutterStandardWriter {} + +private class FirebaseAppCheckMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + FirebaseAppCheckMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + FirebaseAppCheckMessagesPigeonCodecWriter(data: data) + } +} + +class FirebaseAppCheckMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = FirebaseAppCheckMessagesPigeonCodec( + readerWriter: FirebaseAppCheckMessagesPigeonCodecReaderWriter() + ) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol FirebaseAppCheckHostApi { + func activate( + appName: String, androidProvider: String?, appleProvider: String?, + debugToken: String?, + completion: @escaping (Result) -> Void) + func getToken( + appName: String, forceRefresh: Bool, + completion: @escaping (Result) -> Void) + func setTokenAutoRefreshEnabled( + appName: String, isTokenAutoRefreshEnabled: Bool, + completion: @escaping (Result) -> Void) + func registerTokenListener(appName: String, completion: @escaping (Result) -> Void) + func getLimitedUseAppCheckToken( + appName: String, + completion: @escaping (Result) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class FirebaseAppCheckHostApiSetup { + static var codec: FlutterStandardMessageCodec { + FirebaseAppCheckMessagesPigeonCodec.shared + } + + /// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the + /// `binaryMessenger`. + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseAppCheckHostApi?, + messageChannelSuffix: String = "" + ) { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let activateChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + activateChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + let androidProviderArg: String? = nilOrValue(args[1]) + let appleProviderArg: String? = nilOrValue(args[2]) + let debugTokenArg: String? = nilOrValue(args[3]) + api.activate( + appName: appNameArg, androidProvider: androidProviderArg, appleProvider: appleProviderArg, + debugToken: debugTokenArg + ) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + activateChannel.setMessageHandler(nil) + } + let getTokenChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + getTokenChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + let forceRefreshArg = args[1] as! Bool + api.getToken(appName: appNameArg, forceRefresh: forceRefreshArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getTokenChannel.setMessageHandler(nil) + } + let setTokenAutoRefreshEnabledChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + setTokenAutoRefreshEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + let isTokenAutoRefreshEnabledArg = args[1] as! Bool + api.setTokenAutoRefreshEnabled( + appName: appNameArg, isTokenAutoRefreshEnabled: isTokenAutoRefreshEnabledArg + ) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setTokenAutoRefreshEnabledChannel.setMessageHandler(nil) + } + let registerTokenListenerChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + registerTokenListenerChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.registerTokenListener(appName: appNameArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + registerTokenListenerChannel.setMessageHandler(nil) + } + let getLimitedUseAppCheckTokenChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + getLimitedUseAppCheckTokenChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.getLimitedUseAppCheckToken(appName: appNameArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getLimitedUseAppCheckTokenChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift new file mode 100644 index 000000000000..2d63a7652e18 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift @@ -0,0 +1,369 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import FirebaseAppCheck +import FirebaseCore + +#if canImport(FlutterMacOS) + import FlutterMacOS +#else + import Flutter +#endif + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif + +let kFirebaseAppCheckChannelName = "plugins.flutter.io/firebase_app_check" +let kFirebaseAppCheckTokenChannelPrefix = "plugins.flutter.io/firebase_app_check/token/" + +// swift-format-ignore: AvoidRetroactiveConformances +extension FlutterError: @retroactive Error {} + +public class FirebaseAppCheckPlugin: NSObject, FlutterPlugin, + FLTFirebasePluginProtocol, FirebaseAppCheckHostApi +{ + private var eventChannels: [String: FlutterEventChannel] = [:] + private var streamHandlers: [String: AppCheckTokenStreamHandler] = [:] + private var providerFactory: FlutterAppCheckProviderFactory? + + static let shared: FirebaseAppCheckPlugin = { + let instance = FirebaseAppCheckPlugin() + instance.providerFactory = FlutterAppCheckProviderFactory() + AppCheck.setAppCheckProviderFactory(instance.providerFactory) + FLTFirebasePluginRegistry.sharedInstance().register(instance) + return instance + }() + + public static func register(with registrar: FlutterPluginRegistrar) { + let binaryMessenger: FlutterBinaryMessenger + + #if os(macOS) + binaryMessenger = registrar.messenger + #elseif os(iOS) + binaryMessenger = registrar.messenger() + #endif + + let instance = shared + instance.binaryMessenger = binaryMessenger + FirebaseAppCheckHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance) + + if FirebaseApp.responds(to: NSSelectorFromString("registerLibrary:withVersion:")) { + FirebaseApp.perform( + NSSelectorFromString("registerLibrary:withVersion:"), + with: instance.firebaseLibraryName(), + with: instance.firebaseLibraryVersion() + ) + } + } + + private var binaryMessenger: FlutterBinaryMessenger? + + func activate( + appName: String, androidProvider: String?, appleProvider: String?, + debugToken: String?, + completion: @escaping (Result) -> Void + ) { + guard let app = FLTFirebasePlugin.firebaseAppNamed(appName) else { + completion( + .failure( + FlutterError( + code: "unknown", message: "Firebase app not found: \(appName)", details: nil + ) + ) + ) + return + } + let provider = appleProvider ?? "deviceCheck" + + providerFactory?.configure( + app: app, + providerName: provider, + debugToken: debugToken + ) + + completion(.success(())) + } + + func getToken( + appName: String, forceRefresh: Bool, + completion: @escaping (Result) -> Void + ) { + guard let app = FLTFirebasePlugin.firebaseAppNamed(appName), + let appCheck = AppCheck.appCheck(app: app) + else { + completion( + .failure( + FlutterError( + code: "unknown", message: "App Check not available for app: \(appName)", details: nil + ) + ) + ) + return + } + + appCheck.token(forcingRefresh: forceRefresh) { token, error in + if let error { + completion(.failure(self.createFlutterError(error))) + } else { + completion(.success(token?.token)) + } + } + } + + func setTokenAutoRefreshEnabled( + appName: String, isTokenAutoRefreshEnabled: Bool, + completion: @escaping (Result) -> Void + ) { + guard let app = FLTFirebasePlugin.firebaseAppNamed(appName), + let appCheck = AppCheck.appCheck(app: app) + else { + completion( + .failure( + FlutterError( + code: "unknown", message: "App Check not available for app: \(appName)", details: nil + ) + ) + ) + return + } + appCheck.isTokenAutoRefreshEnabled = isTokenAutoRefreshEnabled + completion(.success(())) + } + + func registerTokenListener( + appName: String, + completion: @escaping (Result) -> Void + ) { + let name = kFirebaseAppCheckTokenChannelPrefix + appName + + guard let messenger = binaryMessenger else { + completion( + .failure( + FlutterError( + code: "no-messenger", + message: "Binary messenger not available", + details: nil + ) + ) + ) + return + } + + let channel = FlutterEventChannel(name: name, binaryMessenger: messenger) + let handler = AppCheckTokenStreamHandler() + channel.setStreamHandler(handler) + + eventChannels[name] = channel + streamHandlers[name] = handler + + completion(.success(name)) + } + + func getLimitedUseAppCheckToken( + appName: String, + completion: @escaping (Result) -> Void + ) { + guard let app = FLTFirebasePlugin.firebaseAppNamed(appName), + let appCheck = AppCheck.appCheck(app: app) + else { + completion( + .failure( + FlutterError( + code: "unknown", message: "App Check not available for app: \(appName)", details: nil + ) + ) + ) + return + } + + appCheck.limitedUseToken { token, error in + if let error { + completion(.failure(self.createFlutterError(error))) + } else { + completion(.success(token?.token ?? "")) + } + } + } + + // MARK: - FLTFirebasePluginProtocol + + public func didReinitializeFirebaseCore(_ completion: @escaping () -> Void) { + for (_, channel) in eventChannels { + channel.setStreamHandler(nil) + } + for (_, handler) in streamHandlers { + _ = handler.onCancel(withArguments: nil) + } + eventChannels.removeAll() + streamHandlers.removeAll() + completion() + } + + public func pluginConstants(for firebaseApp: FirebaseApp) -> [AnyHashable: Any] { + [:] + } + + public func firebaseLibraryName() -> String { + "flutter-fire-appcheck" + } + + public func firebaseLibraryVersion() -> String { + versionNumber + } + + public func flutterChannelName() -> String { + kFirebaseAppCheckChannelName + } + + private func createFlutterError(_ error: Error) -> FlutterError { + let nsError = error as NSError + var code = "unknown" + switch nsError.code { + case 0: // FIRAppCheckErrorCodeServerUnreachable + code = "server-unreachable" + case 1: // FIRAppCheckErrorCodeInvalidConfiguration + code = "invalid-configuration" + case 2: // FIRAppCheckErrorCodeKeychain + code = "code-keychain" + case 3: // FIRAppCheckErrorCodeUnsupported + code = "code-unsupported" + default: + code = "unknown" + } + return FlutterError( + code: code, + message: nsError.localizedDescription, + details: nil + ) + } +} + +// MARK: - Token Stream Handler + +class AppCheckTokenStreamHandler: NSObject, FlutterStreamHandler { + private var observer: NSObjectProtocol? + + func onListen( + withArguments arguments: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { + observer = NotificationCenter.default.addObserver( + forName: NSNotification.Name("FIRAppCheckAppCheckTokenDidChangeNotification"), + object: nil, + queue: nil + ) { notification in + if let token = notification.userInfo?["FIRAppCheckTokenNotificationKey"] as? String { + events(["token": token]) + } + } + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + if let observer { + NotificationCenter.default.removeObserver(observer) + self.observer = nil + } + return nil + } +} + +// MARK: - App Check Provider Factory + +class FlutterAppCheckProviderFactory: NSObject, AppCheckProviderFactory { + private var providers: [String: AppCheckProviderWrapper] = [:] + + func createProvider(with app: FirebaseApp) -> (any AppCheckProvider)? { + if providers[app.name] == nil { + let wrapper = AppCheckProviderWrapper() + // Default to deviceCheck. activate() will reconfigure with the correct provider. + wrapper.configure( + app: app, + providerName: "deviceCheck", + debugToken: nil + ) + providers[app.name] = wrapper + } + return providers[app.name] + } + + func configure( + app: FirebaseApp, + providerName: String, + debugToken: String? + ) { + if providers[app.name] == nil { + providers[app.name] = AppCheckProviderWrapper() + } + providers[app.name]?.configure( + app: app, + providerName: providerName, + debugToken: debugToken + ) + } +} + +class AppCheckProviderWrapper: NSObject, AppCheckProvider { + private var delegateProvider: (any AppCheckProvider)? + + func configure( + app: FirebaseApp, + providerName: String, + debugToken: String? + ) { + switch providerName { + case "debug": + if let debugToken { + setenv("FIRAAppCheckDebugToken", debugToken, 1) + } + delegateProvider = AppCheckDebugProvider(app: app) + if debugToken == nil, let debugProvider = delegateProvider as? AppCheckDebugProvider { + print("Firebase App Check Debug Token: \(debugProvider.localDebugToken())") + } + case "appAttest": + if #available(iOS 14.0, macOS 14.0, macCatalyst 14.0, tvOS 15.0, watchOS 9.0, *) { + delegateProvider = AppAttestProvider(app: app) + } else { + delegateProvider = AppCheckDebugProvider(app: app) + } + case "appAttestWithDeviceCheckFallback": + if #available(iOS 14.0, macOS 14.0, *) { + delegateProvider = AppAttestProvider(app: app) + } else { + delegateProvider = DeviceCheckProvider(app: app) + } + case "recaptcha": + #if os(iOS) + delegateProvider = RecaptchaProvider(app: app) + if delegateProvider == nil { + print( + "Firebase App Check: failed to initialize RecaptchaProvider. Ensure site key is in GoogleService-Info.plist." + ) + } + #else + print("Firebase App Check: reCAPTCHA is only supported on iOS.") + #endif + default: + // deviceCheck + delegateProvider = DeviceCheckProvider(app: app) + } + } + + func getToken(completion handler: @escaping (AppCheckToken?, Error?) -> Void) { + guard let delegateProvider else { + handler( + nil, + NSError( + domain: "firebase_app_check", code: -1, + userInfo: [NSLocalizedDescriptionKey: "Provider not configured"] + ) + ) + return + } + delegateProvider.getToken(completion: handler) + } +} diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h deleted file mode 100644 index da9efde18370..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import FirebaseAppCheck; - -@interface FLTAppCheckProvider : NSObject - -@property FIRApp *app; - -@property id delegateProvider; - -- (void)configure:(FIRApp *)app - providerName:(NSString *)providerName - debugToken:(NSString *)debugToken; - -- (id)initWithApp:(FIRApp *)app; - -@end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h deleted file mode 100644 index 8e5511ebea94..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -#import - -@interface FLTAppCheckProviderFactory : NSObject - -@property NSMutableDictionary *_Nullable providers; - -- (void)configure:(FIRApp *_Nonnull)app - providerName:(NSString *_Nonnull)providerName - debugToken:(NSString *_Nullable)debugToken; - -@end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTFirebaseAppCheckPlugin.h b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTFirebaseAppCheckPlugin.h deleted file mode 100644 index 9d07d0ebc671..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTFirebaseAppCheckPlugin.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -#import - -#if TARGET_OS_OSX -#import -#else -#import -#endif - -#import -#if __has_include() -#import -#else -#import -#endif -#import "FLTAppCheckProviderFactory.h" - -@interface FLTFirebaseAppCheckPlugin : FLTFirebasePlugin -@end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTTokenRefreshStreamHandler.h b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTTokenRefreshStreamHandler.h deleted file mode 100644 index acd570bc3b75..000000000000 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTTokenRefreshStreamHandler.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -#if TARGET_OS_OSX -#import -#else -#import -#endif - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTTokenRefreshStreamHandler : NSObject -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart index c5f5ae1ad633..5e6a8cc98b81 100644 --- a/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart @@ -13,15 +13,21 @@ export 'package:firebase_app_check_platform_interface/firebase_app_check_platfor AndroidAppCheckProvider, AndroidDebugProvider, AndroidPlayIntegrityProvider, + AndroidReCaptchaProvider, AppleProvider, AppleAppCheckProvider, AppleDebugProvider, AppleDeviceCheckProvider, AppleAppAttestProvider, AppleAppAttestWithDeviceCheckFallbackProvider, + AppleReCaptchaProvider, ReCaptchaEnterpriseProvider, ReCaptchaV3Provider, - WebDebugProvider; + WebDebugProvider, + WebProvider, + WebReCaptchaProvider, + WindowsAppCheckProvider, + WindowsDebugProvider; export 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' show FirebaseException; diff --git a/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart index d605174a3366..0553fa116e03 100644 --- a/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart @@ -5,7 +5,7 @@ part of '../firebase_app_check.dart'; -class FirebaseAppCheck extends FirebasePluginPlatform { +class FirebaseAppCheck extends FirebasePlugin implements FirebaseService { static Map _firebaseAppCheckInstances = {}; FirebaseAppCheck._({required this.app}) @@ -41,10 +41,22 @@ class FirebaseAppCheck extends FirebasePluginPlatform { /// Returns an instance using a specified [FirebaseApp]. static FirebaseAppCheck instanceFor({required FirebaseApp app}) { return _firebaseAppCheckInstances.putIfAbsent(app.name, () { - return FirebaseAppCheck._(app: app); + final instance = FirebaseAppCheck._(app: app); + app.registerService( + instance, + dispose: (appCheck) => appCheck._dispose(), + ); + return instance; }); } + Future _dispose() async { + _firebaseAppCheckInstances.remove(app.name); + final delegate = _delegatePackingProperty; + _delegatePackingProperty = null; + await delegate?.dispose(); + } + /// Activates the Firebase App Check service. /// /// ## Platform Configuration @@ -61,6 +73,13 @@ class FirebaseAppCheck extends FirebasePluginPlatform { /// "app attest with fallback to device check" via `AppleAppCheckProvider`. /// Note: App Attest is only available on iOS 14.0+ and macOS 14.0+. /// + /// **Windows**: Only the debug provider is supported. You **must** supply a + /// debug token — the desktop C++ SDK does not auto-generate one. Either pass + /// it via `providerWindows: WindowsDebugProvider(debugToken: 'your-token')` + /// or set the `APP_CHECK_DEBUG_TOKEN` environment variable. The token must + /// first be registered in the Firebase Console under + /// *App Check → Apps → Manage debug tokens*. + /// /// ## Migration Notice /// /// The `androidProvider` and `appleProvider` parameters will be deprecated @@ -89,6 +108,7 @@ class FirebaseAppCheck extends FirebasePluginPlatform { AndroidAppCheckProvider providerAndroid = const AndroidPlayIntegrityProvider(), AppleAppCheckProvider providerApple = const AppleDeviceCheckProvider(), + WindowsAppCheckProvider providerWindows = const WindowsDebugProvider(), }) { return _delegate.activate( webProvider: providerWeb ?? webProvider, @@ -98,6 +118,7 @@ class FirebaseAppCheck extends FirebasePluginPlatform { appleProvider: appleProvider, providerAndroid: providerAndroid, providerApple: providerApple, + providerWindows: providerWindows, ); } diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check.podspec b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check.podspec index d1b3b7eb6fe8..497fbc642162 100644 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check.podspec +++ b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check.podspec @@ -43,8 +43,7 @@ Pod::Spec.new do |s| s.authors = 'The Chromium Authors' s.source = { :path => '.' } - s.source_files = 'firebase_app_check/Sources/firebase_app_check/**/*.{h,m}' - s.public_header_files = 'firebase_app_check/Sources/firebase_app_check/include/*.h' + s.source_files = 'firebase_app_check/Sources/firebase_app_check/**/*.swift' s.platform = :osx, '10.13' diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Package.swift b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Package.swift index 092bbc07f8ed..2edc73e6569f 100644 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Package.swift +++ b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Package.swift @@ -7,19 +7,18 @@ import PackageDescription -let library_version = "0.4.1-5" -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_app_check", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-app-check", targets: ["firebase_app_check"]), + .library(name: "firebase-app-check", targets: ["firebase_app_check"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), - ], - cSettings: [ - .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), - .define("LIBRARY_NAME", to: "\"flutter-fire-appcheck\""), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/Constants.swift b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/Constants.swift new file mode 120000 index 000000000000..4a4a4bdd92d1 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/Constants.swift @@ -0,0 +1 @@ +../../../../ios/firebase_app_check/Sources/firebase_app_check/Constants.swift \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m deleted file mode 120000 index 57fc55914ef1..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m deleted file mode 120000 index 90899e731380..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m deleted file mode 120000 index 3bef267bc202..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTTokenRefreshStreamHandler.m b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTTokenRefreshStreamHandler.m deleted file mode 120000 index 93cf7946f352..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FLTTokenRefreshStreamHandler.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/firebase_app_check/Sources/firebase_app_check/FLTTokenRefreshStreamHandler.m \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift new file mode 120000 index 000000000000..3593b6772bba --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift @@ -0,0 +1 @@ +../../../../ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift new file mode 120000 index 000000000000..37995e4b53db --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift @@ -0,0 +1 @@ +../../../../ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h deleted file mode 120000 index 62bc70731543..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h deleted file mode 120000 index 5638882c87c2..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTFirebaseAppCheckPlugin.h b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTFirebaseAppCheckPlugin.h deleted file mode 120000 index e64b79a492b6..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTFirebaseAppCheckPlugin.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../ios/firebase_app_check/Sources/firebase_app_check/include/FLTFirebaseAppCheckPlugin.h \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTTokenRefreshStreamHandler.h b/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTTokenRefreshStreamHandler.h deleted file mode 120000 index f4b967d7d5b3..000000000000 --- a/packages/firebase_app_check/firebase_app_check/macos/firebase_app_check/Sources/firebase_app_check/include/FLTTokenRefreshStreamHandler.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../ios/firebase_app_check/Sources/firebase_app_check/include/FLTTokenRefreshStreamHandler.h \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check/pubspec.yaml b/packages/firebase_app_check/firebase_app_check/pubspec.yaml index ae655d160abc..605a2914b2f6 100644 --- a/packages/firebase_app_check/firebase_app_check/pubspec.yaml +++ b/packages/firebase_app_check/firebase_app_check/pubspec.yaml @@ -2,7 +2,8 @@ name: firebase_app_check description: App Check works alongside other Firebase services to help protect your backend resources from abuse, such as billing fraud or phishing. homepage: https://firebase.google.com/docs/app-check repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_check/firebase_app_check -version: 0.4.2 +version: 0.4.5 +resolution: workspace topics: - firebase - app-check @@ -13,14 +14,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_app_check_platform_interface: ^0.2.2 - firebase_app_check_web: ^0.2.3 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_app_check_platform_interface: ^0.4.1 + firebase_app_check_web: ^0.2.5 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter @@ -36,10 +37,12 @@ flutter: platforms: android: package: io.flutter.plugins.firebase.appcheck - pluginClass: FlutterFirebaseAppCheckPlugin + pluginClass: FirebaseAppCheckPlugin ios: - pluginClass: FLTFirebaseAppCheckPlugin + pluginClass: FirebaseAppCheckPlugin macos: - pluginClass: FLTFirebaseAppCheckPlugin + pluginClass: FirebaseAppCheckPlugin web: default_package: firebase_app_check_web + windows: + pluginClass: FirebaseAppCheckPluginCApi diff --git a/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart b/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart index 98aca115dc08..f939040d5708 100755 --- a/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart +++ b/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -13,7 +11,6 @@ import './mock.dart'; void main() { setupFirebaseAppCheckMocks(); late FirebaseApp secondaryApp; - late FirebaseAppCheck appCheck; group('$FirebaseAppCheck', () { setUpAll(() async { @@ -27,15 +24,8 @@ void main() { messagingSenderId: '1234567890', ), ); - appCheck = FirebaseAppCheck.instance; - }); - - setUp(() async { - methodCallLog.clear(); }); - tearDown(methodCallLog.clear); - group('instance', () { test('successful call', () async { final appCheck = FirebaseAppCheck.instance; @@ -52,80 +42,37 @@ void main() { expect(appCheck, isA()); expect(appCheck.app.name, 'secondaryApp'); }); - }); - group('activate', () { - test('successful call', () async { - await appCheck.activate( - providerWeb: ReCaptchaV3Provider('key'), - providerAndroid: const AndroidDebugProvider( - debugToken: 'androidDebug', - ), - providerApple: const AppleDebugProvider( - debugToken: 'appleDebug', - ), + test('creates a fresh instance after app delete and reinitialize', + () async { + const appName = 'delete-reinit-app-check'; + const options = FirebaseOptions( + appId: '1:1234567890:ios:42424242424242', + apiKey: '123', + projectId: '123', + messagingSenderId: '1234567890', ); - - expect( - methodCallLog, - [ - isMethodCall( - 'FirebaseAppCheck#activate', - arguments: { - 'appName': defaultFirebaseAppName, - 'androidProvider': 'debug', - 'appleProvider': 'debug', - 'androidDebugToken': 'androidDebug', - 'appleDebugToken': 'appleDebug', - }, - ), - ], + final app = await Firebase.initializeApp( + name: appName, + options: options, ); - }); - }); - group('getToken', () { - test('successful call', () async { - await appCheck.getToken(true); + final appCheck1 = FirebaseAppCheck.instanceFor(app: app); - expect( - methodCallLog, - [ - isMethodCall( - 'FirebaseAppCheck#getToken', - arguments: { - 'appName': defaultFirebaseAppName, - 'forceRefresh': true, - }, - ), - ], - ); - }); - }); + expect(app.getService(), same(appCheck1)); - group('setTokenAutoRefreshEnabled', () { - test('successful call', () async { - await appCheck.setTokenAutoRefreshEnabled(false); + await app.delete(); - expect( - methodCallLog, - [ - isMethodCall( - 'FirebaseAppCheck#setTokenAutoRefreshEnabled', - arguments: { - 'appName': defaultFirebaseAppName, - 'isTokenAutoRefreshEnabled': false, - }, - ), - ], + final app2 = await Firebase.initializeApp( + name: appName, + options: options, ); - }); - }); + addTearDown(app2.delete); - group('tokenChanges', () { - test('successful call', () async { - final stream = appCheck.onTokenChange; + final appCheck2 = FirebaseAppCheck.instanceFor(app: app2); - expect(stream, isA>()); + expect(appCheck2, isNot(same(appCheck1))); + expect(appCheck2.app, app2); + expect(app2.getService(), same(appCheck2)); }); }); }); diff --git a/packages/firebase_app_check/firebase_app_check/test/mock.dart b/packages/firebase_app_check/firebase_app_check/test/mock.dart index 2da26a89c239..0a6701d30810 100644 --- a/packages/firebase_app_check/firebase_app_check/test/mock.dart +++ b/packages/firebase_app_check/firebase_app_check/test/mock.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:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart'; import 'package:firebase_core_platform_interface/test.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -15,21 +14,22 @@ void setupFirebaseAppCheckMocks([Callback? customHandlers]) { TestWidgetsFlutterBinding.ensureInitialized(); setupFirebaseCoreMocks(); + TestFirebaseAppHostApi.setUp(MockFirebaseAppHostApi()); +} + +class MockFirebaseAppHostApi implements TestFirebaseAppHostApi { + @override + Future delete(String appName) async {} + + @override + Future setAutomaticDataCollectionEnabled( + String appName, + bool enabled, + ) async {} - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(MethodChannelFirebaseAppCheck.channel, - (MethodCall methodCall) async { - if (methodCall.method != 'FirebaseAppCheck#registerTokenListener') { - methodCallLog.add(methodCall); - } - - switch (methodCall.method) { - case 'FirebaseAppCheck#registerTokenListener': - return 'channelName'; - case 'FirebaseAppCheck#getToken': - return 'test-token'; - default: - return false; - } - }); + @override + Future setAutomaticResourceManagementEnabled( + String appName, + bool enabled, + ) async {} } diff --git a/packages/firebase_app_check/firebase_app_check/windows/CMakeLists.txt b/packages/firebase_app_check/firebase_app_check/windows/CMakeLists.txt new file mode 100644 index 000000000000..7c40c200c5e9 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/CMakeLists.txt @@ -0,0 +1,81 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "firebase_app_check") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "firebase_app_check_plugin") + +# Any new source files that you add to the plugin should be added here. +list(APPEND PLUGIN_SOURCES + "firebase_app_check_plugin.cpp" + "firebase_app_check_plugin.h" + "messages.g.cpp" + "messages.g.h" +) + +# Read version from pubspec.yaml +file(STRINGS "../pubspec.yaml" pubspec_content) +foreach(line ${pubspec_content}) + string(FIND ${line} "version: " has_version) + + if("${has_version}" STREQUAL "0") + string(FIND ${line} ": " version_start_pos) + math(EXPR version_start_pos "${version_start_pos} + 2") + string(LENGTH ${line} version_end_pos) + math(EXPR len "${version_end_pos} - ${version_start_pos}") + string(SUBSTRING ${line} ${version_start_pos} ${len} PLUGIN_VERSION) + break() + endif() +endforeach(line) + +configure_file(plugin_version.h.in ${CMAKE_BINARY_DIR}/generated/firebase_app_check/plugin_version.h) +include_directories(${CMAKE_BINARY_DIR}/generated/) + +# Define the plugin library target. Its name must not be changed (see comment +# on PLUGIN_NAME above). +add_library(${PLUGIN_NAME} STATIC + "include/firebase_app_check/firebase_app_check_plugin_c_api.h" + "firebase_app_check_plugin_c_api.cpp" + ${PLUGIN_SOURCES} + ${CMAKE_BINARY_DIR}/generated/firebase_app_check/plugin_version.h +) + +# Apply a standard set of build settings that are configured in the +# application-level CMakeLists.txt. This can be removed for plugins that want +# full control over build settings. +apply_standard_settings(${PLUGIN_NAME}) + +# Symbols are hidden by default to reduce the chance of accidental conflicts +# between plugins. This should not be removed; any symbols that should be +# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PUBLIC FLUTTER_PLUGIN_IMPL) + +# Enable firebase-cpp-sdk's platform logging api. +target_compile_definitions(${PLUGIN_NAME} PRIVATE -DINTERNAL_EXPERIMENTAL=1) + +# Source include directories and library dependencies. Add any plugin-specific +# dependencies here. +set(MSVC_RUNTIME_MODE MD) +set(firebase_libs firebase_core_plugin firebase_app_check) +target_link_libraries(${PLUGIN_NAME} PRIVATE "${firebase_libs}") + +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PUBLIC flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(firebase_app_check_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp new file mode 100644 index 000000000000..d9a0a72d7014 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp @@ -0,0 +1,249 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "firebase_app_check_plugin.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "firebase/app.h" +#include "firebase/app_check.h" +#include "firebase/app_check/debug_provider.h" +#include "firebase/future.h" +#include "firebase_app_check/plugin_version.h" +#include "firebase_core/firebase_core_plugin_c_api.h" +#include "messages.g.h" + +using ::firebase::App; +using ::firebase::Future; +using ::firebase::app_check::AppCheck; +using ::firebase::app_check::AppCheckListener; +using ::firebase::app_check::AppCheckToken; +using ::firebase::app_check::DebugAppCheckProviderFactory; + +namespace firebase_app_check_windows { + +static const std::string kLibraryName = "flutter-fire-app-check"; +static const std::string kEventChannelNamePrefix = + "plugins.flutter.io/firebase_app_check/token/"; + +flutter::BinaryMessenger* FirebaseAppCheckPlugin::binaryMessenger = nullptr; +std::map>> + FirebaseAppCheckPlugin::event_channels_; +std::map + FirebaseAppCheckPlugin::listeners_map_; + +// AppCheckListener implementation that forwards token changes to an EventSink. +class FlutterAppCheckListener : public AppCheckListener { + public: + void SetEventSink( + std::unique_ptr> event_sink) { + event_sink_ = std::move(event_sink); + } + + void OnAppCheckTokenChanged(const AppCheckToken& token) override { + if (event_sink_) { + flutter::EncodableMap event; + event[flutter::EncodableValue("token")] = + flutter::EncodableValue(token.token); + event_sink_->Success(flutter::EncodableValue(event)); + } + } + + private: + std::unique_ptr> event_sink_; +}; + +// StreamHandler for token change events. +class TokenStreamHandler + : public flutter::StreamHandler { + public: + TokenStreamHandler(AppCheck* app_check, const std::string& app_name) + : app_check_(app_check), app_name_(app_name) {} + + std::unique_ptr> + OnListenInternal( + const flutter::EncodableValue* arguments, + std::unique_ptr>&& events) + override { + listener_ = std::make_unique(); + listener_->SetEventSink(std::move(events)); + app_check_->AddAppCheckListener(listener_.get()); + FirebaseAppCheckPlugin::listeners_map_[app_name_] = listener_.get(); + return nullptr; + } + + std::unique_ptr> + OnCancelInternal(const flutter::EncodableValue* arguments) override { + if (listener_) { + app_check_->RemoveAppCheckListener(listener_.get()); + FirebaseAppCheckPlugin::listeners_map_.erase(app_name_); + listener_.reset(); + } + return nullptr; + } + + private: + AppCheck* app_check_; + std::string app_name_; + std::unique_ptr listener_; +}; + +static AppCheck* GetAppCheckFromPigeon(const std::string& app_name) { + App* app = App::GetInstance(app_name.c_str()); + return AppCheck::GetInstance(app); +} + +static FlutterError ParseError(const firebase::FutureBase& completed_future) { + std::string error_code = "unknown"; + int error = completed_future.error(); + switch (error) { + case firebase::app_check::kAppCheckErrorServerUnreachable: + error_code = "server-unreachable"; + break; + case firebase::app_check::kAppCheckErrorInvalidConfiguration: + error_code = "invalid-configuration"; + break; + case firebase::app_check::kAppCheckErrorSystemKeychain: + error_code = "system-keychain"; + break; + case firebase::app_check::kAppCheckErrorUnsupportedProvider: + error_code = "unsupported-provider"; + break; + default: + error_code = "unknown"; + break; + } + + std::string error_message = completed_future.error_message() + ? completed_future.error_message() + : "An unknown error occurred"; + + return FlutterError(error_code, error_message); +} + +// static +void FirebaseAppCheckPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto plugin = std::make_unique(); + + FirebaseAppCheckHostApi::SetUp(registrar->messenger(), plugin.get()); + + registrar->AddPlugin(std::move(plugin)); + + binaryMessenger = registrar->messenger(); + + // Register for platform logging + App::RegisterLibrary(kLibraryName.c_str(), getPluginVersion().c_str(), + nullptr); +} + +FirebaseAppCheckPlugin::FirebaseAppCheckPlugin() {} + +FirebaseAppCheckPlugin::~FirebaseAppCheckPlugin() { + for (auto& [app_name, listener] : listeners_map_) { + App* app = App::GetInstance(app_name.c_str()); + if (app) { + AppCheck* app_check = AppCheck::GetInstance(app); + if (app_check) { + app_check->RemoveAppCheckListener(listener); + } + } + } + listeners_map_.clear(); + event_channels_.clear(); +} + +void FirebaseAppCheckPlugin::Activate( + const std::string& app_name, const std::string* android_provider, + const std::string* apple_provider, const std::string* debug_token, + std::function reply)> result) { + // On Windows/desktop, only the Debug provider is available. + DebugAppCheckProviderFactory* factory = + DebugAppCheckProviderFactory::GetInstance(); + + if (debug_token != nullptr && !debug_token->empty()) { + factory->SetDebugToken(*debug_token); + } + + AppCheck::SetAppCheckProviderFactory(factory); + + result(std::nullopt); +} + +void FirebaseAppCheckPlugin::GetToken( + const std::string& app_name, bool force_refresh, + std::function> reply)> result) { + AppCheck* app_check = GetAppCheckFromPigeon(app_name); + + Future future = app_check->GetAppCheckToken(force_refresh); + future.OnCompletion([result](const Future& completed_future) { + if (completed_future.error() != 0) { + result(ParseError(completed_future)); + } else { + const AppCheckToken* token = completed_future.result(); + if (token) { + result(std::optional(token->token)); + } else { + result(std::optional(std::nullopt)); + } + } + }); +} + +void FirebaseAppCheckPlugin::SetTokenAutoRefreshEnabled( + const std::string& app_name, bool is_token_auto_refresh_enabled, + std::function reply)> result) { + AppCheck* app_check = GetAppCheckFromPigeon(app_name); + app_check->SetTokenAutoRefreshEnabled(is_token_auto_refresh_enabled); + result(std::nullopt); +} + +void FirebaseAppCheckPlugin::RegisterTokenListener( + const std::string& app_name, + std::function reply)> result) { + AppCheck* app_check = GetAppCheckFromPigeon(app_name); + + const std::string name = kEventChannelNamePrefix + app_name; + + auto event_channel = + std::make_unique>( + binaryMessenger, name, &flutter::StandardMethodCodec::GetInstance()); + event_channel->SetStreamHandler( + std::make_unique(app_check, app_name)); + + event_channels_[app_name] = std::move(event_channel); + + result(name); +} + +void FirebaseAppCheckPlugin::GetLimitedUseAppCheckToken( + const std::string& app_name, + std::function reply)> result) { + AppCheck* app_check = GetAppCheckFromPigeon(app_name); + + Future future = app_check->GetLimitedUseAppCheckToken(); + future.OnCompletion([result](const Future& completed_future) { + if (completed_future.error() != 0) { + result(ParseError(completed_future)); + } else { + const AppCheckToken* token = completed_future.result(); + if (token) { + result(token->token); + } else { + result(FlutterError("unknown", "Failed to get limited use token")); + } + } + }); +} + +} // namespace firebase_app_check_windows diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h new file mode 100644 index 000000000000..baabf2bd5931 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h @@ -0,0 +1,72 @@ +/* + * Copyright 2025, the Chromium project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#ifndef FLUTTER_PLUGIN_FIREBASE_APP_CHECK_PLUGIN_H_ +#define FLUTTER_PLUGIN_FIREBASE_APP_CHECK_PLUGIN_H_ + +#include +#include +#include + +#include +#include +#include + +#include "firebase/app.h" +#include "firebase/app_check.h" +#include "firebase/future.h" +#include "messages.g.h" + +namespace firebase_app_check_windows { + +class TokenStreamHandler; + +class FirebaseAppCheckPlugin : public flutter::Plugin, + public FirebaseAppCheckHostApi { + friend class TokenStreamHandler; + + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + FirebaseAppCheckPlugin(); + + virtual ~FirebaseAppCheckPlugin(); + + // Disallow copy and assign. + FirebaseAppCheckPlugin(const FirebaseAppCheckPlugin&) = delete; + FirebaseAppCheckPlugin& operator=(const FirebaseAppCheckPlugin&) = delete; + + // FirebaseAppCheckHostApi methods. + void Activate( + const std::string& app_name, const std::string* android_provider, + const std::string* apple_provider, const std::string* debug_token, + std::function reply)> result) override; + void GetToken(const std::string& app_name, bool force_refresh, + std::function> reply)> + result) override; + void SetTokenAutoRefreshEnabled( + const std::string& app_name, bool is_token_auto_refresh_enabled, + std::function reply)> result) override; + void RegisterTokenListener( + const std::string& app_name, + std::function reply)> result) override; + void GetLimitedUseAppCheckToken( + const std::string& app_name, + std::function reply)> result) override; + + private: + static flutter::BinaryMessenger* binaryMessenger; + static std::map< + std::string, + std::unique_ptr>> + event_channels_; + static std::map + listeners_map_; +}; + +} // namespace firebase_app_check_windows + +#endif // FLUTTER_PLUGIN_FIREBASE_APP_CHECK_PLUGIN_H_ diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin_c_api.cpp b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin_c_api.cpp new file mode 100644 index 000000000000..f0ace78f6241 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin_c_api.cpp @@ -0,0 +1,16 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "include/firebase_app_check/firebase_app_check_plugin_c_api.h" + +#include + +#include "firebase_app_check_plugin.h" + +void FirebaseAppCheckPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + firebase_app_check_windows::FirebaseAppCheckPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/packages/firebase_app_check/firebase_app_check/windows/include/firebase_app_check/firebase_app_check_plugin_c_api.h b/packages/firebase_app_check/firebase_app_check/windows/include/firebase_app_check/firebase_app_check_plugin_c_api.h new file mode 100644 index 000000000000..ab2d9e9464ee --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/include/firebase_app_check/firebase_app_check_plugin_c_api.h @@ -0,0 +1,29 @@ +/* + * Copyright 2025, the Chromium project authors. Please see the AUTHORS file + * for details. All rights reserved. Use of this source code is governed by a + * BSD-style license that can be found in the LICENSE file. + */ + +#ifndef FLUTTER_PLUGIN_FIREBASE_APP_CHECK_PLUGIN_C_API_H_ +#define FLUTTER_PLUGIN_FIREBASE_APP_CHECK_PLUGIN_C_API_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void FirebaseAppCheckPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_FIREBASE_APP_CHECK_PLUGIN_C_API_H_ diff --git a/packages/firebase_app_check/firebase_app_check/windows/messages.g.cpp b/packages/firebase_app_check/firebase_app_check/windows/messages.g.cpp new file mode 100644 index 000000000000..0da3e3c1ded5 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/messages.g.cpp @@ -0,0 +1,517 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. 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 (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "messages.g.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace firebase_app_check_windows { +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; + +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace + +PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} + +EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const { + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); +} + +void PigeonInternalCodecSerializer::WriteValue( + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { + ::flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/// The codec used by FirebaseAppCheckHostApi. +const ::flutter::StandardMessageCodec& FirebaseAppCheckHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); +} + +// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through +// the `binary_messenger`. +void FirebaseAppCheckHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseAppCheckHostApi* api) { + FirebaseAppCheckHostApi::SetUp(binary_messenger, api, ""); +} + +void FirebaseAppCheckHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, FirebaseAppCheckHostApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = + message_channel_suffix.length() > 0 + ? std::string(".") + message_channel_suffix + : ""; + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface." + "FirebaseAppCheckHostApi.activate" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + const auto& encodable_android_provider_arg = args.at(1); + const auto* android_provider_arg = + std::get_if(&encodable_android_provider_arg); + const auto& encodable_apple_provider_arg = args.at(2); + const auto* apple_provider_arg = + std::get_if(&encodable_apple_provider_arg); + const auto& encodable_debug_token_arg = args.at(3); + const auto* debug_token_arg = + std::get_if(&encodable_debug_token_arg); + api->Activate(app_name_arg, android_provider_arg, + apple_provider_arg, debug_token_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface." + "FirebaseAppCheckHostApi.getToken" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + const auto& encodable_force_refresh_arg = args.at(1); + if (encodable_force_refresh_arg.IsNull()) { + reply(WrapError("force_refresh_arg unexpectedly null.")); + return; + } + const auto& force_refresh_arg = + std::get(encodable_force_refresh_arg); + api->GetToken( + app_name_arg, force_refresh_arg, + [reply](ErrorOr>&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + auto output_optional = std::move(output).TakeValue(); + if (output_optional) { + wrapped.push_back( + EncodableValue(std::move(output_optional).value())); + } else { + wrapped.push_back(EncodableValue()); + } + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface." + "FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + const auto& encodable_is_token_auto_refresh_enabled_arg = + args.at(1); + if (encodable_is_token_auto_refresh_enabled_arg.IsNull()) { + reply(WrapError( + "is_token_auto_refresh_enabled_arg unexpectedly null.")); + return; + } + const auto& is_token_auto_refresh_enabled_arg = + std::get(encodable_is_token_auto_refresh_enabled_arg); + api->SetTokenAutoRefreshEnabled( + app_name_arg, is_token_auto_refresh_enabled_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface." + "FirebaseAppCheckHostApi.registerTokenListener" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->RegisterTokenListener( + app_name_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface." + "FirebaseAppCheckHostApi.getLimitedUseAppCheckToken" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->GetLimitedUseAppCheckToken( + app_name_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } +} + +EncodableValue FirebaseAppCheckHostApi::WrapError( + std::string_view error_message) { + return EncodableValue( + EncodableList{EncodableValue(std::string(error_message)), + EncodableValue("Error"), EncodableValue()}); +} + +EncodableValue FirebaseAppCheckHostApi::WrapError(const FlutterError& error) { + return EncodableValue(EncodableList{EncodableValue(error.code()), + EncodableValue(error.message()), + error.details()}); +} + +} // namespace firebase_app_check_windows diff --git a/packages/firebase_app_check/firebase_app_check/windows/messages.g.h b/packages/firebase_app_check/firebase_app_check/windows/messages.g.h new file mode 100644 index 000000000000..50ae482963dc --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/messages.g.h @@ -0,0 +1,119 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. 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 (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_MESSAGES_G_H_ +#define PIGEON_MESSAGES_G_H_ +#include +#include +#include +#include + +#include +#include +#include + +namespace firebase_app_check_windows { + +// Generated class from Pigeon. + +class FlutterError { + public: + explicit FlutterError(const std::string& code) : code_(code) {} + explicit FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, + const ::flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const ::flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + ::flutter::EncodableValue details_; +}; + +template +class ErrorOr { + public: + ErrorOr(const T& rhs) : v_(rhs) {} + ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} + ErrorOr(const FlutterError& rhs) : v_(rhs) {} + ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {} + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class FirebaseAppCheckHostApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { + public: + PigeonInternalCodecSerializer(); + inline static PigeonInternalCodecSerializer& GetInstance() { + static PigeonInternalCodecSerializer sInstance; + return sInstance; + } + + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; + + protected: + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; +}; + +// Generated interface from Pigeon that represents a handler of messages from +// Flutter. +class FirebaseAppCheckHostApi { + public: + FirebaseAppCheckHostApi(const FirebaseAppCheckHostApi&) = delete; + FirebaseAppCheckHostApi& operator=(const FirebaseAppCheckHostApi&) = delete; + virtual ~FirebaseAppCheckHostApi() {} + virtual void Activate( + const std::string& app_name, const std::string* android_provider, + const std::string* apple_provider, const std::string* debug_token, + std::function reply)> result) = 0; + virtual void GetToken( + const std::string& app_name, bool force_refresh, + std::function> reply)> + result) = 0; + virtual void SetTokenAutoRefreshEnabled( + const std::string& app_name, bool is_token_auto_refresh_enabled, + std::function reply)> result) = 0; + virtual void RegisterTokenListener( + const std::string& app_name, + std::function reply)> result) = 0; + virtual void GetLimitedUseAppCheckToken( + const std::string& app_name, + std::function reply)> result) = 0; + + // The codec used by FirebaseAppCheckHostApi. + static const ::flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through + // the `binary_messenger`. + static void SetUp(::flutter::BinaryMessenger* binary_messenger, + FirebaseAppCheckHostApi* api); + static void SetUp(::flutter::BinaryMessenger* binary_messenger, + FirebaseAppCheckHostApi* api, + const std::string& message_channel_suffix); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + FirebaseAppCheckHostApi() = default; +}; +} // namespace firebase_app_check_windows +#endif // PIGEON_MESSAGES_G_H_ diff --git a/packages/firebase_app_check/firebase_app_check/windows/plugin_version.h.in b/packages/firebase_app_check/firebase_app_check/windows/plugin_version.h.in new file mode 100644 index 000000000000..0a52f845105a --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check/windows/plugin_version.h.in @@ -0,0 +1,13 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef PLUGIN_VERSION_CONFIG_H +#define PLUGIN_VERSION_CONFIG_H + +namespace firebase_app_check_windows { + +std::string getPluginVersion() { return "@PLUGIN_VERSION@"; } +} // namespace firebase_app_check_windows + +#endif // PLUGIN_VERSION_CONFIG_H diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/CHANGELOG.md b/packages/firebase_app_check/firebase_app_check_platform_interface/CHANGELOG.md index b582ef26dc26..00c7fb3e4937 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/CHANGELOG.md +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/CHANGELOG.md @@ -1,3 +1,28 @@ +## 0.4.1 + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + +## 0.4.0+2 + + - Update a dependency to the latest release. + +## 0.4.0+1 + + - Update a dependency to the latest release. + +## 0.4.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FIX**(app_check): fix an issue with debug token that would sometime not be passed properly ([#18258](https://github.com/firebase/flutterfire/issues/18258)). ([b0bc6e8f](https://github.com/firebase/flutterfire/commit/b0bc6e8f0e92aed2f3da99725eff85b3cf358282)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 0.3.0 + + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + ## 0.2.2 - **FEAT**(messaging,web): add support for debug tokens on Web ([#18057](https://github.com/firebase/flutterfire/issues/18057)). ([b853386e](https://github.com/firebase/flutterfire/commit/b853386e987d686eab4b8fd9b8dad14eda97479c)) diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart index c8292c8b7eaf..53a285e48729 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart @@ -10,3 +10,4 @@ export 'src/apple_providers.dart'; export 'src/method_channel/method_channel_firebase_app_check.dart'; export 'src/platform_interface/platform_interface_firebase_app_check.dart'; export 'src/web_providers.dart'; +export 'src/windows_providers.dart'; diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/android_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/android_providers.dart index c827b3184022..47640028475a 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/android_providers.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/android_providers.dart @@ -32,3 +32,10 @@ class AndroidDebugProvider extends AndroidAppCheckProvider { class AndroidPlayIntegrityProvider extends AndroidAppCheckProvider { const AndroidPlayIntegrityProvider() : super('playIntegrity'); } + +/// reCAPTCHA provider for Android. +/// +/// The site key is retrieved automatically from google-services.json. +class AndroidReCaptchaProvider extends AndroidAppCheckProvider { + const AndroidReCaptchaProvider() : super('recaptcha'); +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/apple_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/apple_providers.dart index 5e6af1ce2359..e768fca29477 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/apple_providers.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/apple_providers.dart @@ -54,3 +54,10 @@ class AppleAppAttestWithDeviceCheckFallbackProvider const AppleAppAttestWithDeviceCheckFallbackProvider() : super('appAttestWithDeviceCheckFallback'); } + +/// reCAPTCHA provider for Apple platforms. +/// +/// The site key is retrieved automatically from GoogleService-Info.plist. +class AppleReCaptchaProvider extends AppleAppCheckProvider { + const AppleReCaptchaProvider() : super('recaptcha'); +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart index 59f05fa8b527..b7f2d035d212 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart @@ -10,6 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import '../../firebase_app_check_platform_interface.dart'; +import '../pigeon/messages.pigeon.dart'; import 'utils/exception.dart'; import 'utils/provider_to_string.dart'; @@ -18,23 +19,32 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { MethodChannelFirebaseAppCheck({required FirebaseApp app}) : super(appInstance: app) { _tokenChangesListeners[app.name] = StreamController.broadcast(); + _listenerRegistration = _registerTokenListener(app); + } + + Future _registerTokenListener(FirebaseApp app) async { + try { + final channelName = await _pigeonApi.registerTokenListener(app.name); + if (_isDisposed) { + return; + } - channel.invokeMethod('FirebaseAppCheck#registerTokenListener', { - 'appName': app.name, - }).then((channelName) { - final events = EventChannel(channelName!, channel.codec); - events + final events = EventChannel(channelName); + _subscription = events .receiveGuardedBroadcastStream(onError: convertPlatformException) - .listen( - (arguments) { - // ignore: close_sinks - StreamController controller = - _tokenChangesListeners[app.name]!; + .listen((arguments) { + // ignore: close_sinks + final controller = _tokenChangesListeners[app.name]; + if (!_isDisposed && controller != null) { Map result = arguments; controller.add(result['token'] as String?); - }, - ); - }); + } + }); + // ignore: avoid_catches_without_on_clauses + } catch (_) { + // Silently ignore errors during token listener registration. + // This can happen in test environments where the host API is not set up. + } } static final Map> _tokenChangesListeners = @@ -44,10 +54,11 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { _methodChannelFirebaseAppCheckInstances = {}; - /// The [MethodChannel] used to communicate with the native plugin - static MethodChannel channel = const MethodChannel( - 'plugins.flutter.io/firebase_app_check', - ); + /// The Pigeon API used for platform communication. + final FirebaseAppCheckHostApi _pigeonApi = FirebaseAppCheckHostApi(); + late final Future _listenerRegistration; + StreamSubscription? _subscription; + bool _isDisposed = false; /// Returns a stub instance to allow the platform interface to access /// the class instance statically. @@ -68,6 +79,16 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { }); } + @override + Future dispose() async { + _isDisposed = true; + await _listenerRegistration; + await _subscription?.cancel(); + _subscription = null; + await _tokenChangesListeners.remove(app.name)?.close(); + _methodChannelFirebaseAppCheckInstances.remove(app.name); + } + @override MethodChannelFirebaseAppCheck setInitialValues() { return this; @@ -88,30 +109,31 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { AppleProvider? appleProvider, AndroidAppCheckProvider? providerAndroid, AppleAppCheckProvider? providerApple, + WindowsAppCheckProvider? providerWindows, }) async { try { - await channel.invokeMethod('FirebaseAppCheck#activate', { - 'appName': app.name, - // Allow value to pass for debug mode for unit testing - if (defaultTargetPlatform == TargetPlatform.android || kDebugMode) - 'androidProvider': getAndroidProviderString( - legacyProvider: androidProvider, - newProvider: providerAndroid, - ), - if (providerAndroid is AndroidDebugProvider && - providerAndroid.debugToken != null) - 'androidDebugToken': providerAndroid.debugToken, - if (defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.macOS || - kDebugMode) - 'appleProvider': getAppleProviderString( - legacyProvider: appleProvider, - newProvider: providerApple, - ), - if (providerApple is AppleDebugProvider && - providerApple.debugToken != null) - 'appleDebugToken': providerApple.debugToken, - }); + await _pigeonApi.activate( + app.name, + defaultTargetPlatform == TargetPlatform.android || kDebugMode + ? getAndroidProviderString( + legacyProvider: androidProvider, + newProvider: providerAndroid, + ) + : null, + defaultTargetPlatform == TargetPlatform.iOS || + defaultTargetPlatform == TargetPlatform.macOS || + kDebugMode + ? getAppleProviderString( + legacyProvider: appleProvider, + newProvider: providerApple, + ) + : null, + _getDebugToken( + providerAndroid: providerAndroid, + providerApple: providerApple, + providerWindows: providerWindows, + ), + ); } on PlatformException catch (e, s) { convertPlatformException(e, s); } @@ -120,12 +142,7 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { @override Future getToken(bool forceRefresh) async { try { - final result = await channel.invokeMethod( - 'FirebaseAppCheck#getToken', - {'appName': app.name, 'forceRefresh': forceRefresh}, - ); - - return result; + return await _pigeonApi.getToken(app.name, forceRefresh); } on PlatformException catch (e, s) { convertPlatformException(e, s); } @@ -136,12 +153,9 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { bool isTokenAutoRefreshEnabled, ) async { try { - await channel.invokeMethod( - 'FirebaseAppCheck#setTokenAutoRefreshEnabled', - { - 'appName': app.name, - 'isTokenAutoRefreshEnabled': isTokenAutoRefreshEnabled, - }, + await _pigeonApi.setTokenAutoRefreshEnabled( + app.name, + isTokenAutoRefreshEnabled, ); } on PlatformException catch (e, s) { convertPlatformException(e, s); @@ -156,16 +170,34 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { @override Future getLimitedUseToken() async { try { - final result = await channel.invokeMethod( - 'FirebaseAppCheck#getLimitedUseAppCheckToken', - { - 'appName': app.name, - }, - ); - - return result; + return await _pigeonApi.getLimitedUseAppCheckToken(app.name); } on PlatformException catch (e, s) { convertPlatformException(e, s); } } } + +String? _getDebugToken({ + AndroidAppCheckProvider? providerAndroid, + AppleAppCheckProvider? providerApple, + WindowsAppCheckProvider? providerWindows, +}) { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return providerAndroid is AndroidDebugProvider + ? providerAndroid.debugToken + : null; + case TargetPlatform.iOS: + case TargetPlatform.macOS: + return providerApple is AppleDebugProvider + ? providerApple.debugToken + : null; + case TargetPlatform.windows: + return providerWindows is WindowsDebugProvider + ? providerWindows.debugToken + : null; + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + return null; + } +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart new file mode 100644 index 000000000000..ca154bea1029 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -0,0 +1,175 @@ +// Copyright 2025, the Chromium project authors. Please see the AUTHORS file +// for details. 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 (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + +class FirebaseAppCheckHostApi { + /// Constructor for [FirebaseAppCheckHostApi]. 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. + FirebaseAppCheckHostApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future activate(String appName, String? androidProvider, + String? appleProvider, String? debugToken) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([appName, androidProvider, appleProvider, debugToken]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + } + + Future getToken(String appName, bool forceRefresh) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, forceRefresh]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + return pigeonVar_replyValue as String?; + } + + Future setTokenAutoRefreshEnabled( + String appName, bool isTokenAutoRefreshEnabled) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, isTokenAutoRefreshEnabled]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + } + + Future registerTokenListener(String appName) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; + } + + Future getLimitedUseAppCheckToken(String appName) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; + } +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart index 988d7e0b8e1f..3346544a3bed 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart @@ -67,6 +67,13 @@ abstract class FirebaseAppCheckPlatform extends PlatformInterface { /// "app attest with fallback to device check" via `AppleAppCheckProvider`. /// Note: App Attest is only available on iOS 14.0+ and macOS 14.0+. /// + /// **Windows**: Only the debug provider is supported. You **must** supply a + /// debug token — the desktop C++ SDK does not auto-generate one. Either pass + /// it via `providerWindows: WindowsDebugProvider(debugToken: 'your-token')` + /// or set the `APP_CHECK_DEBUG_TOKEN` environment variable. The token must + /// first be registered in the Firebase Console under + /// *App Check → Apps → Manage debug tokens*. + /// /// ## Migration Notice /// /// The `androidProvider` and `appleProvider` parameters will be deprecated @@ -89,6 +96,7 @@ abstract class FirebaseAppCheckPlatform extends PlatformInterface { AppleProvider? appleProvider, AndroidAppCheckProvider? providerAndroid, AppleAppCheckProvider? providerApple, + WindowsAppCheckProvider? providerWindows, }) { throw UnimplementedError('activate() is not implemented'); } @@ -139,4 +147,7 @@ abstract class FirebaseAppCheckPlatform extends PlatformInterface { FirebaseAppCheckPlatform setInitialValues() { throw UnimplementedError('setInitialValues() is not implemented'); } + + /// Disposes resources tied to this platform App Check instance. + Future dispose() async {} } diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/web_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/web_providers.dart index 55d5ab576c7e..6d4a59844cd4 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/web_providers.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/web_providers.dart @@ -5,7 +5,7 @@ abstract class WebProvider { final String siteKey; - WebProvider(this.siteKey); + const WebProvider(this.siteKey); } class ReCaptchaV3Provider extends WebProvider { @@ -31,3 +31,8 @@ class WebDebugProvider extends WebProvider { /// The debug token for this provider. final String? debugToken; } + +/// reCAPTCHA Enterprise provider for Web that does not use the enterprise name and does not take in a siteKey. +class WebReCaptchaProvider extends WebProvider { + const WebReCaptchaProvider() : super(''); +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart new file mode 100644 index 000000000000..b6b09e55b20b --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart @@ -0,0 +1,44 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Base class for Windows App Check providers. +/// +/// On Windows, only the [WindowsDebugProvider] is supported. The Firebase C++ +/// SDK does not support platform attestation providers (such as Play Integrity +/// or DeviceCheck) on desktop platforms. +abstract class WindowsAppCheckProvider { + final String type; + const WindowsAppCheckProvider(this.type); +} + +/// Debug provider for Windows. +/// +/// This is the **only** provider available on Windows. Unlike mobile platforms, +/// the desktop C++ SDK does **not** auto-generate a debug token. You must +/// supply one explicitly. +/// +/// ## Setup +/// +/// 1. Generate a debug token (a UUID v4) — for example using an online +/// generator or a CLI tool. +/// 2. Register it in the **Firebase Console** under +/// *App Check → Apps → Manage debug tokens*. +/// 3. Pass it here via [debugToken], **or** set the `APP_CHECK_DEBUG_TOKEN` +/// environment variable before launching your app. +/// +/// If neither a [debugToken] nor the environment variable is provided, +/// `getToken()` will fail with an `invalid-configuration` error. +/// +/// **Do not ship the debug provider or debug tokens in production builds.** +class WindowsDebugProvider extends WindowsAppCheckProvider { + /// Creates a Windows debug provider. + /// + /// [debugToken] is the debug token registered in the Firebase Console. + /// If omitted, the C++ SDK falls back to the `APP_CHECK_DEBUG_TOKEN` + /// environment variable. + const WindowsDebugProvider({this.debugToken}) : super('debug'); + + /// The debug token for this provider. + final String? debugToken; +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/copyright.txt b/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/copyright.txt new file mode 100644 index 000000000000..4e197781c6db --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/copyright.txt @@ -0,0 +1,3 @@ +Copyright 2025, the Chromium project authors. Please see the AUTHORS file +for details. All rights reserved. Use of this source code is governed by a +BSD-style license that can be found in the LICENSE file. \ No newline at end of file diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/messages.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/messages.dart new file mode 100644 index 000000000000..e84ff78ab5f4 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/messages.dart @@ -0,0 +1,48 @@ +// Copyright 2025 The Chromium Authors. 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/pigeon/messages.pigeon.dart', + dartPackageName: 'firebase_app_check_platform_interface', + kotlinOut: + '../firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt', + kotlinOptions: KotlinOptions( + package: 'io.flutter.plugins.firebase.appcheck', + ), + swiftOut: + '../firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift', + cppHeaderOut: '../firebase_app_check/windows/messages.g.h', + cppSourceOut: '../firebase_app_check/windows/messages.g.cpp', + cppOptions: CppOptions(namespace: 'firebase_app_check_windows'), + copyrightHeader: 'pigeons/copyright.txt', + ), +) +@HostApi(dartHostTestHandler: 'TestFirebaseAppCheckHostApi') +abstract class FirebaseAppCheckHostApi { + @async + void activate( + String appName, + String? androidProvider, + String? appleProvider, + String? debugToken, + ); + + @async + String? getToken(String appName, bool forceRefresh); + + @async + void setTokenAutoRefreshEnabled( + String appName, + bool isTokenAutoRefreshEnabled, + ); + + @async + String registerTokenListener(String appName); + + @async + String getLimitedUseAppCheckToken(String appName); +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/pubspec.yaml b/packages/firebase_app_check/firebase_app_check_platform_interface/pubspec.yaml index 98f24a09895a..ee66bbb1fca0 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/pubspec.yaml +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/pubspec.yaml @@ -1,22 +1,24 @@ name: firebase_app_check_platform_interface description: A common platform interface for the firebase_app_check plugin. homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_check/firebase_app_check_platform_interface -version: 0.2.2 +version: 0.4.1 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 + pigeon: 26.3.4 diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart index 721dc2de0b7c..7c60c253ff3d 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart'; +import 'package:firebase_app_check_platform_interface/src/pigeon/messages.pigeon.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/services.dart'; @@ -13,13 +13,11 @@ import '../mock.dart'; void main() { setupFirebaseAppCheckMocks(); - late FirebaseAppCheckPlatform appCheck; late FirebaseApp secondaryApp; - final List methodCallLogger = []; group('$MethodChannelFirebaseAppCheck', () { setUpAll(() async { - FirebaseApp app = await Firebase.initializeApp(); + await Firebase.initializeApp(); secondaryApp = await Firebase.initializeApp( name: 'secondaryApp', options: const FirebaseOptions( @@ -29,28 +27,20 @@ void main() { messagingSenderId: '1234567890', ), ); - handleMethodCall((call) async { - methodCallLogger.add(call); - - switch (call.method) { - case 'FirebaseAppCheck#registerTokenListener': - return 'channelName'; - case 'FirebaseAppCheck#getToken': - return 'test-token'; - default: - return true; - } - }); - - appCheck = MethodChannelFirebaseAppCheck(app: app); }); - setUp(() async { - methodCallLogger.clear(); + tearDown(() { + debugDefaultTargetPlatformOverride = null; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + null, + ); }); group('delegateFor()', () { test('returns a [FirebaseAppCheckPlatform]', () { + final appCheck = FirebaseAppCheckPlatform.instance; expect( // ignore: invalid_use_of_protected_member appCheck.delegateFor(app: secondaryApp), @@ -61,96 +51,131 @@ void main() { group('setInitialValues()', () { test('returns a [MethodChannelFirebaseAppCheck]', () { + final appCheck = MethodChannelFirebaseAppCheck.instance; // ignore: invalid_use_of_protected_member expect(appCheck.setInitialValues(), appCheck); }); }); - test('activate', () async { - await appCheck.activate( - webProvider: ReCaptchaV3Provider('test-key'), - providerAndroid: const AndroidPlayIntegrityProvider(), - providerApple: const AppleDeviceCheckProvider(), - ); - expect( - methodCallLogger, - [ - isMethodCall( - 'FirebaseAppCheck#activate', - arguments: { - 'appName': defaultFirebaseAppName, - 'androidProvider': 'playIntegrity', - 'appleProvider': 'deviceCheck', - }, - ), - ], - ); - }); + group('activate()', () { + test('passes the Apple debug token on Apple platforms', () async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + final calls = >[]; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + (ByteData? message) async { + calls.add( + FirebaseAppCheckHostApi.pigeonChannelCodec.decodeMessage(message)! + as List, + ); + return FirebaseAppCheckHostApi.pigeonChannelCodec.encodeMessage( + [], + ); + }, + ); - test('activate with debug providers', () async { - await appCheck.activate( - webProvider: ReCaptchaV3Provider('test-key'), - providerAndroid: const AndroidDebugProvider(debugToken: 'androidDebug'), - providerApple: const AppleDebugProvider(debugToken: 'appleDebug'), - ); - expect( - methodCallLogger, - [ - isMethodCall( - 'FirebaseAppCheck#activate', - arguments: { - 'appName': defaultFirebaseAppName, - 'androidProvider': 'debug', - 'appleProvider': 'debug', - 'androidDebugToken': 'androidDebug', - 'appleDebugToken': 'appleDebug', - }, - ), - ], - ); - }); + final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); - test('getToken', () async { - final tokenResult = await appCheck.getToken(true); - expect( - methodCallLogger, - [ - isMethodCall( - 'FirebaseAppCheck#getToken', - arguments: { - 'appName': defaultFirebaseAppName, - 'forceRefresh': true, - }, + await appCheck.activate( + providerAndroid: const AndroidDebugProvider( + debugToken: 'android-debug-token', ), - ], - ); + providerApple: const AppleDebugProvider( + debugToken: 'apple-debug-token', + ), + ); - expect(tokenResult, isA()); - }); + expect(calls, hasLength(1)); + expect(calls.single[3], 'apple-debug-token'); + }); + + test('passes the Android debug token on Android', () async { + debugDefaultTargetPlatformOverride = TargetPlatform.android; + final calls = >[]; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + (ByteData? message) async { + calls.add( + FirebaseAppCheckHostApi.pigeonChannelCodec.decodeMessage(message)! + as List, + ); + return FirebaseAppCheckHostApi.pigeonChannelCodec.encodeMessage( + [], + ); + }, + ); - test('setTokenAutoRefreshEnabled', () async { - await appCheck.setTokenAutoRefreshEnabled(false); - expect( - methodCallLogger, - [ - isMethodCall( - 'FirebaseAppCheck#setTokenAutoRefreshEnabled', - arguments: { - 'appName': defaultFirebaseAppName, - 'isTokenAutoRefreshEnabled': false, - }, + final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); + + await appCheck.activate( + providerAndroid: const AndroidDebugProvider( + debugToken: 'android-debug-token', ), - ], - ); + providerApple: const AppleDebugProvider( + debugToken: 'apple-debug-token', + ), + ); + + expect(calls, hasLength(1)); + expect(calls.single[3], 'android-debug-token'); + }); }); - test('tokenChanges', () async { - final stream = appCheck.onTokenChange; - expect(stream, isA>()); + group('activate() with Recaptcha', () { + test('passes recaptcha on Android', () async { + final appCheck = MethodChannelFirebaseAppCheck(app: Firebase.app()); + + final List log = []; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + (message) async { + final list = const StandardMessageCodec().decodeMessage(message) + as List; + log.add(list); + return const StandardMessageCodec() + .encodeMessage([null]); // Return success + }, + ); + + await appCheck.activate( + providerAndroid: const AndroidReCaptchaProvider(), + ); + + expect(log.length, 1); + expect(log[0][0], '[DEFAULT]'); // appName + expect(log[0][1], 'recaptcha'); // androidProvider + }); + + test('passes recaptcha on iOS', () async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + final appCheck = MethodChannelFirebaseAppCheck(app: Firebase.app()); + + final List log = []; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + (message) async { + final list = const StandardMessageCodec().decodeMessage(message) + as List; + log.add(list); + return const StandardMessageCodec() + .encodeMessage([null]); // Return success + }, + ); + + await appCheck.activate( + providerApple: const AppleReCaptchaProvider(), + ); + + expect(log.length, 1); + expect(log[0][0], '[DEFAULT]'); // appName + expect(log[0][2], 'recaptcha'); // appleProvider + }); }); }); } - -class TestMethodChannelFirebaseAppCheck extends MethodChannelFirebaseAppCheck { - TestMethodChannelFirebaseAppCheck(FirebaseApp app) : super(app: app); -} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/test/mock.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/test/mock.dart index 9cefc319fe4c..454b7253292b 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/test/mock.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/test/mock.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart'; -import 'package:firebase_app_check_platform_interface/src/method_channel/method_channel_firebase_app_check.dart'; import 'package:firebase_core_platform_interface/test.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -17,21 +15,4 @@ void setupFirebaseAppCheckMocks([Callback? customHandlers]) { TestWidgetsFlutterBinding.ensureInitialized(); setupFirebaseCoreMocks(); - - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(MethodChannelFirebaseAppCheck.channel, - (MethodCall methodCall) async { - methodCallLog.add(methodCall); - switch (methodCall.method) { - default: - return false; - } - }); } - -void handleMethodCall(MethodCallCallback methodCallCallback) => - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(MethodChannelFirebaseAppCheck.channel, - (call) async { - return await methodCallCallback(call); - }); diff --git a/packages/firebase_app_check/firebase_app_check_web/CHANGELOG.md b/packages/firebase_app_check/firebase_app_check_web/CHANGELOG.md index f5dd0b7fa0b2..fd2e9954aac8 100644 --- a/packages/firebase_app_check/firebase_app_check_web/CHANGELOG.md +++ b/packages/firebase_app_check/firebase_app_check_web/CHANGELOG.md @@ -1,3 +1,24 @@ +## 0.2.5 + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + +## 0.2.4+3 + + - Update a dependency to the latest release. + +## 0.2.4+2 + + - Update a dependency to the latest release. + +## 0.2.4+1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.2.4 + + - **FIX**(app_check,web): fix an error that could occur when refreshing a token ([#18135](https://github.com/firebase/flutterfire/issues/18135)). ([6998e512](https://github.com/firebase/flutterfire/commit/6998e512ea5404a20ad81a0306aafaa607babc2a)) + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + ## 0.2.3 - **FEAT**(messaging,web): add support for debug tokens on Web ([#18057](https://github.com/firebase/flutterfire/issues/18057)). ([b853386e](https://github.com/firebase/flutterfire/commit/b853386e987d686eab4b8fd9b8dad14eda97479c)) diff --git a/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart b/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart index 8524c3a1352c..f4cd415164c8 100644 --- a/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart +++ b/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart @@ -23,6 +23,7 @@ class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform { static const recaptchaTypeV3 = 'recaptcha-v3'; static const recaptchaTypeEnterprise = 'enterprise'; static const recaptchaTypeDebug = 'debug'; + static const recaptchaTypeWebRecaptcha = 'web-recaptcha'; static Map> _tokenChangesListeners = {}; /// Stub initializer to allow the [registerWith] to create an instance without @@ -63,6 +64,8 @@ class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform { final debugToken = recaptchaSiteKey?.isNotEmpty ?? false ? recaptchaSiteKey : null; provider = WebDebugProvider(debugToken: debugToken); + } else if (recaptchaType == recaptchaTypeWebRecaptcha) { + provider = const WebReCaptchaProvider(); } else if (recaptchaSiteKey != null) { if (recaptchaType == recaptchaTypeV3) { provider = ReCaptchaV3Provider(recaptchaSiteKey); @@ -132,6 +135,7 @@ class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform { AppleProvider? appleProvider, AndroidAppCheckProvider? providerAndroid, AppleAppCheckProvider? providerApple, + WindowsAppCheckProvider? providerWindows, }) async { // save the recaptcha type and site key for future startups if (webProvider != null) { @@ -142,6 +146,8 @@ class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform { recaptchaType = recaptchaTypeV3; } else if (webProvider is ReCaptchaEnterpriseProvider) { recaptchaType = recaptchaTypeEnterprise; + } else if (webProvider is WebReCaptchaProvider) { + recaptchaType = recaptchaTypeWebRecaptcha; } else { throw Exception('Invalid web provider: $webProvider'); } diff --git a/packages/firebase_app_check/firebase_app_check_web/lib/src/firebase_app_check_version.dart b/packages/firebase_app_check/firebase_app_check_web/lib/src/firebase_app_check_version.dart index e538fa901e42..5e2793aa9166 100644 --- a/packages/firebase_app_check/firebase_app_check_web/lib/src/firebase_app_check_version.dart +++ b/packages/firebase_app_check/firebase_app_check_web/lib/src/firebase_app_check_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '0.4.2'; +const packageVersion = '0.4.5'; diff --git a/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check.dart b/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check.dart index c35a5d1d8d16..a3d2b896e96b 100644 --- a/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check.dart +++ b/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check.dart @@ -16,9 +16,11 @@ export 'app_check_interop.dart'; /// Given an AppJSImp, return the AppCheck instance. AppCheck? getAppCheckInstance([App? app, WebProvider? provider]) { - late app_check_interop.ReCaptchaProvider jsProvider; + app_check_interop.ReCaptchaProvider? jsProvider; - if (provider is WebDebugProvider) { + if (provider == null || provider is WebReCaptchaProvider) { + jsProvider = null; + } else if (provider is WebDebugProvider) { // Set the debug token global before initializing App Check. // The Firebase JS SDK reads this and creates a DebugProvider internally. if (provider.debugToken != null) { diff --git a/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check_interop.dart b/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check_interop.dart index a72b2cf0c2a4..1afb3a69b181 100644 --- a/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check_interop.dart +++ b/packages/firebase_app_check/firebase_app_check_web/lib/src/interop/app_check_interop.dart @@ -74,14 +74,14 @@ extension type AppCheckTokenResultJsImpl._(JSObject _) implements JSObject { class AppCheckOptions { external factory AppCheckOptions({ JSBoolean? isTokenAutoRefreshEnabled, - ReCaptchaProvider provider, + ReCaptchaProvider? provider, }); } extension AppCheckOptionsJsImplX on AppCheckOptions { external JSBoolean? get isTokenAutoRefreshEnabled; - external ReCaptchaProvider get provider; + external ReCaptchaProvider? get provider; } extension type AppCheckJsImpl._(JSObject _) implements JSObject { diff --git a/packages/firebase_app_check/firebase_app_check_web/pubspec.yaml b/packages/firebase_app_check/firebase_app_check_web/pubspec.yaml index 264586f2958a..2e52290f7f64 100644 --- a/packages/firebase_app_check/firebase_app_check_web/pubspec.yaml +++ b/packages/firebase_app_check/firebase_app_check_web/pubspec.yaml @@ -1,17 +1,18 @@ name: firebase_app_check_web description: The web implementation of firebase_app_check homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_check/firebase_app_check_web -version: 0.2.3 +version: 0.2.5 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_app_check_platform_interface: ^0.2.2 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 + _flutterfire_internals: ^1.3.73 + firebase_app_check_platform_interface: ^0.4.1 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 flutter: sdk: flutter flutter_web_plugins: @@ -20,7 +21,7 @@ dependencies: dev_dependencies: build_runner: ^2.3.3 - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.dart b/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.dart index dd91f9bde80a..f549b3419891 100644 --- a/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.dart +++ b/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.dart @@ -58,6 +58,19 @@ void main() { verifyNoMoreInteractions(appCheck); }); + test('activate with WebReCaptchaProvider', () async { + const provider = WebReCaptchaProvider(); + await appCheck.activate( + webProvider: provider, + ); + verify( + appCheck.activate( + webProvider: provider, + ), + ); + verifyNoMoreInteractions(appCheck); + }); + test('getToken', () async { await appCheck.getToken(true); verify(appCheck.getToken(true)); diff --git a/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart b/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart index 122699cb4b89..357b30beeca5 100644 --- a/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart +++ b/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart @@ -141,6 +141,7 @@ class MockFirebaseAppCheckWeb extends _i1.Mock _i3.AppleProvider? appleProvider, _i3.AndroidAppCheckProvider? providerAndroid, _i3.AppleAppCheckProvider? providerApple, + _i3.WindowsAppCheckProvider? providerWindows, }) => (super.noSuchMethod( Invocation.method( @@ -152,6 +153,7 @@ class MockFirebaseAppCheckWeb extends _i1.Mock #appleProvider: appleProvider, #providerAndroid: providerAndroid, #providerApple: providerApple, + #providerWindows: providerWindows, }, ), returnValue: _i5.Future.value(), diff --git a/packages/firebase_app_installations/firebase_app_installations/CHANGELOG.md b/packages/firebase_app_installations/firebase_app_installations/CHANGELOG.md index fec1198534b0..387cd4d64fe3 100644 --- a/packages/firebase_app_installations/firebase_app_installations/CHANGELOG.md +++ b/packages/firebase_app_installations/firebase_app_installations/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.4.2+4 + + - Update a dependency to the latest release. + +## 0.4.2+3 + + - Update a dependency to the latest release. + +## 0.4.2+2 + + - Update a dependency to the latest release. + +## 0.4.2+1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.4.2 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 0.4.1 - **FEAT**(ios): migrate iOS to UIScene lifecycle ([#18054](https://github.com/firebase/flutterfire/issues/18054)). ([3ffa4110](https://github.com/firebase/flutterfire/commit/3ffa411098132fd5182a84be4e7a226106bc7451)) diff --git a/packages/firebase_app_installations/firebase_app_installations/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/installations/example/MainActivity.kt b/packages/firebase_app_installations/firebase_app_installations/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/installations/example/MainActivity.kt index 6f76296229d1..a8d726fe6633 100644 --- a/packages/firebase_app_installations/firebase_app_installations/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/installations/example/MainActivity.kt +++ b/packages/firebase_app_installations/firebase_app_installations/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/installations/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.installations.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_app_installations/firebase_app_installations/example/ios/Runner/AppDelegate.swift b/packages/firebase_app_installations/firebase_app_installations/example/ios/Runner/AppDelegate.swift index b6363034812b..626664468b89 100644 --- a/packages/firebase_app_installations/firebase_app_installations/example/ios/Runner/AppDelegate.swift +++ b/packages/firebase_app_installations/firebase_app_installations/example/ios/Runner/AppDelegate.swift @@ -1,5 +1,5 @@ -import UIKit import Flutter +import UIKit @main @objc class AppDelegate: FlutterAppDelegate { diff --git a/packages/firebase_app_installations/firebase_app_installations/example/lib/main.dart b/packages/firebase_app_installations/firebase_app_installations/example/lib/main.dart index 56332b8cbe82..eb64f6a490f1 100644 --- a/packages/firebase_app_installations/firebase_app_installations/example/lib/main.dart +++ b/packages/firebase_app_installations/firebase_app_installations/example/lib/main.dart @@ -72,7 +72,7 @@ class _InstallationsCardState extends State { String id = 'None'; String authToken = 'None'; - init() async { + Future init() async { await getId(); await getAuthToken(); } @@ -101,7 +101,7 @@ class _InstallationsCardState extends State { } } - Future getAuthToken([forceRefresh = false]) async { + Future getAuthToken([bool forceRefresh = false]) async { try { final token = await FirebaseInstallations.instance.getToken(forceRefresh); setState(() { diff --git a/packages/firebase_app_installations/firebase_app_installations/example/pubspec.yaml b/packages/firebase_app_installations/firebase_app_installations/example/pubspec.yaml index d70a3719ac82..56d8d3b8a94f 100644 --- a/packages/firebase_app_installations/firebase_app_installations/example/pubspec.yaml +++ b/packages/firebase_app_installations/firebase_app_installations/example/pubspec.yaml @@ -4,18 +4,20 @@ description: A new Flutter project. publish_to: 'none' version: 1.0.0+1 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_app_installations: ^0.4.1 + firebase_core: ^4.11.0 + firebase_app_installations: ^0.4.2+4 flutter: sdk: flutter dev_dependencies: - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 flutter: uses-material-design: true diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift index 307f4f583d63..9227ec496045 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_app_installations", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]), + .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/Constants.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/Constants.swift index 96fe962609a3..2e6a3b8195d8 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/Constants.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/Constants.swift @@ -3,4 +3,4 @@ // found in the LICENSE file. /// Auto-generated file. Do not edit. -public let versionNumber = "0.4.1" +public let versionNumber = "0.4.2+4" diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift index fb9ed2a06894..eb1d365a1f7e 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebaseInstallations + #if canImport(FlutterMacOS) import FlutterMacOS #else @@ -13,7 +15,6 @@ #else import firebase_core_shared #endif -import FirebaseInstallations let kFLTFirebaseInstallationsChannelName = "plugins.flutter.io/firebase_app_installations" @@ -75,8 +76,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F /// - Parameter arguments: the arguments passed by the Dart calling method /// - Parameter result: the result instance used to send the result to Dart. /// - Parameter errorBlock: the error block used to send the error to Dart. - private func getId(arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) { + private func getId( + arguments: NSDictionary, result: @escaping FlutterResult, + errorBlock: @escaping FLTFirebaseMethodCallErrorBlock + ) { let instance = getInstallations(appName: arguments["appName"] as! String) instance.installationID { (id: String?, error: Error?) in if let error { @@ -91,8 +94,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F /// - Parameter arguments: the arguments passed by the Dart calling method /// - Parameter result: the result instance used to send the result to Dart. /// - Parameter errorBlock: the error block used to send the error to Dart. - private func deleteId(arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) { + private func deleteId( + arguments: NSDictionary, result: @escaping FlutterResult, + errorBlock: @escaping FLTFirebaseMethodCallErrorBlock + ) { let instance = getInstallations(appName: arguments["appName"] as! String) instance.delete { (error: Error?) in if let error { @@ -107,18 +112,23 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F /// - Parameter arguments: the arguments passed by the Dart calling method /// - Parameter result: the result instance used to send the result to Dart. /// - Parameter errorBlock: the error block used to send the error to Dart. - private func getToken(arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) { + private func getToken( + arguments: NSDictionary, result: @escaping FlutterResult, + errorBlock: @escaping FLTFirebaseMethodCallErrorBlock + ) { let instance = getInstallations(appName: arguments["appName"] as! String) let forceRefresh = arguments["forceRefresh"] as? Bool ?? false instance - .authTokenForcingRefresh(forceRefresh) { (tokenResult: InstallationsAuthTokenResult?, - error: Error?) in - if let error { - errorBlock(nil, nil, nil, error) - } else { - result(tokenResult?.authToken) - } + .authTokenForcingRefresh(forceRefresh) { + ( + tokenResult: InstallationsAuthTokenResult?, + error: Error? + ) in + if let error { + errorBlock(nil, nil, nil, error) + } else { + result(tokenResult?.authToken) + } } } @@ -126,8 +136,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F /// - Parameter arguments: the arguments passed by the Dart calling method /// - Parameter result: the result instance used to send the result to Dart. /// - Parameter errorBlock: the error block used to send the error to Dart. - private func registerIdChangeListener(arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock) { + private func registerIdChangeListener( + arguments: NSDictionary, result: @escaping FlutterResult, + errorBlock: @escaping FLTFirebaseMethodCallErrorBlock + ) { let instance = getInstallations(appName: arguments["appName"] as! String) let appName = arguments["appName"] as! String let eventChannelName = kFLTFirebaseInstallationsChannelName + "/token/" + appName @@ -144,8 +156,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } private func mapInstallationsErrorCodes(code: UInt) -> NSString { - let error = InstallationsErrorCode(InstallationsErrorCode - .Code(rawValue: Int(code)) ?? InstallationsErrorCode.unknown) + let error = InstallationsErrorCode( + InstallationsErrorCode + .Code(rawValue: Int(code)) ?? InstallationsErrorCode.unknown + ) switch error { case InstallationsErrorCode.invalidConfiguration: @@ -163,37 +177,49 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { guard let args = call.arguments as? NSDictionary else { - result(FlutterError( - code: "invalid-arguments", - message: "Arguments are not a dictionary", - details: nil - )) + result( + FlutterError( + code: "invalid-arguments", + message: "Arguments are not a dictionary", + details: nil + ) + ) return } - let errorBlock: FLTFirebaseMethodCallErrorBlock = { (code, message, details, - error: Error?) in - var errorDetails = [String: Any?]() - - errorDetails["code"] = code ?? self - .mapInstallationsErrorCodes(code: UInt((error! as NSError).code)) - errorDetails["message"] = message ?? error? - .localizedDescription ?? "An unknown error has occurred." - errorDetails["additionalData"] = details - - if code == "unknown" { - NSLog( - "FLTFirebaseInstallations: An error occurred while calling method %@", - call.method - ) - } + let errorBlock: FLTFirebaseMethodCallErrorBlock = { + ( + code, message, details, + error: Error? + ) in + var errorDetails = [String: Any?]() + + errorDetails["code"] = + code + ?? self + .mapInstallationsErrorCodes(code: UInt((error! as NSError).code)) + errorDetails["message"] = + message ?? error? + .localizedDescription ?? "An unknown error has occurred." + errorDetails["additionalData"] = details + + if code == "unknown" { + NSLog( + "FLTFirebaseInstallations: An error occurred while calling method %@", + call.method + ) + } - result(FLTFirebasePlugin.createFlutterError(fromCode: errorDetails["code"] as! String, - message: errorDetails["message"] as! String, - optionalDetails: errorDetails[ - "additionalData" - ] as? [AnyHashable: Any], - andOptionalNSError: error)) + result( + FLTFirebasePlugin.createFlutterError( + fromCode: errorDetails["code"] as! String, + message: errorDetails["message"] as! String, + optionalDetails: errorDetails[ + "additionalData" + ] as? [AnyHashable: Any], + andOptionalNSError: error + ) + ) } switch call.method { diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift index 6d07257dfee8..89f9cbc2e4e9 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebaseInstallations +import Foundation + #if canImport(FlutterMacOS) import FlutterMacOS #else import Flutter #endif -import FirebaseInstallations -import Foundation - class IdChangedStreamHandler: NSObject, FlutterStreamHandler { var eventSink: FlutterEventSink? var installationIDObserver: NSObjectProtocol? @@ -33,11 +33,13 @@ class IdChangedStreamHandler: NSObject, FlutterStreamHandler { guard let self else { return } if let error { - self.eventSink?(FlutterError( - code: "unknown", - message: error.localizedDescription, - details: ["code": "unknown", "message": error.localizedDescription] - )) + self.eventSink?( + FlutterError( + code: "unknown", + message: error.localizedDescription, + details: ["code": "unknown", "message": error.localizedDescription] + ) + ) } else if let newId, newId != self.installationsId { self.installationsId = newId self.eventSink?(["token": self.installationsId]) @@ -45,8 +47,10 @@ class IdChangedStreamHandler: NSObject, FlutterStreamHandler { } } - func onListen(withArguments _: Any?, - eventSink events: @escaping FlutterEventSink) -> FlutterError? { + func onListen( + withArguments _: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { eventSink = events installationIDObserver = NotificationCenter.default.addObserver( diff --git a/packages/firebase_app_installations/firebase_app_installations/lib/firebase_app_installations.dart b/packages/firebase_app_installations/firebase_app_installations/lib/firebase_app_installations.dart index 2bfd24bab7b3..44a0e46bd29c 100644 --- a/packages/firebase_app_installations/firebase_app_installations/lib/firebase_app_installations.dart +++ b/packages/firebase_app_installations/firebase_app_installations/lib/firebase_app_installations.dart @@ -5,6 +5,6 @@ import 'package:firebase_app_installations_platform_interface/firebase_app_installations_platform_interface.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; part 'src/firebase_app_installations.dart'; diff --git a/packages/firebase_app_installations/firebase_app_installations/lib/src/firebase_app_installations.dart b/packages/firebase_app_installations/firebase_app_installations/lib/src/firebase_app_installations.dart index 627386b104e6..fa21dd564810 100644 --- a/packages/firebase_app_installations/firebase_app_installations/lib/src/firebase_app_installations.dart +++ b/packages/firebase_app_installations/firebase_app_installations/lib/src/firebase_app_installations.dart @@ -4,7 +4,7 @@ part of '../firebase_app_installations.dart'; -class FirebaseInstallations extends FirebasePluginPlatform { +class FirebaseInstallations extends FirebasePlugin { FirebaseInstallations._({required this.app}) : super(app.name, 'plugins.flutter.io/firebase_app_installations'); diff --git a/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift index 1a47ca9ae6e5..2e91bb5f3819 100644 --- a/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift +++ b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_app_installations", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]), + .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_app_installations/firebase_app_installations/pubspec.yaml b/packages/firebase_app_installations/firebase_app_installations/pubspec.yaml index 4339fc3b1eb0..76cf8f419538 100644 --- a/packages/firebase_app_installations/firebase_app_installations/pubspec.yaml +++ b/packages/firebase_app_installations/firebase_app_installations/pubspec.yaml @@ -1,6 +1,7 @@ name: firebase_app_installations description: A Flutter plugin allowing you to use Firebase Installations. -version: 0.4.1 +version: 0.4.2+4 +resolution: workspace homepage: https://firebase.google.com/docs/projects/manage-installations#flutter repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_installations/firebase_app_installations topics: @@ -13,14 +14,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_app_installations_platform_interface: ^0.1.4+67 - firebase_app_installations_web: ^0.1.7+4 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_app_installations_platform_interface: ^0.1.4+72 + firebase_app_installations_web: ^0.1.7+9 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/CHANGELOG.md b/packages/firebase_app_installations/firebase_app_installations_platform_interface/CHANGELOG.md index eee03dc13120..093953d2ece0 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/CHANGELOG.md +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.1.4+72 + + - Update a dependency to the latest release. + +## 0.1.4+71 + + - Update a dependency to the latest release. + +## 0.1.4+70 + + - Update a dependency to the latest release. + +## 0.1.4+69 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.1.4+68 + + - Update a dependency to the latest release. + ## 0.1.4+67 - Update a dependency to the latest release. diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/firebase_app_installations_platform_interface.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/firebase_app_installations_platform_interface.dart index 1c0e99358cfb..2dacec194481 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/firebase_app_installations_platform_interface.dart +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/firebase_app_installations_platform_interface.dart @@ -2,6 +2,4 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -library firebase_app_installations_platform_interface; - export 'src/platform_interface/firebase_app_installations_platform_interface.dart'; diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml index 71d312a8e9c4..6710e216bc45 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml @@ -1,23 +1,24 @@ name: firebase_app_installations_platform_interface description: A common platform interface for the firebase_app_installations plugin. -version: 0.1.4+67 +version: 0.1.4+72 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_installations/firebase_app_installations_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_installations/firebase_app_installations_platform_interface environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 diff --git a/packages/firebase_app_installations/firebase_app_installations_web/CHANGELOG.md b/packages/firebase_app_installations/firebase_app_installations_web/CHANGELOG.md index cc905206ef16..75ae2b8f134f 100644 --- a/packages/firebase_app_installations/firebase_app_installations_web/CHANGELOG.md +++ b/packages/firebase_app_installations/firebase_app_installations_web/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.1.7+9 + + - Update a dependency to the latest release. + +## 0.1.7+8 + + - Update a dependency to the latest release. + +## 0.1.7+7 + + - Update a dependency to the latest release. + +## 0.1.7+6 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.1.7+5 + + - Update a dependency to the latest release. + ## 0.1.7+4 - Update a dependency to the latest release. diff --git a/packages/firebase_app_installations/firebase_app_installations_web/lib/src/firebase_app_installations_version.dart b/packages/firebase_app_installations/firebase_app_installations_web/lib/src/firebase_app_installations_version.dart index 56ae76ebdc1a..e26978d18006 100644 --- a/packages/firebase_app_installations/firebase_app_installations_web/lib/src/firebase_app_installations_version.dart +++ b/packages/firebase_app_installations/firebase_app_installations_web/lib/src/firebase_app_installations_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '0.4.1'; +const packageVersion = '0.4.2+4'; diff --git a/packages/firebase_app_installations/firebase_app_installations_web/lib/src/interop/installations_interop.dart b/packages/firebase_app_installations/firebase_app_installations_web/lib/src/interop/installations_interop.dart index 64a899e4e9fb..00f72ab66cd8 100644 --- a/packages/firebase_app_installations/firebase_app_installations_web/lib/src/interop/installations_interop.dart +++ b/packages/firebase_app_installations/firebase_app_installations_web/lib/src/interop/installations_interop.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. @JS('firebase_installations') -library firebase_interop.installations; +library; import 'dart:js_interop'; diff --git a/packages/firebase_app_installations/firebase_app_installations_web/pubspec.yaml b/packages/firebase_app_installations/firebase_app_installations_web/pubspec.yaml index 60b1c3954790..b7d74891d862 100644 --- a/packages/firebase_app_installations/firebase_app_installations_web/pubspec.yaml +++ b/packages/firebase_app_installations/firebase_app_installations_web/pubspec.yaml @@ -1,28 +1,29 @@ name: firebase_app_installations_web description: The web implementation of firebase_app_installations. -version: 0.1.7+4 +version: 0.1.7+9 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_installations/firebase_app_installations_web repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_app_installations/firebase_app_installations_web environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_app_installations_platform_interface: ^0.1.4+67 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 + _flutterfire_internals: ^1.3.73 + firebase_app_installations_platform_interface: ^0.1.4+72 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 flutter: sdk: flutter flutter_web_plugins: sdk: flutter dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 flutter: plugin: diff --git a/packages/firebase_auth/analysis_options.yaml b/packages/firebase_auth/analysis_options.yaml index 8b3451b56a01..6aa2853bfc0a 100644 --- a/packages/firebase_auth/analysis_options.yaml +++ b/packages/firebase_auth/analysis_options.yaml @@ -9,3 +9,4 @@ analyzer: exclude: - firebase_auth_platform_interface/lib/src/pigeon/messages.pigeon.dart - firebase_auth_platform_interface/test/pigeon/test_api.dart + - firebase_auth_platform_interface/pigeons/messages.dart diff --git a/packages/firebase_auth/firebase_auth/CHANGELOG.md b/packages/firebase_auth/firebase_auth/CHANGELOG.md index d7d25139c76b..b712b5e1e36a 100644 --- a/packages/firebase_auth/firebase_auth/CHANGELOG.md +++ b/packages/firebase_auth/firebase_auth/CHANGELOG.md @@ -1,3 +1,35 @@ +## 6.5.4 + + - **FIX**: resolve FlutterSceneLifeCycleDelegate conformance guard ([#18385](https://github.com/firebase/flutterfire/issues/18385)). ([48d67196](https://github.com/firebase/flutterfire/commit/48d67196a10affe09724529df5f67cf40b62bccf)) + +## 6.5.3 + + - Update a dependency to the latest release. + +## 6.5.2 + + - **FIX**(auth,android): update token retrieval in PigeonParser to handle Number type correctly ([#18328](https://github.com/firebase/flutterfire/issues/18328)). ([3b77147b](https://github.com/firebase/flutterfire/commit/3b77147bc00bb19af5f4821436a1a4cdd8ff6791)) + - **DOCS**(auth): clarify behavior of password reset email with email enumeration protection enabled ([#18296](https://github.com/firebase/flutterfire/issues/18296)). ([0bcce87a](https://github.com/firebase/flutterfire/commit/0bcce87a17830797dae6ff3c1a8ba4ce210c2c0d)) + +## 6.5.1 + + - Update a dependency to the latest release. + +## 6.5.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FIX**(auth,apple): remove incorrect paths in Package.swift files search paths ([#18239](https://github.com/firebase/flutterfire/issues/18239)). ([7c2fa5b8](https://github.com/firebase/flutterfire/commit/7c2fa5b83201f2f68e031476dc37ad41809215f2)) + - **FIX**(auth,iOS): update import path for autogenerated messages ([#18227](https://github.com/firebase/flutterfire/issues/18227)). ([4351179d](https://github.com/firebase/flutterfire/commit/4351179d357eeab6b23ec66f45d558c02d3fde69)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + - **FEAT**(auth,android): add revokeAccessToken support for Android ([#18206](https://github.com/firebase/flutterfire/issues/18206)) ([#18207](https://github.com/firebase/flutterfire/issues/18207)). ([7e0a2227](https://github.com/firebase/flutterfire/commit/7e0a222700178a57d064c27b4ef62cefdda1e253)) + +## 6.4.0 + + - **FIX**(auth,ios): serialize Sign in with Apple to prevent crash on overlapping requests ([#18172](https://github.com/firebase/flutterfire/issues/18172)). ([752cbcaa](https://github.com/firebase/flutterfire/commit/752cbcaa57f887a8fea3bda728bb8482290fa049)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 6.3.0 - **FIX**(auth): fix inconsistence in casing in the native iOS SDK and Web SDK ([#18086](https://github.com/firebase/flutterfire/issues/18086)). ([60b5cd5c](https://github.com/firebase/flutterfire/commit/60b5cd5c7888fa932124958125e87bd39e1c323c)) diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPlugin.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPlugin.java index 1f9a73d433ab..a122221c601c 100755 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPlugin.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPlugin.java @@ -142,7 +142,8 @@ static FirebaseAuth getAuthFromPigeon( auth.setCustomAuthDomain(customDomain); } - // Auth's `getCustomAuthDomain` supersedes value from `customAuthDomain` map set by `initializeApp` + // Auth's `getCustomAuthDomain` supersedes value from `customAuthDomain` map set by + // `initializeApp` if (pigeonApp.getCustomAuthDomain() != null) { auth.setCustomAuthDomain(pigeonApp.getCustomAuthDomain()); } @@ -224,7 +225,7 @@ public void checkActionCode( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String code, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); firebaseAuth @@ -270,7 +271,7 @@ public void createUserWithEmailAndPassword( @NonNull String email, @NonNull String password, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); @@ -293,7 +294,7 @@ public void createUserWithEmailAndPassword( public void signInAnonymously( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); firebaseAuth @@ -316,7 +317,7 @@ public void signInWithCredential( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull Map input, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); AuthCredential credential = PigeonParser.getCredential(input); @@ -344,7 +345,7 @@ public void signInWithCustomToken( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String token, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); @@ -369,7 +370,7 @@ public void signInWithEmailAndPassword( @NonNull String email, @NonNull String password, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); firebaseAuth @@ -392,7 +393,7 @@ public void signInWithEmailLink( @NonNull String email, @NonNull String emailLink, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); firebaseAuth @@ -413,9 +414,9 @@ public void signInWithEmailLink( @Override public void signInWithProvider( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonSignInProvider signInProvider, + @NonNull GeneratedAndroidFirebaseAuth.InternalSignInProvider signInProvider, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); @@ -489,7 +490,7 @@ public void fetchSignInMethodsForEmail( public void sendPasswordResetEmail( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String email, - @Nullable GeneratedAndroidFirebaseAuth.PigeonActionCodeSettings actionCodeSettings, + @Nullable GeneratedAndroidFirebaseAuth.InternalActionCodeSettings actionCodeSettings, @NonNull GeneratedAndroidFirebaseAuth.VoidResult result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); @@ -527,7 +528,7 @@ public void sendPasswordResetEmail( public void sendSignInLinkToEmail( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String email, - @NonNull GeneratedAndroidFirebaseAuth.PigeonActionCodeSettings actionCodeSettings, + @NonNull GeneratedAndroidFirebaseAuth.InternalActionCodeSettings actionCodeSettings, @NonNull GeneratedAndroidFirebaseAuth.VoidResult result) { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); @@ -568,7 +569,7 @@ public void setLanguageCode( @Override public void setSettings( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonFirebaseAuthSettings settings, + @NonNull GeneratedAndroidFirebaseAuth.InternalFirebaseAuthSettings settings, @NonNull GeneratedAndroidFirebaseAuth.VoidResult result) { try { FirebaseAuth firebaseAuth = getAuthFromPigeon(app); @@ -620,7 +621,7 @@ public void verifyPasswordResetCode( @Override public void verifyPhoneNumber( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonVerifyPhoneNumberRequest request, + @NonNull GeneratedAndroidFirebaseAuth.InternalVerifyPhoneNumberRequest request, @NonNull GeneratedAndroidFirebaseAuth.Result result) { try { String eventChannelName = METHOD_CHANNEL_NAME + "/phone/" + UUID.randomUUID().toString(); @@ -678,6 +679,27 @@ public void revokeTokenWithAuthorizationCode( result.success(); } + @Override + public void revokeAccessToken( + @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, + @NonNull String accessToken, + @NonNull GeneratedAndroidFirebaseAuth.VoidResult result) { + FirebaseAuth firebaseAuth = getAuthFromPigeon(app); + + firebaseAuth + .revokeAccessToken(accessToken) + .addOnCompleteListener( + task -> { + if (task.isSuccessful()) { + result.success(); + } else { + result.error( + FlutterFirebaseAuthPluginException.parserExceptionToFlutter( + task.getException())); + } + }); + } + @Override public void initializeRecaptchaConfig( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @@ -709,7 +731,7 @@ public Task> getPluginConstantsForFirebaseApp(FirebaseApp fi FirebaseUser firebaseUser = firebaseAuth.getCurrentUser(); String languageCode = firebaseAuth.getLanguageCode(); - GeneratedAndroidFirebaseAuth.PigeonUserDetails user = + GeneratedAndroidFirebaseAuth.InternalUserDetails user = firebaseUser == null ? null : PigeonParser.parseFirebaseUser(firebaseUser); if (languageCode != null) { diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPluginException.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPluginException.java index 7d8ef4338065..a42bcb56956d 100644 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPluginException.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthPluginException.java @@ -70,7 +70,8 @@ static GeneratedAndroidFirebaseAuth.FlutterError parserExceptionToFlutter( && nativeException.getCause() instanceof FirebaseNetworkException)) { return new GeneratedAndroidFirebaseAuth.FlutterError( "network-request-failed", - "A network error (such as timeout, interrupted connection or unreachable host) has occurred.", + "A network error (such as timeout, interrupted connection or unreachable host) has" + + " occurred.", null); } diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthUser.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthUser.java index 9f84d832c419..7039a38f18e8 100644 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthUser.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseAuthUser.java @@ -73,7 +73,7 @@ public void getIdToken( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull Boolean forceRefresh, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { cachedThreadPool.execute( () -> { @@ -97,7 +97,7 @@ public void linkWithCredential( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull Map input, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); AuthCredential credential = PigeonParser.getCredential(input); @@ -129,9 +129,9 @@ public void linkWithCredential( @Override public void linkWithProvider( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonSignInProvider signInProvider, + @NonNull GeneratedAndroidFirebaseAuth.InternalSignInProvider signInProvider, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -162,7 +162,7 @@ public void reauthenticateWithCredential( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull Map input, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); AuthCredential credential = PigeonParser.getCredential(input); @@ -194,9 +194,9 @@ public void reauthenticateWithCredential( @Override public void reauthenticateWithProvider( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonSignInProvider signInProvider, + @NonNull GeneratedAndroidFirebaseAuth.InternalSignInProvider signInProvider, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -226,7 +226,7 @@ public void reauthenticateWithProvider( public void reload( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -252,7 +252,7 @@ public void reload( @Override public void sendEmailVerification( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @Nullable GeneratedAndroidFirebaseAuth.PigeonActionCodeSettings actionCodeSettings, + @Nullable GeneratedAndroidFirebaseAuth.InternalActionCodeSettings actionCodeSettings, @NonNull GeneratedAndroidFirebaseAuth.VoidResult result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -296,7 +296,7 @@ public void unlink( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String providerId, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -330,7 +330,7 @@ public void updateEmail( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String newEmail, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -369,7 +369,7 @@ public void updatePassword( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String newPassword, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -408,7 +408,7 @@ public void updatePhoneNumber( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull Map input, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -453,9 +453,9 @@ public void updatePhoneNumber( @Override public void updateProfile( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonUserProfile profile, + @NonNull GeneratedAndroidFirebaseAuth.InternalUserProfile profile, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); @@ -507,7 +507,7 @@ public void updateProfile( public void verifyBeforeUpdateEmail( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull String newEmail, - @Nullable GeneratedAndroidFirebaseAuth.PigeonActionCodeSettings actionCodeSettings, + @Nullable GeneratedAndroidFirebaseAuth.InternalActionCodeSettings actionCodeSettings, @NonNull GeneratedAndroidFirebaseAuth.VoidResult result) { FirebaseUser firebaseUser = getCurrentUserFromPigeon(app); diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseMultiFactor.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseMultiFactor.java index f2d1ed575ca6..1ba51914254e 100644 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseMultiFactor.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseMultiFactor.java @@ -60,7 +60,7 @@ MultiFactor getAppMultiFactor(@NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFi @Override public void enrollPhone( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonPhoneMultiFactorAssertion assertion, + @NonNull GeneratedAndroidFirebaseAuth.InternalPhoneMultiFactorAssertion assertion, @Nullable String displayName, @NonNull GeneratedAndroidFirebaseAuth.VoidResult result) { final MultiFactor multiFactor; @@ -126,7 +126,8 @@ public void enrollTotp( public void getSession( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result< + GeneratedAndroidFirebaseAuth.InternalMultiFactorSession> result) { final MultiFactor multiFactor; try { @@ -145,7 +146,7 @@ public void getSession( final String id = UUID.randomUUID().toString(); multiFactorSessionMap.put(id, sessionResult); result.success( - new GeneratedAndroidFirebaseAuth.PigeonMultiFactorSession.Builder() + new GeneratedAndroidFirebaseAuth.InternalMultiFactorSession.Builder() .setId(id) .build()); } else { @@ -188,7 +189,7 @@ public void getEnrolledFactors( @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, @NonNull GeneratedAndroidFirebaseAuth.Result< - List> + List> result) { final MultiFactor multiFactor; try { @@ -200,7 +201,7 @@ public void getEnrolledFactors( final List factors = multiFactor.getEnrolledFactors(); - final List resultFactors = + final List resultFactors = PigeonParser.multiFactorInfoToPigeon(factors); result.success(resultFactors); @@ -209,10 +210,10 @@ public void getEnrolledFactors( @Override public void resolveSignIn( @NonNull String resolverId, - @Nullable GeneratedAndroidFirebaseAuth.PigeonPhoneMultiFactorAssertion assertion, + @Nullable GeneratedAndroidFirebaseAuth.InternalPhoneMultiFactorAssertion assertion, @Nullable String totpAssertionId, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { final MultiFactorResolver resolver = multiFactorResolverMap.get(resolverId); diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseTotpMultiFactor.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseTotpMultiFactor.java index c766b9598ed4..9761a5df73f2 100644 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseTotpMultiFactor.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/FlutterFirebaseTotpMultiFactor.java @@ -25,7 +25,7 @@ public class FlutterFirebaseTotpMultiFactor public void generateSecret( @NonNull String sessionId, @NonNull - GeneratedAndroidFirebaseAuth.Result + GeneratedAndroidFirebaseAuth.Result result) { MultiFactorSession multiFactorSession = FlutterFirebaseMultiFactor.multiFactorSessionMap.get(sessionId); @@ -38,7 +38,7 @@ public void generateSecret( TotpSecret secret = task.getResult(); multiFactorSecret.put(secret.getSharedSecretKey(), secret); result.success( - new GeneratedAndroidFirebaseAuth.PigeonTotpSecret.Builder() + new GeneratedAndroidFirebaseAuth.InternalTotpSecret.Builder() .setCodeIntervalSeconds((long) secret.getCodeIntervalSeconds()) .setCodeLength((long) secret.getCodeLength()) .setSecretKey(secret.getSharedSecretKey()) diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/GeneratedAndroidFirebaseAuth.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/GeneratedAndroidFirebaseAuth.java index f9666b78087a..5aaa168b70e8 100644 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/GeneratedAndroidFirebaseAuth.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/GeneratedAndroidFirebaseAuth.java @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v19.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.firebase.auth; @@ -21,12 +21,170 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; /** Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class GeneratedAndroidFirebaseAuth { + static boolean pigeonDoubleEquals(double a, double b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b)); + } + + static boolean pigeonFloatEquals(float a, float b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b)); + } + + static int pigeonDoubleHashCode(double d) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (d == 0.0) { + d = 0.0; + } + long bits = Double.doubleToLongBits(d); + return (int) (bits ^ (bits >>> 32)); + } + + static int pigeonFloatHashCode(float f) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (f == 0.0f) { + f = 0.0f; + } + return Float.floatToIntBits(f); + } + + static boolean pigeonDeepEquals(Object a, Object b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + if (a instanceof byte[] && b instanceof byte[]) { + return Arrays.equals((byte[]) a, (byte[]) b); + } + if (a instanceof int[] && b instanceof int[]) { + return Arrays.equals((int[]) a, (int[]) b); + } + if (a instanceof long[] && b instanceof long[]) { + return Arrays.equals((long[]) a, (long[]) b); + } + if (a instanceof double[] && b instanceof double[]) { + double[] da = (double[]) a; + double[] db = (double[]) b; + if (da.length != db.length) { + return false; + } + for (int i = 0; i < da.length; i++) { + if (!pigeonDoubleEquals(da[i], db[i])) { + return false; + } + } + return true; + } + if (a instanceof List && b instanceof List) { + List listA = (List) a; + List listB = (List) b; + if (listA.size() != listB.size()) { + return false; + } + for (int i = 0; i < listA.size(); i++) { + if (!pigeonDeepEquals(listA.get(i), listB.get(i))) { + return false; + } + } + return true; + } + if (a instanceof Map && b instanceof Map) { + Map mapA = (Map) a; + Map mapB = (Map) b; + if (mapA.size() != mapB.size()) { + return false; + } + for (Map.Entry entryA : mapA.entrySet()) { + Object keyA = entryA.getKey(); + Object valueA = entryA.getValue(); + boolean found = false; + for (Map.Entry entryB : mapB.entrySet()) { + Object keyB = entryB.getKey(); + if (pigeonDeepEquals(keyA, keyB)) { + Object valueB = entryB.getValue(); + if (pigeonDeepEquals(valueA, valueB)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + if (a instanceof Double && b instanceof Double) { + return pigeonDoubleEquals((double) a, (double) b); + } + if (a instanceof Float && b instanceof Float) { + return pigeonFloatEquals((float) a, (float) b); + } + return a.equals(b); + } + + static int pigeonDeepHashCode(Object value) { + if (value == null) { + return 0; + } + if (value instanceof byte[]) { + return Arrays.hashCode((byte[]) value); + } + if (value instanceof int[]) { + return Arrays.hashCode((int[]) value); + } + if (value instanceof long[]) { + return Arrays.hashCode((long[]) value); + } + if (value instanceof double[]) { + double[] da = (double[]) value; + int result = 1; + for (double d : da) { + result = 31 * result + pigeonDoubleHashCode(d); + } + return result; + } + if (value instanceof List) { + int result = 1; + for (Object item : (List) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Map) { + int result = 0; + for (Map.Entry entry : ((Map) value).entrySet()) { + result += + ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue())); + } + return result; + } + if (value instanceof Object[]) { + int result = 1; + for (Object item : (Object[]) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Double) { + return pigeonDoubleHashCode((double) value); + } + if (value instanceof Float) { + return pigeonFloatHashCode((float) value); + } + return value.hashCode(); + } /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ public static class FlutterError extends RuntimeException { @@ -46,7 +204,7 @@ public FlutterError(@NonNull String code, @Nullable String message, @Nullable Ob @NonNull protected static ArrayList wrapError(@NonNull Throwable exception) { - ArrayList errorList = new ArrayList(3); + ArrayList errorList = new ArrayList<>(3); if (exception instanceof FlutterError) { FlutterError error = (FlutterError) exception; errorList.add(error.code); @@ -84,13 +242,13 @@ public enum ActionCodeInfoOperation { final int index; - private ActionCodeInfoOperation(final int index) { + ActionCodeInfoOperation(final int index) { this.index = index; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonMultiFactorSession { + public static final class InternalMultiFactorSession { private @NonNull String id; public @NonNull String getId() { @@ -105,7 +263,25 @@ public void setId(@NonNull String setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonMultiFactorSession() {} + InternalMultiFactorSession() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalMultiFactorSession that = (InternalMultiFactorSession) o; + return pigeonDeepEquals(id, that.id); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), id}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -117,30 +293,30 @@ public static final class Builder { return this; } - public @NonNull PigeonMultiFactorSession build() { - PigeonMultiFactorSession pigeonReturn = new PigeonMultiFactorSession(); + public @NonNull InternalMultiFactorSession build() { + InternalMultiFactorSession pigeonReturn = new InternalMultiFactorSession(); pigeonReturn.setId(id); return pigeonReturn; } } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(1); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(1); toListResult.add(id); return toListResult; } - static @NonNull PigeonMultiFactorSession fromList(@NonNull ArrayList __pigeon_list) { - PigeonMultiFactorSession pigeonResult = new PigeonMultiFactorSession(); - Object id = __pigeon_list.get(0); + static @NonNull InternalMultiFactorSession fromList(@NonNull ArrayList pigeonVar_list) { + InternalMultiFactorSession pigeonResult = new InternalMultiFactorSession(); + Object id = pigeonVar_list.get(0); pigeonResult.setId((String) id); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonPhoneMultiFactorAssertion { + public static final class InternalPhoneMultiFactorAssertion { private @NonNull String verificationId; public @NonNull String getVerificationId() { @@ -168,7 +344,26 @@ public void setVerificationCode(@NonNull String setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonPhoneMultiFactorAssertion() {} + InternalPhoneMultiFactorAssertion() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalPhoneMultiFactorAssertion that = (InternalPhoneMultiFactorAssertion) o; + return pigeonDeepEquals(verificationId, that.verificationId) + && pigeonDeepEquals(verificationCode, that.verificationCode); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), verificationId, verificationCode}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -188,8 +383,8 @@ public static final class Builder { return this; } - public @NonNull PigeonPhoneMultiFactorAssertion build() { - PigeonPhoneMultiFactorAssertion pigeonReturn = new PigeonPhoneMultiFactorAssertion(); + public @NonNull InternalPhoneMultiFactorAssertion build() { + InternalPhoneMultiFactorAssertion pigeonReturn = new InternalPhoneMultiFactorAssertion(); pigeonReturn.setVerificationId(verificationId); pigeonReturn.setVerificationCode(verificationCode); return pigeonReturn; @@ -197,26 +392,26 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(2); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); toListResult.add(verificationId); toListResult.add(verificationCode); return toListResult; } - static @NonNull PigeonPhoneMultiFactorAssertion fromList( - @NonNull ArrayList __pigeon_list) { - PigeonPhoneMultiFactorAssertion pigeonResult = new PigeonPhoneMultiFactorAssertion(); - Object verificationId = __pigeon_list.get(0); + static @NonNull InternalPhoneMultiFactorAssertion fromList( + @NonNull ArrayList pigeonVar_list) { + InternalPhoneMultiFactorAssertion pigeonResult = new InternalPhoneMultiFactorAssertion(); + Object verificationId = pigeonVar_list.get(0); pigeonResult.setVerificationId((String) verificationId); - Object verificationCode = __pigeon_list.get(1); + Object verificationCode = pigeonVar_list.get(1); pigeonResult.setVerificationCode((String) verificationCode); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonMultiFactorInfo { + public static final class InternalMultiFactorInfo { private @Nullable String displayName; public @Nullable String getDisplayName() { @@ -274,7 +469,30 @@ public void setPhoneNumber(@Nullable String setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonMultiFactorInfo() {} + InternalMultiFactorInfo() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalMultiFactorInfo that = (InternalMultiFactorInfo) o; + return pigeonDeepEquals(displayName, that.displayName) + && pigeonDeepEquals(enrollmentTimestamp, that.enrollmentTimestamp) + && pigeonDeepEquals(factorId, that.factorId) + && pigeonDeepEquals(uid, that.uid) + && pigeonDeepEquals(phoneNumber, that.phoneNumber); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] {getClass(), displayName, enrollmentTimestamp, factorId, uid, phoneNumber}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -318,8 +536,8 @@ public static final class Builder { return this; } - public @NonNull PigeonMultiFactorInfo build() { - PigeonMultiFactorInfo pigeonReturn = new PigeonMultiFactorInfo(); + public @NonNull InternalMultiFactorInfo build() { + InternalMultiFactorInfo pigeonReturn = new InternalMultiFactorInfo(); pigeonReturn.setDisplayName(displayName); pigeonReturn.setEnrollmentTimestamp(enrollmentTimestamp); pigeonReturn.setFactorId(factorId); @@ -330,8 +548,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(5); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(5); toListResult.add(displayName); toListResult.add(enrollmentTimestamp); toListResult.add(factorId); @@ -340,17 +558,17 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonMultiFactorInfo fromList(@NonNull ArrayList __pigeon_list) { - PigeonMultiFactorInfo pigeonResult = new PigeonMultiFactorInfo(); - Object displayName = __pigeon_list.get(0); + static @NonNull InternalMultiFactorInfo fromList(@NonNull ArrayList pigeonVar_list) { + InternalMultiFactorInfo pigeonResult = new InternalMultiFactorInfo(); + Object displayName = pigeonVar_list.get(0); pigeonResult.setDisplayName((String) displayName); - Object enrollmentTimestamp = __pigeon_list.get(1); + Object enrollmentTimestamp = pigeonVar_list.get(1); pigeonResult.setEnrollmentTimestamp((Double) enrollmentTimestamp); - Object factorId = __pigeon_list.get(2); + Object factorId = pigeonVar_list.get(2); pigeonResult.setFactorId((String) factorId); - Object uid = __pigeon_list.get(3); + Object uid = pigeonVar_list.get(3); pigeonResult.setUid((String) uid); - Object phoneNumber = __pigeon_list.get(4); + Object phoneNumber = pigeonVar_list.get(4); pigeonResult.setPhoneNumber((String) phoneNumber); return pigeonResult; } @@ -394,6 +612,26 @@ public void setCustomAuthDomain(@Nullable String setterArg) { /** Constructor is non-public to enforce null safety; use Builder. */ AuthPigeonFirebaseApp() {} + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AuthPigeonFirebaseApp that = (AuthPigeonFirebaseApp) o; + return pigeonDeepEquals(appName, that.appName) + && pigeonDeepEquals(tenantId, that.tenantId) + && pigeonDeepEquals(customAuthDomain, that.customAuthDomain); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), appName, tenantId, customAuthDomain}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable String appName; @@ -430,28 +668,28 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(3); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(3); toListResult.add(appName); toListResult.add(tenantId); toListResult.add(customAuthDomain); return toListResult; } - static @NonNull AuthPigeonFirebaseApp fromList(@NonNull ArrayList __pigeon_list) { + static @NonNull AuthPigeonFirebaseApp fromList(@NonNull ArrayList pigeonVar_list) { AuthPigeonFirebaseApp pigeonResult = new AuthPigeonFirebaseApp(); - Object appName = __pigeon_list.get(0); + Object appName = pigeonVar_list.get(0); pigeonResult.setAppName((String) appName); - Object tenantId = __pigeon_list.get(1); + Object tenantId = pigeonVar_list.get(1); pigeonResult.setTenantId((String) tenantId); - Object customAuthDomain = __pigeon_list.get(2); + Object customAuthDomain = pigeonVar_list.get(2); pigeonResult.setCustomAuthDomain((String) customAuthDomain); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonActionCodeInfoData { + public static final class InternalActionCodeInfoData { private @Nullable String email; public @Nullable String getEmail() { @@ -472,6 +710,25 @@ public void setPreviousEmail(@Nullable String setterArg) { this.previousEmail = setterArg; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalActionCodeInfoData that = (InternalActionCodeInfoData) o; + return pigeonDeepEquals(email, that.email) + && pigeonDeepEquals(previousEmail, that.previousEmail); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), email, previousEmail}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable String email; @@ -490,8 +747,8 @@ public static final class Builder { return this; } - public @NonNull PigeonActionCodeInfoData build() { - PigeonActionCodeInfoData pigeonReturn = new PigeonActionCodeInfoData(); + public @NonNull InternalActionCodeInfoData build() { + InternalActionCodeInfoData pigeonReturn = new InternalActionCodeInfoData(); pigeonReturn.setEmail(email); pigeonReturn.setPreviousEmail(previousEmail); return pigeonReturn; @@ -499,25 +756,25 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(2); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); toListResult.add(email); toListResult.add(previousEmail); return toListResult; } - static @NonNull PigeonActionCodeInfoData fromList(@NonNull ArrayList __pigeon_list) { - PigeonActionCodeInfoData pigeonResult = new PigeonActionCodeInfoData(); - Object email = __pigeon_list.get(0); + static @NonNull InternalActionCodeInfoData fromList(@NonNull ArrayList pigeonVar_list) { + InternalActionCodeInfoData pigeonResult = new InternalActionCodeInfoData(); + Object email = pigeonVar_list.get(0); pigeonResult.setEmail((String) email); - Object previousEmail = __pigeon_list.get(1); + Object previousEmail = pigeonVar_list.get(1); pigeonResult.setPreviousEmail((String) previousEmail); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonActionCodeInfo { + public static final class InternalActionCodeInfo { private @NonNull ActionCodeInfoOperation operation; public @NonNull ActionCodeInfoOperation getOperation() { @@ -531,13 +788,13 @@ public void setOperation(@NonNull ActionCodeInfoOperation setterArg) { this.operation = setterArg; } - private @NonNull PigeonActionCodeInfoData data; + private @NonNull InternalActionCodeInfoData data; - public @NonNull PigeonActionCodeInfoData getData() { + public @NonNull InternalActionCodeInfoData getData() { return data; } - public void setData(@NonNull PigeonActionCodeInfoData setterArg) { + public void setData(@NonNull InternalActionCodeInfoData setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"data\" is null."); } @@ -545,7 +802,25 @@ public void setData(@NonNull PigeonActionCodeInfoData setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonActionCodeInfo() {} + InternalActionCodeInfo() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalActionCodeInfo that = (InternalActionCodeInfo) o; + return pigeonDeepEquals(operation, that.operation) && pigeonDeepEquals(data, that.data); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), operation, data}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -557,16 +832,16 @@ public static final class Builder { return this; } - private @Nullable PigeonActionCodeInfoData data; + private @Nullable InternalActionCodeInfoData data; @CanIgnoreReturnValue - public @NonNull Builder setData(@NonNull PigeonActionCodeInfoData setterArg) { + public @NonNull Builder setData(@NonNull InternalActionCodeInfoData setterArg) { this.data = setterArg; return this; } - public @NonNull PigeonActionCodeInfo build() { - PigeonActionCodeInfo pigeonReturn = new PigeonActionCodeInfo(); + public @NonNull InternalActionCodeInfo build() { + InternalActionCodeInfo pigeonReturn = new InternalActionCodeInfo(); pigeonReturn.setOperation(operation); pigeonReturn.setData(data); return pigeonReturn; @@ -574,25 +849,25 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(2); - toListResult.add(operation == null ? null : operation.index); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(operation); toListResult.add(data); return toListResult; } - static @NonNull PigeonActionCodeInfo fromList(@NonNull ArrayList __pigeon_list) { - PigeonActionCodeInfo pigeonResult = new PigeonActionCodeInfo(); - Object operation = __pigeon_list.get(0); - pigeonResult.setOperation(ActionCodeInfoOperation.values()[(int) operation]); - Object data = __pigeon_list.get(1); - pigeonResult.setData((PigeonActionCodeInfoData) data); + static @NonNull InternalActionCodeInfo fromList(@NonNull ArrayList pigeonVar_list) { + InternalActionCodeInfo pigeonResult = new InternalActionCodeInfo(); + Object operation = pigeonVar_list.get(0); + pigeonResult.setOperation((ActionCodeInfoOperation) operation); + Object data = pigeonVar_list.get(1); + pigeonResult.setData((InternalActionCodeInfoData) data); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonAdditionalUserInfo { + public static final class InternalAdditionalUserInfo { private @NonNull Boolean isNewUser; public @NonNull Boolean getIsNewUser() { @@ -647,7 +922,30 @@ public void setProfile(@Nullable Map setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonAdditionalUserInfo() {} + InternalAdditionalUserInfo() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalAdditionalUserInfo that = (InternalAdditionalUserInfo) o; + return pigeonDeepEquals(isNewUser, that.isNewUser) + && pigeonDeepEquals(providerId, that.providerId) + && pigeonDeepEquals(username, that.username) + && pigeonDeepEquals(authorizationCode, that.authorizationCode) + && pigeonDeepEquals(profile, that.profile); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] {getClass(), isNewUser, providerId, username, authorizationCode, profile}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -691,8 +989,8 @@ public static final class Builder { return this; } - public @NonNull PigeonAdditionalUserInfo build() { - PigeonAdditionalUserInfo pigeonReturn = new PigeonAdditionalUserInfo(); + public @NonNull InternalAdditionalUserInfo build() { + InternalAdditionalUserInfo pigeonReturn = new InternalAdditionalUserInfo(); pigeonReturn.setIsNewUser(isNewUser); pigeonReturn.setProviderId(providerId); pigeonReturn.setUsername(username); @@ -703,8 +1001,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(5); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(5); toListResult.add(isNewUser); toListResult.add(providerId); toListResult.add(username); @@ -713,24 +1011,24 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonAdditionalUserInfo fromList(@NonNull ArrayList __pigeon_list) { - PigeonAdditionalUserInfo pigeonResult = new PigeonAdditionalUserInfo(); - Object isNewUser = __pigeon_list.get(0); + static @NonNull InternalAdditionalUserInfo fromList(@NonNull ArrayList pigeonVar_list) { + InternalAdditionalUserInfo pigeonResult = new InternalAdditionalUserInfo(); + Object isNewUser = pigeonVar_list.get(0); pigeonResult.setIsNewUser((Boolean) isNewUser); - Object providerId = __pigeon_list.get(1); + Object providerId = pigeonVar_list.get(1); pigeonResult.setProviderId((String) providerId); - Object username = __pigeon_list.get(2); + Object username = pigeonVar_list.get(2); pigeonResult.setUsername((String) username); - Object authorizationCode = __pigeon_list.get(3); + Object authorizationCode = pigeonVar_list.get(3); pigeonResult.setAuthorizationCode((String) authorizationCode); - Object profile = __pigeon_list.get(4); + Object profile = pigeonVar_list.get(4); pigeonResult.setProfile((Map) profile); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonAuthCredential { + public static final class InternalAuthCredential { private @NonNull String providerId; public @NonNull String getProviderId() { @@ -781,7 +1079,28 @@ public void setAccessToken(@Nullable String setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonAuthCredential() {} + InternalAuthCredential() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalAuthCredential that = (InternalAuthCredential) o; + return pigeonDeepEquals(providerId, that.providerId) + && pigeonDeepEquals(signInMethod, that.signInMethod) + && pigeonDeepEquals(nativeId, that.nativeId) + && pigeonDeepEquals(accessToken, that.accessToken); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), providerId, signInMethod, nativeId, accessToken}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -817,8 +1136,8 @@ public static final class Builder { return this; } - public @NonNull PigeonAuthCredential build() { - PigeonAuthCredential pigeonReturn = new PigeonAuthCredential(); + public @NonNull InternalAuthCredential build() { + InternalAuthCredential pigeonReturn = new InternalAuthCredential(); pigeonReturn.setProviderId(providerId); pigeonReturn.setSignInMethod(signInMethod); pigeonReturn.setNativeId(nativeId); @@ -828,8 +1147,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(4); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(4); toListResult.add(providerId); toListResult.add(signInMethod); toListResult.add(nativeId); @@ -837,25 +1156,22 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonAuthCredential fromList(@NonNull ArrayList __pigeon_list) { - PigeonAuthCredential pigeonResult = new PigeonAuthCredential(); - Object providerId = __pigeon_list.get(0); + static @NonNull InternalAuthCredential fromList(@NonNull ArrayList pigeonVar_list) { + InternalAuthCredential pigeonResult = new InternalAuthCredential(); + Object providerId = pigeonVar_list.get(0); pigeonResult.setProviderId((String) providerId); - Object signInMethod = __pigeon_list.get(1); + Object signInMethod = pigeonVar_list.get(1); pigeonResult.setSignInMethod((String) signInMethod); - Object nativeId = __pigeon_list.get(2); - pigeonResult.setNativeId( - (nativeId == null) - ? null - : ((nativeId instanceof Integer) ? (Integer) nativeId : (Long) nativeId)); - Object accessToken = __pigeon_list.get(3); + Object nativeId = pigeonVar_list.get(2); + pigeonResult.setNativeId((Long) nativeId); + Object accessToken = pigeonVar_list.get(3); pigeonResult.setAccessToken((String) accessToken); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonUserInfo { + public static final class InternalUserInfo { private @NonNull String uid; public @NonNull String getUid() { @@ -986,7 +1302,51 @@ public void setLastSignInTimestamp(@Nullable Long setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonUserInfo() {} + InternalUserInfo() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalUserInfo that = (InternalUserInfo) o; + return pigeonDeepEquals(uid, that.uid) + && pigeonDeepEquals(email, that.email) + && pigeonDeepEquals(displayName, that.displayName) + && pigeonDeepEquals(photoUrl, that.photoUrl) + && pigeonDeepEquals(phoneNumber, that.phoneNumber) + && pigeonDeepEquals(isAnonymous, that.isAnonymous) + && pigeonDeepEquals(isEmailVerified, that.isEmailVerified) + && pigeonDeepEquals(providerId, that.providerId) + && pigeonDeepEquals(tenantId, that.tenantId) + && pigeonDeepEquals(refreshToken, that.refreshToken) + && pigeonDeepEquals(creationTimestamp, that.creationTimestamp) + && pigeonDeepEquals(lastSignInTimestamp, that.lastSignInTimestamp); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + uid, + email, + displayName, + photoUrl, + phoneNumber, + isAnonymous, + isEmailVerified, + providerId, + tenantId, + refreshToken, + creationTimestamp, + lastSignInTimestamp + }; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -1086,8 +1446,8 @@ public static final class Builder { return this; } - public @NonNull PigeonUserInfo build() { - PigeonUserInfo pigeonReturn = new PigeonUserInfo(); + public @NonNull InternalUserInfo build() { + InternalUserInfo pigeonReturn = new InternalUserInfo(); pigeonReturn.setUid(uid); pigeonReturn.setEmail(email); pigeonReturn.setDisplayName(displayName); @@ -1105,8 +1465,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(12); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(12); toListResult.add(uid); toListResult.add(email); toListResult.add(displayName); @@ -1122,55 +1482,45 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonUserInfo fromList(@NonNull ArrayList __pigeon_list) { - PigeonUserInfo pigeonResult = new PigeonUserInfo(); - Object uid = __pigeon_list.get(0); + static @NonNull InternalUserInfo fromList(@NonNull ArrayList pigeonVar_list) { + InternalUserInfo pigeonResult = new InternalUserInfo(); + Object uid = pigeonVar_list.get(0); pigeonResult.setUid((String) uid); - Object email = __pigeon_list.get(1); + Object email = pigeonVar_list.get(1); pigeonResult.setEmail((String) email); - Object displayName = __pigeon_list.get(2); + Object displayName = pigeonVar_list.get(2); pigeonResult.setDisplayName((String) displayName); - Object photoUrl = __pigeon_list.get(3); + Object photoUrl = pigeonVar_list.get(3); pigeonResult.setPhotoUrl((String) photoUrl); - Object phoneNumber = __pigeon_list.get(4); + Object phoneNumber = pigeonVar_list.get(4); pigeonResult.setPhoneNumber((String) phoneNumber); - Object isAnonymous = __pigeon_list.get(5); + Object isAnonymous = pigeonVar_list.get(5); pigeonResult.setIsAnonymous((Boolean) isAnonymous); - Object isEmailVerified = __pigeon_list.get(6); + Object isEmailVerified = pigeonVar_list.get(6); pigeonResult.setIsEmailVerified((Boolean) isEmailVerified); - Object providerId = __pigeon_list.get(7); + Object providerId = pigeonVar_list.get(7); pigeonResult.setProviderId((String) providerId); - Object tenantId = __pigeon_list.get(8); + Object tenantId = pigeonVar_list.get(8); pigeonResult.setTenantId((String) tenantId); - Object refreshToken = __pigeon_list.get(9); + Object refreshToken = pigeonVar_list.get(9); pigeonResult.setRefreshToken((String) refreshToken); - Object creationTimestamp = __pigeon_list.get(10); - pigeonResult.setCreationTimestamp( - (creationTimestamp == null) - ? null - : ((creationTimestamp instanceof Integer) - ? (Integer) creationTimestamp - : (Long) creationTimestamp)); - Object lastSignInTimestamp = __pigeon_list.get(11); - pigeonResult.setLastSignInTimestamp( - (lastSignInTimestamp == null) - ? null - : ((lastSignInTimestamp instanceof Integer) - ? (Integer) lastSignInTimestamp - : (Long) lastSignInTimestamp)); + Object creationTimestamp = pigeonVar_list.get(10); + pigeonResult.setCreationTimestamp((Long) creationTimestamp); + Object lastSignInTimestamp = pigeonVar_list.get(11); + pigeonResult.setLastSignInTimestamp((Long) lastSignInTimestamp); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonUserDetails { - private @NonNull PigeonUserInfo userInfo; + public static final class InternalUserDetails { + private @NonNull InternalUserInfo userInfo; - public @NonNull PigeonUserInfo getUserInfo() { + public @NonNull InternalUserInfo getUserInfo() { return userInfo; } - public void setUserInfo(@NonNull PigeonUserInfo setterArg) { + public void setUserInfo(@NonNull InternalUserInfo setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"userInfo\" is null."); } @@ -1191,14 +1541,33 @@ public void setProviderData(@NonNull List> setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonUserDetails() {} + InternalUserDetails() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalUserDetails that = (InternalUserDetails) o; + return pigeonDeepEquals(userInfo, that.userInfo) + && pigeonDeepEquals(providerData, that.providerData); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), userInfo, providerData}; + return pigeonDeepHashCode(fields); + } public static final class Builder { - private @Nullable PigeonUserInfo userInfo; + private @Nullable InternalUserInfo userInfo; @CanIgnoreReturnValue - public @NonNull Builder setUserInfo(@NonNull PigeonUserInfo setterArg) { + public @NonNull Builder setUserInfo(@NonNull InternalUserInfo setterArg) { this.userInfo = setterArg; return this; } @@ -1211,8 +1580,8 @@ public static final class Builder { return this; } - public @NonNull PigeonUserDetails build() { - PigeonUserDetails pigeonReturn = new PigeonUserDetails(); + public @NonNull InternalUserDetails build() { + InternalUserDetails pigeonReturn = new InternalUserDetails(); pigeonReturn.setUserInfo(userInfo); pigeonReturn.setProviderData(providerData); return pigeonReturn; @@ -1220,83 +1589,104 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(2); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); toListResult.add(userInfo); toListResult.add(providerData); return toListResult; } - static @NonNull PigeonUserDetails fromList(@NonNull ArrayList __pigeon_list) { - PigeonUserDetails pigeonResult = new PigeonUserDetails(); - Object userInfo = __pigeon_list.get(0); - pigeonResult.setUserInfo((PigeonUserInfo) userInfo); - Object providerData = __pigeon_list.get(1); + static @NonNull InternalUserDetails fromList(@NonNull ArrayList pigeonVar_list) { + InternalUserDetails pigeonResult = new InternalUserDetails(); + Object userInfo = pigeonVar_list.get(0); + pigeonResult.setUserInfo((InternalUserInfo) userInfo); + Object providerData = pigeonVar_list.get(1); pigeonResult.setProviderData((List>) providerData); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonUserCredential { - private @Nullable PigeonUserDetails user; + public static final class InternalUserCredential { + private @Nullable InternalUserDetails user; - public @Nullable PigeonUserDetails getUser() { + public @Nullable InternalUserDetails getUser() { return user; } - public void setUser(@Nullable PigeonUserDetails setterArg) { + public void setUser(@Nullable InternalUserDetails setterArg) { this.user = setterArg; } - private @Nullable PigeonAdditionalUserInfo additionalUserInfo; + private @Nullable InternalAdditionalUserInfo additionalUserInfo; - public @Nullable PigeonAdditionalUserInfo getAdditionalUserInfo() { + public @Nullable InternalAdditionalUserInfo getAdditionalUserInfo() { return additionalUserInfo; } - public void setAdditionalUserInfo(@Nullable PigeonAdditionalUserInfo setterArg) { + public void setAdditionalUserInfo(@Nullable InternalAdditionalUserInfo setterArg) { this.additionalUserInfo = setterArg; } - private @Nullable PigeonAuthCredential credential; + private @Nullable InternalAuthCredential credential; - public @Nullable PigeonAuthCredential getCredential() { + public @Nullable InternalAuthCredential getCredential() { return credential; } - public void setCredential(@Nullable PigeonAuthCredential setterArg) { + public void setCredential(@Nullable InternalAuthCredential setterArg) { this.credential = setterArg; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalUserCredential that = (InternalUserCredential) o; + return pigeonDeepEquals(user, that.user) + && pigeonDeepEquals(additionalUserInfo, that.additionalUserInfo) + && pigeonDeepEquals(credential, that.credential); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), user, additionalUserInfo, credential}; + return pigeonDeepHashCode(fields); + } + public static final class Builder { - private @Nullable PigeonUserDetails user; + private @Nullable InternalUserDetails user; @CanIgnoreReturnValue - public @NonNull Builder setUser(@Nullable PigeonUserDetails setterArg) { + public @NonNull Builder setUser(@Nullable InternalUserDetails setterArg) { this.user = setterArg; return this; } - private @Nullable PigeonAdditionalUserInfo additionalUserInfo; + private @Nullable InternalAdditionalUserInfo additionalUserInfo; @CanIgnoreReturnValue - public @NonNull Builder setAdditionalUserInfo(@Nullable PigeonAdditionalUserInfo setterArg) { + public @NonNull Builder setAdditionalUserInfo( + @Nullable InternalAdditionalUserInfo setterArg) { this.additionalUserInfo = setterArg; return this; } - private @Nullable PigeonAuthCredential credential; + private @Nullable InternalAuthCredential credential; @CanIgnoreReturnValue - public @NonNull Builder setCredential(@Nullable PigeonAuthCredential setterArg) { + public @NonNull Builder setCredential(@Nullable InternalAuthCredential setterArg) { this.credential = setterArg; return this; } - public @NonNull PigeonUserCredential build() { - PigeonUserCredential pigeonReturn = new PigeonUserCredential(); + public @NonNull InternalUserCredential build() { + InternalUserCredential pigeonReturn = new InternalUserCredential(); pigeonReturn.setUser(user); pigeonReturn.setAdditionalUserInfo(additionalUserInfo); pigeonReturn.setCredential(credential); @@ -1305,28 +1695,169 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(3); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(3); toListResult.add(user); toListResult.add(additionalUserInfo); toListResult.add(credential); return toListResult; } - static @NonNull PigeonUserCredential fromList(@NonNull ArrayList __pigeon_list) { - PigeonUserCredential pigeonResult = new PigeonUserCredential(); - Object user = __pigeon_list.get(0); - pigeonResult.setUser((PigeonUserDetails) user); - Object additionalUserInfo = __pigeon_list.get(1); - pigeonResult.setAdditionalUserInfo((PigeonAdditionalUserInfo) additionalUserInfo); - Object credential = __pigeon_list.get(2); - pigeonResult.setCredential((PigeonAuthCredential) credential); + static @NonNull InternalUserCredential fromList(@NonNull ArrayList pigeonVar_list) { + InternalUserCredential pigeonResult = new InternalUserCredential(); + Object user = pigeonVar_list.get(0); + pigeonResult.setUser((InternalUserDetails) user); + Object additionalUserInfo = pigeonVar_list.get(1); + pigeonResult.setAdditionalUserInfo((InternalAdditionalUserInfo) additionalUserInfo); + Object credential = pigeonVar_list.get(2); + pigeonResult.setCredential((InternalAuthCredential) credential); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class InternalAuthCredentialInput { + private @NonNull String providerId; + + public @NonNull String getProviderId() { + return providerId; + } + + public void setProviderId(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"providerId\" is null."); + } + this.providerId = setterArg; + } + + private @NonNull String signInMethod; + + public @NonNull String getSignInMethod() { + return signInMethod; + } + + public void setSignInMethod(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"signInMethod\" is null."); + } + this.signInMethod = setterArg; + } + + private @Nullable String token; + + public @Nullable String getToken() { + return token; + } + + public void setToken(@Nullable String setterArg) { + this.token = setterArg; + } + + private @Nullable String accessToken; + + public @Nullable String getAccessToken() { + return accessToken; + } + + public void setAccessToken(@Nullable String setterArg) { + this.accessToken = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + InternalAuthCredentialInput() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalAuthCredentialInput that = (InternalAuthCredentialInput) o; + return pigeonDeepEquals(providerId, that.providerId) + && pigeonDeepEquals(signInMethod, that.signInMethod) + && pigeonDeepEquals(token, that.token) + && pigeonDeepEquals(accessToken, that.accessToken); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), providerId, signInMethod, token, accessToken}; + return pigeonDeepHashCode(fields); + } + + public static final class Builder { + + private @Nullable String providerId; + + @CanIgnoreReturnValue + public @NonNull Builder setProviderId(@NonNull String setterArg) { + this.providerId = setterArg; + return this; + } + + private @Nullable String signInMethod; + + @CanIgnoreReturnValue + public @NonNull Builder setSignInMethod(@NonNull String setterArg) { + this.signInMethod = setterArg; + return this; + } + + private @Nullable String token; + + @CanIgnoreReturnValue + public @NonNull Builder setToken(@Nullable String setterArg) { + this.token = setterArg; + return this; + } + + private @Nullable String accessToken; + + @CanIgnoreReturnValue + public @NonNull Builder setAccessToken(@Nullable String setterArg) { + this.accessToken = setterArg; + return this; + } + + public @NonNull InternalAuthCredentialInput build() { + InternalAuthCredentialInput pigeonReturn = new InternalAuthCredentialInput(); + pigeonReturn.setProviderId(providerId); + pigeonReturn.setSignInMethod(signInMethod); + pigeonReturn.setToken(token); + pigeonReturn.setAccessToken(accessToken); + return pigeonReturn; + } + } + + @NonNull + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(4); + toListResult.add(providerId); + toListResult.add(signInMethod); + toListResult.add(token); + toListResult.add(accessToken); + return toListResult; + } + + static @NonNull InternalAuthCredentialInput fromList( + @NonNull ArrayList pigeonVar_list) { + InternalAuthCredentialInput pigeonResult = new InternalAuthCredentialInput(); + Object providerId = pigeonVar_list.get(0); + pigeonResult.setProviderId((String) providerId); + Object signInMethod = pigeonVar_list.get(1); + pigeonResult.setSignInMethod((String) signInMethod); + Object token = pigeonVar_list.get(2); + pigeonResult.setToken((String) token); + Object accessToken = pigeonVar_list.get(3); + pigeonResult.setAccessToken((String) accessToken); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonActionCodeSettings { + public static final class InternalActionCodeSettings { private @NonNull String url; public @NonNull String getUrl() { @@ -1417,7 +1948,43 @@ public void setLinkDomain(@Nullable String setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonActionCodeSettings() {} + InternalActionCodeSettings() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalActionCodeSettings that = (InternalActionCodeSettings) o; + return pigeonDeepEquals(url, that.url) + && pigeonDeepEquals(dynamicLinkDomain, that.dynamicLinkDomain) + && pigeonDeepEquals(handleCodeInApp, that.handleCodeInApp) + && pigeonDeepEquals(iOSBundleId, that.iOSBundleId) + && pigeonDeepEquals(androidPackageName, that.androidPackageName) + && pigeonDeepEquals(androidInstallApp, that.androidInstallApp) + && pigeonDeepEquals(androidMinimumVersion, that.androidMinimumVersion) + && pigeonDeepEquals(linkDomain, that.linkDomain); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + url, + dynamicLinkDomain, + handleCodeInApp, + iOSBundleId, + androidPackageName, + androidInstallApp, + androidMinimumVersion, + linkDomain + }; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -1485,8 +2052,8 @@ public static final class Builder { return this; } - public @NonNull PigeonActionCodeSettings build() { - PigeonActionCodeSettings pigeonReturn = new PigeonActionCodeSettings(); + public @NonNull InternalActionCodeSettings build() { + InternalActionCodeSettings pigeonReturn = new InternalActionCodeSettings(); pigeonReturn.setUrl(url); pigeonReturn.setDynamicLinkDomain(dynamicLinkDomain); pigeonReturn.setHandleCodeInApp(handleCodeInApp); @@ -1500,8 +2067,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(8); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(8); toListResult.add(url); toListResult.add(dynamicLinkDomain); toListResult.add(handleCodeInApp); @@ -1513,30 +2080,30 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonActionCodeSettings fromList(@NonNull ArrayList __pigeon_list) { - PigeonActionCodeSettings pigeonResult = new PigeonActionCodeSettings(); - Object url = __pigeon_list.get(0); + static @NonNull InternalActionCodeSettings fromList(@NonNull ArrayList pigeonVar_list) { + InternalActionCodeSettings pigeonResult = new InternalActionCodeSettings(); + Object url = pigeonVar_list.get(0); pigeonResult.setUrl((String) url); - Object dynamicLinkDomain = __pigeon_list.get(1); + Object dynamicLinkDomain = pigeonVar_list.get(1); pigeonResult.setDynamicLinkDomain((String) dynamicLinkDomain); - Object handleCodeInApp = __pigeon_list.get(2); + Object handleCodeInApp = pigeonVar_list.get(2); pigeonResult.setHandleCodeInApp((Boolean) handleCodeInApp); - Object iOSBundleId = __pigeon_list.get(3); + Object iOSBundleId = pigeonVar_list.get(3); pigeonResult.setIOSBundleId((String) iOSBundleId); - Object androidPackageName = __pigeon_list.get(4); + Object androidPackageName = pigeonVar_list.get(4); pigeonResult.setAndroidPackageName((String) androidPackageName); - Object androidInstallApp = __pigeon_list.get(5); + Object androidInstallApp = pigeonVar_list.get(5); pigeonResult.setAndroidInstallApp((Boolean) androidInstallApp); - Object androidMinimumVersion = __pigeon_list.get(6); + Object androidMinimumVersion = pigeonVar_list.get(6); pigeonResult.setAndroidMinimumVersion((String) androidMinimumVersion); - Object linkDomain = __pigeon_list.get(7); + Object linkDomain = pigeonVar_list.get(7); pigeonResult.setLinkDomain((String) linkDomain); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonFirebaseAuthSettings { + public static final class InternalFirebaseAuthSettings { private @NonNull Boolean appVerificationDisabledForTesting; public @NonNull Boolean getAppVerificationDisabledForTesting() { @@ -1592,7 +2159,38 @@ public void setForceRecaptchaFlow(@Nullable Boolean setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonFirebaseAuthSettings() {} + InternalFirebaseAuthSettings() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalFirebaseAuthSettings that = (InternalFirebaseAuthSettings) o; + return pigeonDeepEquals( + appVerificationDisabledForTesting, that.appVerificationDisabledForTesting) + && pigeonDeepEquals(userAccessGroup, that.userAccessGroup) + && pigeonDeepEquals(phoneNumber, that.phoneNumber) + && pigeonDeepEquals(smsCode, that.smsCode) + && pigeonDeepEquals(forceRecaptchaFlow, that.forceRecaptchaFlow); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + appVerificationDisabledForTesting, + userAccessGroup, + phoneNumber, + smsCode, + forceRecaptchaFlow + }; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -1636,8 +2234,8 @@ public static final class Builder { return this; } - public @NonNull PigeonFirebaseAuthSettings build() { - PigeonFirebaseAuthSettings pigeonReturn = new PigeonFirebaseAuthSettings(); + public @NonNull InternalFirebaseAuthSettings build() { + InternalFirebaseAuthSettings pigeonReturn = new InternalFirebaseAuthSettings(); pigeonReturn.setAppVerificationDisabledForTesting(appVerificationDisabledForTesting); pigeonReturn.setUserAccessGroup(userAccessGroup); pigeonReturn.setPhoneNumber(phoneNumber); @@ -1648,8 +2246,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(5); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(5); toListResult.add(appVerificationDisabledForTesting); toListResult.add(userAccessGroup); toListResult.add(phoneNumber); @@ -1658,25 +2256,26 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonFirebaseAuthSettings fromList(@NonNull ArrayList __pigeon_list) { - PigeonFirebaseAuthSettings pigeonResult = new PigeonFirebaseAuthSettings(); - Object appVerificationDisabledForTesting = __pigeon_list.get(0); + static @NonNull InternalFirebaseAuthSettings fromList( + @NonNull ArrayList pigeonVar_list) { + InternalFirebaseAuthSettings pigeonResult = new InternalFirebaseAuthSettings(); + Object appVerificationDisabledForTesting = pigeonVar_list.get(0); pigeonResult.setAppVerificationDisabledForTesting( (Boolean) appVerificationDisabledForTesting); - Object userAccessGroup = __pigeon_list.get(1); + Object userAccessGroup = pigeonVar_list.get(1); pigeonResult.setUserAccessGroup((String) userAccessGroup); - Object phoneNumber = __pigeon_list.get(2); + Object phoneNumber = pigeonVar_list.get(2); pigeonResult.setPhoneNumber((String) phoneNumber); - Object smsCode = __pigeon_list.get(3); + Object smsCode = pigeonVar_list.get(3); pigeonResult.setSmsCode((String) smsCode); - Object forceRecaptchaFlow = __pigeon_list.get(4); + Object forceRecaptchaFlow = pigeonVar_list.get(4); pigeonResult.setForceRecaptchaFlow((Boolean) forceRecaptchaFlow); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonSignInProvider { + public static final class InternalSignInProvider { private @NonNull String providerId; public @NonNull String getProviderId() { @@ -1711,7 +2310,27 @@ public void setCustomParameters(@Nullable Map setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonSignInProvider() {} + InternalSignInProvider() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalSignInProvider that = (InternalSignInProvider) o; + return pigeonDeepEquals(providerId, that.providerId) + && pigeonDeepEquals(scopes, that.scopes) + && pigeonDeepEquals(customParameters, that.customParameters); + } + + @Override + public int hashCode() { + Object[] fields = new Object[] {getClass(), providerId, scopes, customParameters}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -1739,8 +2358,8 @@ public static final class Builder { return this; } - public @NonNull PigeonSignInProvider build() { - PigeonSignInProvider pigeonReturn = new PigeonSignInProvider(); + public @NonNull InternalSignInProvider build() { + InternalSignInProvider pigeonReturn = new InternalSignInProvider(); pigeonReturn.setProviderId(providerId); pigeonReturn.setScopes(scopes); pigeonReturn.setCustomParameters(customParameters); @@ -1749,28 +2368,28 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(3); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(3); toListResult.add(providerId); toListResult.add(scopes); toListResult.add(customParameters); return toListResult; } - static @NonNull PigeonSignInProvider fromList(@NonNull ArrayList __pigeon_list) { - PigeonSignInProvider pigeonResult = new PigeonSignInProvider(); - Object providerId = __pigeon_list.get(0); + static @NonNull InternalSignInProvider fromList(@NonNull ArrayList pigeonVar_list) { + InternalSignInProvider pigeonResult = new InternalSignInProvider(); + Object providerId = pigeonVar_list.get(0); pigeonResult.setProviderId((String) providerId); - Object scopes = __pigeon_list.get(1); + Object scopes = pigeonVar_list.get(1); pigeonResult.setScopes((List) scopes); - Object customParameters = __pigeon_list.get(2); + Object customParameters = pigeonVar_list.get(2); pigeonResult.setCustomParameters((Map) customParameters); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonVerifyPhoneNumberRequest { + public static final class InternalVerifyPhoneNumberRequest { private @Nullable String phoneNumber; public @Nullable String getPhoneNumber() { @@ -1835,7 +2454,39 @@ public void setMultiFactorSessionId(@Nullable String setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonVerifyPhoneNumberRequest() {} + InternalVerifyPhoneNumberRequest() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalVerifyPhoneNumberRequest that = (InternalVerifyPhoneNumberRequest) o; + return pigeonDeepEquals(phoneNumber, that.phoneNumber) + && pigeonDeepEquals(timeout, that.timeout) + && pigeonDeepEquals(forceResendingToken, that.forceResendingToken) + && pigeonDeepEquals(autoRetrievedSmsCodeForTesting, that.autoRetrievedSmsCodeForTesting) + && pigeonDeepEquals(multiFactorInfoId, that.multiFactorInfoId) + && pigeonDeepEquals(multiFactorSessionId, that.multiFactorSessionId); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + phoneNumber, + timeout, + forceResendingToken, + autoRetrievedSmsCodeForTesting, + multiFactorInfoId, + multiFactorSessionId + }; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -1887,8 +2538,8 @@ public static final class Builder { return this; } - public @NonNull PigeonVerifyPhoneNumberRequest build() { - PigeonVerifyPhoneNumberRequest pigeonReturn = new PigeonVerifyPhoneNumberRequest(); + public @NonNull InternalVerifyPhoneNumberRequest build() { + InternalVerifyPhoneNumberRequest pigeonReturn = new InternalVerifyPhoneNumberRequest(); pigeonReturn.setPhoneNumber(phoneNumber); pigeonReturn.setTimeout(timeout); pigeonReturn.setForceResendingToken(forceResendingToken); @@ -1900,8 +2551,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(6); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(6); toListResult.add(phoneNumber); toListResult.add(timeout); toListResult.add(forceResendingToken); @@ -1911,35 +2562,27 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonVerifyPhoneNumberRequest fromList( - @NonNull ArrayList __pigeon_list) { - PigeonVerifyPhoneNumberRequest pigeonResult = new PigeonVerifyPhoneNumberRequest(); - Object phoneNumber = __pigeon_list.get(0); + static @NonNull InternalVerifyPhoneNumberRequest fromList( + @NonNull ArrayList pigeonVar_list) { + InternalVerifyPhoneNumberRequest pigeonResult = new InternalVerifyPhoneNumberRequest(); + Object phoneNumber = pigeonVar_list.get(0); pigeonResult.setPhoneNumber((String) phoneNumber); - Object timeout = __pigeon_list.get(1); - pigeonResult.setTimeout( - (timeout == null) - ? null - : ((timeout instanceof Integer) ? (Integer) timeout : (Long) timeout)); - Object forceResendingToken = __pigeon_list.get(2); - pigeonResult.setForceResendingToken( - (forceResendingToken == null) - ? null - : ((forceResendingToken instanceof Integer) - ? (Integer) forceResendingToken - : (Long) forceResendingToken)); - Object autoRetrievedSmsCodeForTesting = __pigeon_list.get(3); + Object timeout = pigeonVar_list.get(1); + pigeonResult.setTimeout((Long) timeout); + Object forceResendingToken = pigeonVar_list.get(2); + pigeonResult.setForceResendingToken((Long) forceResendingToken); + Object autoRetrievedSmsCodeForTesting = pigeonVar_list.get(3); pigeonResult.setAutoRetrievedSmsCodeForTesting((String) autoRetrievedSmsCodeForTesting); - Object multiFactorInfoId = __pigeon_list.get(4); + Object multiFactorInfoId = pigeonVar_list.get(4); pigeonResult.setMultiFactorInfoId((String) multiFactorInfoId); - Object multiFactorSessionId = __pigeon_list.get(5); + Object multiFactorSessionId = pigeonVar_list.get(5); pigeonResult.setMultiFactorSessionId((String) multiFactorSessionId); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonIdTokenResult { + public static final class InternalIdTokenResult { private @Nullable String token; public @Nullable String getToken() { @@ -2010,6 +2653,40 @@ public void setSignInSecondFactor(@Nullable String setterArg) { this.signInSecondFactor = setterArg; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalIdTokenResult that = (InternalIdTokenResult) o; + return pigeonDeepEquals(token, that.token) + && pigeonDeepEquals(expirationTimestamp, that.expirationTimestamp) + && pigeonDeepEquals(authTimestamp, that.authTimestamp) + && pigeonDeepEquals(issuedAtTimestamp, that.issuedAtTimestamp) + && pigeonDeepEquals(signInProvider, that.signInProvider) + && pigeonDeepEquals(claims, that.claims) + && pigeonDeepEquals(signInSecondFactor, that.signInSecondFactor); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + token, + expirationTimestamp, + authTimestamp, + issuedAtTimestamp, + signInProvider, + claims, + signInSecondFactor + }; + return pigeonDeepHashCode(fields); + } + public static final class Builder { private @Nullable String token; @@ -2068,8 +2745,8 @@ public static final class Builder { return this; } - public @NonNull PigeonIdTokenResult build() { - PigeonIdTokenResult pigeonReturn = new PigeonIdTokenResult(); + public @NonNull InternalIdTokenResult build() { + InternalIdTokenResult pigeonReturn = new InternalIdTokenResult(); pigeonReturn.setToken(token); pigeonReturn.setExpirationTimestamp(expirationTimestamp); pigeonReturn.setAuthTimestamp(authTimestamp); @@ -2082,8 +2759,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(7); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(7); toListResult.add(token); toListResult.add(expirationTimestamp); toListResult.add(authTimestamp); @@ -2094,43 +2771,28 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonIdTokenResult fromList(@NonNull ArrayList __pigeon_list) { - PigeonIdTokenResult pigeonResult = new PigeonIdTokenResult(); - Object token = __pigeon_list.get(0); + static @NonNull InternalIdTokenResult fromList(@NonNull ArrayList pigeonVar_list) { + InternalIdTokenResult pigeonResult = new InternalIdTokenResult(); + Object token = pigeonVar_list.get(0); pigeonResult.setToken((String) token); - Object expirationTimestamp = __pigeon_list.get(1); - pigeonResult.setExpirationTimestamp( - (expirationTimestamp == null) - ? null - : ((expirationTimestamp instanceof Integer) - ? (Integer) expirationTimestamp - : (Long) expirationTimestamp)); - Object authTimestamp = __pigeon_list.get(2); - pigeonResult.setAuthTimestamp( - (authTimestamp == null) - ? null - : ((authTimestamp instanceof Integer) - ? (Integer) authTimestamp - : (Long) authTimestamp)); - Object issuedAtTimestamp = __pigeon_list.get(3); - pigeonResult.setIssuedAtTimestamp( - (issuedAtTimestamp == null) - ? null - : ((issuedAtTimestamp instanceof Integer) - ? (Integer) issuedAtTimestamp - : (Long) issuedAtTimestamp)); - Object signInProvider = __pigeon_list.get(4); + Object expirationTimestamp = pigeonVar_list.get(1); + pigeonResult.setExpirationTimestamp((Long) expirationTimestamp); + Object authTimestamp = pigeonVar_list.get(2); + pigeonResult.setAuthTimestamp((Long) authTimestamp); + Object issuedAtTimestamp = pigeonVar_list.get(3); + pigeonResult.setIssuedAtTimestamp((Long) issuedAtTimestamp); + Object signInProvider = pigeonVar_list.get(4); pigeonResult.setSignInProvider((String) signInProvider); - Object claims = __pigeon_list.get(5); + Object claims = pigeonVar_list.get(5); pigeonResult.setClaims((Map) claims); - Object signInSecondFactor = __pigeon_list.get(6); + Object signInSecondFactor = pigeonVar_list.get(6); pigeonResult.setSignInSecondFactor((String) signInSecondFactor); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonUserProfile { + public static final class InternalUserProfile { private @Nullable String displayName; public @Nullable String getDisplayName() { @@ -2178,7 +2840,29 @@ public void setPhotoUrlChanged(@NonNull Boolean setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonUserProfile() {} + InternalUserProfile() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalUserProfile that = (InternalUserProfile) o; + return pigeonDeepEquals(displayName, that.displayName) + && pigeonDeepEquals(photoUrl, that.photoUrl) + && pigeonDeepEquals(displayNameChanged, that.displayNameChanged) + && pigeonDeepEquals(photoUrlChanged, that.photoUrlChanged); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] {getClass(), displayName, photoUrl, displayNameChanged, photoUrlChanged}; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -2214,8 +2898,8 @@ public static final class Builder { return this; } - public @NonNull PigeonUserProfile build() { - PigeonUserProfile pigeonReturn = new PigeonUserProfile(); + public @NonNull InternalUserProfile build() { + InternalUserProfile pigeonReturn = new InternalUserProfile(); pigeonReturn.setDisplayName(displayName); pigeonReturn.setPhotoUrl(photoUrl); pigeonReturn.setDisplayNameChanged(displayNameChanged); @@ -2225,8 +2909,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(4); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(4); toListResult.add(displayName); toListResult.add(photoUrl); toListResult.add(displayNameChanged); @@ -2234,22 +2918,22 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonUserProfile fromList(@NonNull ArrayList __pigeon_list) { - PigeonUserProfile pigeonResult = new PigeonUserProfile(); - Object displayName = __pigeon_list.get(0); + static @NonNull InternalUserProfile fromList(@NonNull ArrayList pigeonVar_list) { + InternalUserProfile pigeonResult = new InternalUserProfile(); + Object displayName = pigeonVar_list.get(0); pigeonResult.setDisplayName((String) displayName); - Object photoUrl = __pigeon_list.get(1); + Object photoUrl = pigeonVar_list.get(1); pigeonResult.setPhotoUrl((String) photoUrl); - Object displayNameChanged = __pigeon_list.get(2); + Object displayNameChanged = pigeonVar_list.get(2); pigeonResult.setDisplayNameChanged((Boolean) displayNameChanged); - Object photoUrlChanged = __pigeon_list.get(3); + Object photoUrlChanged = pigeonVar_list.get(3); pigeonResult.setPhotoUrlChanged((Boolean) photoUrlChanged); return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class PigeonTotpSecret { + public static final class InternalTotpSecret { private @Nullable Long codeIntervalSeconds; public @Nullable Long getCodeIntervalSeconds() { @@ -2304,7 +2988,37 @@ public void setSecretKey(@NonNull String setterArg) { } /** Constructor is non-public to enforce null safety; use Builder. */ - PigeonTotpSecret() {} + InternalTotpSecret() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InternalTotpSecret that = (InternalTotpSecret) o; + return pigeonDeepEquals(codeIntervalSeconds, that.codeIntervalSeconds) + && pigeonDeepEquals(codeLength, that.codeLength) + && pigeonDeepEquals(enrollmentCompletionDeadline, that.enrollmentCompletionDeadline) + && pigeonDeepEquals(hashingAlgorithm, that.hashingAlgorithm) + && pigeonDeepEquals(secretKey, that.secretKey); + } + + @Override + public int hashCode() { + Object[] fields = + new Object[] { + getClass(), + codeIntervalSeconds, + codeLength, + enrollmentCompletionDeadline, + hashingAlgorithm, + secretKey + }; + return pigeonDeepHashCode(fields); + } public static final class Builder { @@ -2348,8 +3062,8 @@ public static final class Builder { return this; } - public @NonNull PigeonTotpSecret build() { - PigeonTotpSecret pigeonReturn = new PigeonTotpSecret(); + public @NonNull InternalTotpSecret build() { + InternalTotpSecret pigeonReturn = new InternalTotpSecret(); pigeonReturn.setCodeIntervalSeconds(codeIntervalSeconds); pigeonReturn.setCodeLength(codeLength); pigeonReturn.setEnrollmentCompletionDeadline(enrollmentCompletionDeadline); @@ -2360,8 +3074,8 @@ public static final class Builder { } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList(5); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(5); toListResult.add(codeIntervalSeconds); toListResult.add(codeLength); toListResult.add(enrollmentCompletionDeadline); @@ -2370,104 +3084,75 @@ ArrayList toList() { return toListResult; } - static @NonNull PigeonTotpSecret fromList(@NonNull ArrayList __pigeon_list) { - PigeonTotpSecret pigeonResult = new PigeonTotpSecret(); - Object codeIntervalSeconds = __pigeon_list.get(0); - pigeonResult.setCodeIntervalSeconds( - (codeIntervalSeconds == null) - ? null - : ((codeIntervalSeconds instanceof Integer) - ? (Integer) codeIntervalSeconds - : (Long) codeIntervalSeconds)); - Object codeLength = __pigeon_list.get(1); - pigeonResult.setCodeLength( - (codeLength == null) - ? null - : ((codeLength instanceof Integer) ? (Integer) codeLength : (Long) codeLength)); - Object enrollmentCompletionDeadline = __pigeon_list.get(2); - pigeonResult.setEnrollmentCompletionDeadline( - (enrollmentCompletionDeadline == null) - ? null - : ((enrollmentCompletionDeadline instanceof Integer) - ? (Integer) enrollmentCompletionDeadline - : (Long) enrollmentCompletionDeadline)); - Object hashingAlgorithm = __pigeon_list.get(3); + static @NonNull InternalTotpSecret fromList(@NonNull ArrayList pigeonVar_list) { + InternalTotpSecret pigeonResult = new InternalTotpSecret(); + Object codeIntervalSeconds = pigeonVar_list.get(0); + pigeonResult.setCodeIntervalSeconds((Long) codeIntervalSeconds); + Object codeLength = pigeonVar_list.get(1); + pigeonResult.setCodeLength((Long) codeLength); + Object enrollmentCompletionDeadline = pigeonVar_list.get(2); + pigeonResult.setEnrollmentCompletionDeadline((Long) enrollmentCompletionDeadline); + Object hashingAlgorithm = pigeonVar_list.get(3); pigeonResult.setHashingAlgorithm((String) hashingAlgorithm); - Object secretKey = __pigeon_list.get(4); + Object secretKey = pigeonVar_list.get(4); pigeonResult.setSecretKey((String) secretKey); return pigeonResult; } } - /** Asynchronous error handling return type for non-nullable API method returns. */ - public interface Result { - /** Success case callback method for handling returns. */ - void success(@NonNull T result); - - /** Failure case callback method for handling errors. */ - void error(@NonNull Throwable error); - } - /** Asynchronous error handling return type for nullable API method returns. */ - public interface NullableResult { - /** Success case callback method for handling returns. */ - void success(@Nullable T result); - - /** Failure case callback method for handling errors. */ - void error(@NonNull Throwable error); - } - /** Asynchronous error handling return type for void API method returns. */ - public interface VoidResult { - /** Success case callback method for handling returns. */ - void success(); - - /** Failure case callback method for handling errors. */ - void error(@NonNull Throwable error); - } - - private static class FirebaseAuthHostApiCodec extends StandardMessageCodec { - public static final FirebaseAuthHostApiCodec INSTANCE = new FirebaseAuthHostApiCodec(); + private static class PigeonCodec extends StandardMessageCodec { + public static final PigeonCodec INSTANCE = new PigeonCodec(); - private FirebaseAuthHostApiCodec() {} + private PigeonCodec() {} @Override protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 128: - return AuthPigeonFirebaseApp.fromList((ArrayList) readValue(buffer)); case (byte) 129: - return PigeonActionCodeInfo.fromList((ArrayList) readValue(buffer)); + { + Object value = readValue(buffer); + return value == null + ? null + : ActionCodeInfoOperation.values()[((Long) value).intValue()]; + } case (byte) 130: - return PigeonActionCodeInfoData.fromList((ArrayList) readValue(buffer)); + return InternalMultiFactorSession.fromList((ArrayList) readValue(buffer)); case (byte) 131: - return PigeonActionCodeSettings.fromList((ArrayList) readValue(buffer)); + return InternalPhoneMultiFactorAssertion.fromList((ArrayList) readValue(buffer)); case (byte) 132: - return PigeonAdditionalUserInfo.fromList((ArrayList) readValue(buffer)); + return InternalMultiFactorInfo.fromList((ArrayList) readValue(buffer)); case (byte) 133: - return PigeonAuthCredential.fromList((ArrayList) readValue(buffer)); + return AuthPigeonFirebaseApp.fromList((ArrayList) readValue(buffer)); case (byte) 134: - return PigeonFirebaseAuthSettings.fromList((ArrayList) readValue(buffer)); + return InternalActionCodeInfoData.fromList((ArrayList) readValue(buffer)); case (byte) 135: - return PigeonIdTokenResult.fromList((ArrayList) readValue(buffer)); + return InternalActionCodeInfo.fromList((ArrayList) readValue(buffer)); case (byte) 136: - return PigeonMultiFactorInfo.fromList((ArrayList) readValue(buffer)); + return InternalAdditionalUserInfo.fromList((ArrayList) readValue(buffer)); case (byte) 137: - return PigeonMultiFactorSession.fromList((ArrayList) readValue(buffer)); + return InternalAuthCredential.fromList((ArrayList) readValue(buffer)); case (byte) 138: - return PigeonPhoneMultiFactorAssertion.fromList((ArrayList) readValue(buffer)); + return InternalUserInfo.fromList((ArrayList) readValue(buffer)); case (byte) 139: - return PigeonSignInProvider.fromList((ArrayList) readValue(buffer)); + return InternalUserDetails.fromList((ArrayList) readValue(buffer)); case (byte) 140: - return PigeonTotpSecret.fromList((ArrayList) readValue(buffer)); + return InternalUserCredential.fromList((ArrayList) readValue(buffer)); case (byte) 141: - return PigeonUserCredential.fromList((ArrayList) readValue(buffer)); + return InternalAuthCredentialInput.fromList((ArrayList) readValue(buffer)); case (byte) 142: - return PigeonUserDetails.fromList((ArrayList) readValue(buffer)); + return InternalActionCodeSettings.fromList((ArrayList) readValue(buffer)); case (byte) 143: - return PigeonUserInfo.fromList((ArrayList) readValue(buffer)); + return InternalFirebaseAuthSettings.fromList((ArrayList) readValue(buffer)); case (byte) 144: - return PigeonUserProfile.fromList((ArrayList) readValue(buffer)); + return InternalSignInProvider.fromList((ArrayList) readValue(buffer)); case (byte) 145: - return PigeonVerifyPhoneNumberRequest.fromList((ArrayList) readValue(buffer)); + return InternalVerifyPhoneNumberRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 146: + return InternalIdTokenResult.fromList((ArrayList) readValue(buffer)); + case (byte) 147: + return InternalUserProfile.fromList((ArrayList) readValue(buffer)); + case (byte) 148: + return InternalTotpSecret.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); } @@ -2475,66 +3160,99 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { @Override protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof AuthPigeonFirebaseApp) { - stream.write(128); - writeValue(stream, ((AuthPigeonFirebaseApp) value).toList()); - } else if (value instanceof PigeonActionCodeInfo) { + if (value instanceof ActionCodeInfoOperation) { stream.write(129); - writeValue(stream, ((PigeonActionCodeInfo) value).toList()); - } else if (value instanceof PigeonActionCodeInfoData) { + writeValue(stream, value == null ? null : ((ActionCodeInfoOperation) value).index); + } else if (value instanceof InternalMultiFactorSession) { stream.write(130); - writeValue(stream, ((PigeonActionCodeInfoData) value).toList()); - } else if (value instanceof PigeonActionCodeSettings) { + writeValue(stream, ((InternalMultiFactorSession) value).toList()); + } else if (value instanceof InternalPhoneMultiFactorAssertion) { stream.write(131); - writeValue(stream, ((PigeonActionCodeSettings) value).toList()); - } else if (value instanceof PigeonAdditionalUserInfo) { + writeValue(stream, ((InternalPhoneMultiFactorAssertion) value).toList()); + } else if (value instanceof InternalMultiFactorInfo) { stream.write(132); - writeValue(stream, ((PigeonAdditionalUserInfo) value).toList()); - } else if (value instanceof PigeonAuthCredential) { + writeValue(stream, ((InternalMultiFactorInfo) value).toList()); + } else if (value instanceof AuthPigeonFirebaseApp) { stream.write(133); - writeValue(stream, ((PigeonAuthCredential) value).toList()); - } else if (value instanceof PigeonFirebaseAuthSettings) { + writeValue(stream, ((AuthPigeonFirebaseApp) value).toList()); + } else if (value instanceof InternalActionCodeInfoData) { stream.write(134); - writeValue(stream, ((PigeonFirebaseAuthSettings) value).toList()); - } else if (value instanceof PigeonIdTokenResult) { + writeValue(stream, ((InternalActionCodeInfoData) value).toList()); + } else if (value instanceof InternalActionCodeInfo) { stream.write(135); - writeValue(stream, ((PigeonIdTokenResult) value).toList()); - } else if (value instanceof PigeonMultiFactorInfo) { + writeValue(stream, ((InternalActionCodeInfo) value).toList()); + } else if (value instanceof InternalAdditionalUserInfo) { stream.write(136); - writeValue(stream, ((PigeonMultiFactorInfo) value).toList()); - } else if (value instanceof PigeonMultiFactorSession) { + writeValue(stream, ((InternalAdditionalUserInfo) value).toList()); + } else if (value instanceof InternalAuthCredential) { stream.write(137); - writeValue(stream, ((PigeonMultiFactorSession) value).toList()); - } else if (value instanceof PigeonPhoneMultiFactorAssertion) { + writeValue(stream, ((InternalAuthCredential) value).toList()); + } else if (value instanceof InternalUserInfo) { stream.write(138); - writeValue(stream, ((PigeonPhoneMultiFactorAssertion) value).toList()); - } else if (value instanceof PigeonSignInProvider) { + writeValue(stream, ((InternalUserInfo) value).toList()); + } else if (value instanceof InternalUserDetails) { stream.write(139); - writeValue(stream, ((PigeonSignInProvider) value).toList()); - } else if (value instanceof PigeonTotpSecret) { + writeValue(stream, ((InternalUserDetails) value).toList()); + } else if (value instanceof InternalUserCredential) { stream.write(140); - writeValue(stream, ((PigeonTotpSecret) value).toList()); - } else if (value instanceof PigeonUserCredential) { + writeValue(stream, ((InternalUserCredential) value).toList()); + } else if (value instanceof InternalAuthCredentialInput) { stream.write(141); - writeValue(stream, ((PigeonUserCredential) value).toList()); - } else if (value instanceof PigeonUserDetails) { + writeValue(stream, ((InternalAuthCredentialInput) value).toList()); + } else if (value instanceof InternalActionCodeSettings) { stream.write(142); - writeValue(stream, ((PigeonUserDetails) value).toList()); - } else if (value instanceof PigeonUserInfo) { + writeValue(stream, ((InternalActionCodeSettings) value).toList()); + } else if (value instanceof InternalFirebaseAuthSettings) { stream.write(143); - writeValue(stream, ((PigeonUserInfo) value).toList()); - } else if (value instanceof PigeonUserProfile) { + writeValue(stream, ((InternalFirebaseAuthSettings) value).toList()); + } else if (value instanceof InternalSignInProvider) { stream.write(144); - writeValue(stream, ((PigeonUserProfile) value).toList()); - } else if (value instanceof PigeonVerifyPhoneNumberRequest) { + writeValue(stream, ((InternalSignInProvider) value).toList()); + } else if (value instanceof InternalVerifyPhoneNumberRequest) { stream.write(145); - writeValue(stream, ((PigeonVerifyPhoneNumberRequest) value).toList()); + writeValue(stream, ((InternalVerifyPhoneNumberRequest) value).toList()); + } else if (value instanceof InternalIdTokenResult) { + stream.write(146); + writeValue(stream, ((InternalIdTokenResult) value).toList()); + } else if (value instanceof InternalUserProfile) { + stream.write(147); + writeValue(stream, ((InternalUserProfile) value).toList()); + } else if (value instanceof InternalTotpSecret) { + stream.write(148); + writeValue(stream, ((InternalTotpSecret) value).toList()); } else { super.writeValue(stream, value); } } } + /** Asynchronous error handling return type for non-nullable API method returns. */ + public interface Result { + /** Success case callback method for handling returns. */ + void success(@NonNull T result); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + + /** Asynchronous error handling return type for nullable API method returns. */ + public interface NullableResult { + /** Success case callback method for handling returns. */ + void success(@Nullable T result); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + + /** Asynchronous error handling return type for void API method returns. */ + public interface VoidResult { + /** Success case callback method for handling returns. */ + void success(); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FirebaseAuthHostApi { @@ -2556,7 +3274,7 @@ void applyActionCode( void checkActionCode( @NonNull AuthPigeonFirebaseApp app, @NonNull String code, - @NonNull Result result); + @NonNull Result result); void confirmPasswordReset( @NonNull AuthPigeonFirebaseApp app, @@ -2568,37 +3286,37 @@ void createUserWithEmailAndPassword( @NonNull AuthPigeonFirebaseApp app, @NonNull String email, @NonNull String password, - @NonNull Result result); + @NonNull Result result); void signInAnonymously( - @NonNull AuthPigeonFirebaseApp app, @NonNull Result result); + @NonNull AuthPigeonFirebaseApp app, @NonNull Result result); void signInWithCredential( @NonNull AuthPigeonFirebaseApp app, @NonNull Map input, - @NonNull Result result); + @NonNull Result result); void signInWithCustomToken( @NonNull AuthPigeonFirebaseApp app, @NonNull String token, - @NonNull Result result); + @NonNull Result result); void signInWithEmailAndPassword( @NonNull AuthPigeonFirebaseApp app, @NonNull String email, @NonNull String password, - @NonNull Result result); + @NonNull Result result); void signInWithEmailLink( @NonNull AuthPigeonFirebaseApp app, @NonNull String email, @NonNull String emailLink, - @NonNull Result result); + @NonNull Result result); void signInWithProvider( @NonNull AuthPigeonFirebaseApp app, - @NonNull PigeonSignInProvider signInProvider, - @NonNull Result result); + @NonNull InternalSignInProvider signInProvider, + @NonNull Result result); void signOut(@NonNull AuthPigeonFirebaseApp app, @NonNull VoidResult result); @@ -2610,13 +3328,13 @@ void fetchSignInMethodsForEmail( void sendPasswordResetEmail( @NonNull AuthPigeonFirebaseApp app, @NonNull String email, - @Nullable PigeonActionCodeSettings actionCodeSettings, + @Nullable InternalActionCodeSettings actionCodeSettings, @NonNull VoidResult result); void sendSignInLinkToEmail( @NonNull AuthPigeonFirebaseApp app, @NonNull String email, - @NonNull PigeonActionCodeSettings actionCodeSettings, + @NonNull InternalActionCodeSettings actionCodeSettings, @NonNull VoidResult result); void setLanguageCode( @@ -2626,7 +3344,7 @@ void setLanguageCode( void setSettings( @NonNull AuthPigeonFirebaseApp app, - @NonNull PigeonFirebaseAuthSettings settings, + @NonNull InternalFirebaseAuthSettings settings, @NonNull VoidResult result); void verifyPasswordResetCode( @@ -2634,7 +3352,7 @@ void verifyPasswordResetCode( void verifyPhoneNumber( @NonNull AuthPigeonFirebaseApp app, - @NonNull PigeonVerifyPhoneNumberRequest request, + @NonNull InternalVerifyPhoneNumberRequest request, @NonNull Result result); void revokeTokenWithAuthorizationCode( @@ -2642,12 +3360,18 @@ void revokeTokenWithAuthorizationCode( @NonNull String authorizationCode, @NonNull VoidResult result); + void revokeAccessToken( + @NonNull AuthPigeonFirebaseApp app, + @NonNull String accessToken, + @NonNull VoidResult result); + void initializeRecaptchaConfig(@NonNull AuthPigeonFirebaseApp app, @NonNull VoidResult result); /** The codec used by FirebaseAuthHostApi. */ static @NonNull MessageCodec getCodec() { - return FirebaseAuthHostApiCodec.INSTANCE; + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `FirebaseAuthHostApi` to handle messages through the * `binaryMessenger`. @@ -2671,7 +3395,7 @@ static void setUp( if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); Result resultCallback = @@ -2703,7 +3427,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); Result resultCallback = @@ -2735,11 +3459,11 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String hostArg = (String) args.get(1); - Number portArg = (Number) args.get(2); + Long portArg = (Long) args.get(2); VoidResult resultCallback = new VoidResult() { public void success() { @@ -2753,11 +3477,7 @@ public void error(Throwable error) { } }; - api.useEmulator( - appArg, - hostArg, - (portArg == null) ? null : portArg.longValue(), - resultCallback); + api.useEmulator(appArg, hostArg, portArg, resultCallback); }); } else { channel.setMessageHandler(null); @@ -2773,7 +3493,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String codeArg = (String) args.get(1); @@ -2806,13 +3526,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String codeArg = (String) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonActionCodeInfo result) { + Result resultCallback = + new Result() { + public void success(InternalActionCodeInfo result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -2839,7 +3559,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String codeArg = (String) args.get(1); @@ -2873,14 +3593,14 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String emailArg = (String) args.get(1); String passwordArg = (String) args.get(2); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -2907,12 +3627,12 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -2939,13 +3659,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); Map inputArg = (Map) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -2972,13 +3692,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String tokenArg = (String) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3005,14 +3725,14 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String emailArg = (String) args.get(1); String passwordArg = (String) args.get(2); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3039,14 +3759,14 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String emailArg = (String) args.get(1); String emailLinkArg = (String) args.get(2); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3073,13 +3793,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonSignInProvider signInProviderArg = (PigeonSignInProvider) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + InternalSignInProvider signInProviderArg = (InternalSignInProvider) args.get(1); + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3106,7 +3826,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); VoidResult resultCallback = @@ -3138,7 +3858,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String emailArg = (String) args.get(1); @@ -3171,12 +3891,12 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String emailArg = (String) args.get(1); - PigeonActionCodeSettings actionCodeSettingsArg = - (PigeonActionCodeSettings) args.get(2); + InternalActionCodeSettings actionCodeSettingsArg = + (InternalActionCodeSettings) args.get(2); VoidResult resultCallback = new VoidResult() { public void success() { @@ -3206,12 +3926,12 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String emailArg = (String) args.get(1); - PigeonActionCodeSettings actionCodeSettingsArg = - (PigeonActionCodeSettings) args.get(2); + InternalActionCodeSettings actionCodeSettingsArg = + (InternalActionCodeSettings) args.get(2); VoidResult resultCallback = new VoidResult() { public void success() { @@ -3241,7 +3961,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String languageCodeArg = (String) args.get(1); @@ -3274,10 +3994,11 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonFirebaseAuthSettings settingsArg = (PigeonFirebaseAuthSettings) args.get(1); + InternalFirebaseAuthSettings settingsArg = + (InternalFirebaseAuthSettings) args.get(1); VoidResult resultCallback = new VoidResult() { public void success() { @@ -3307,7 +4028,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String codeArg = (String) args.get(1); @@ -3340,11 +4061,11 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonVerifyPhoneNumberRequest requestArg = - (PigeonVerifyPhoneNumberRequest) args.get(1); + InternalVerifyPhoneNumberRequest requestArg = + (InternalVerifyPhoneNumberRequest) args.get(1); Result resultCallback = new Result() { public void success(String result) { @@ -3374,7 +4095,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String authorizationCodeArg = (String) args.get(1); @@ -3401,15 +4122,16 @@ public void error(Throwable error) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig" + "dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeAccessToken" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); + String accessTokenArg = (String) args.get(1); VoidResult resultCallback = new VoidResult() { public void success() { @@ -3423,122 +4145,43 @@ public void error(Throwable error) { } }; - api.initializeRecaptchaConfig(appArg, resultCallback); + api.revokeAccessToken(appArg, accessTokenArg, resultCallback); }); } else { channel.setMessageHandler(null); } } - } - } - - private static class FirebaseAuthUserHostApiCodec extends StandardMessageCodec { - public static final FirebaseAuthUserHostApiCodec INSTANCE = new FirebaseAuthUserHostApiCodec(); - - private FirebaseAuthUserHostApiCodec() {} + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + ArrayList args = (ArrayList) message; + AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); + VoidResult resultCallback = + new VoidResult() { + public void success() { + wrapped.add(0, null); + reply.reply(wrapped); + } - @Override - protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { - switch (type) { - case (byte) 128: - return AuthPigeonFirebaseApp.fromList((ArrayList) readValue(buffer)); - case (byte) 129: - return PigeonActionCodeInfo.fromList((ArrayList) readValue(buffer)); - case (byte) 130: - return PigeonActionCodeInfoData.fromList((ArrayList) readValue(buffer)); - case (byte) 131: - return PigeonActionCodeSettings.fromList((ArrayList) readValue(buffer)); - case (byte) 132: - return PigeonAdditionalUserInfo.fromList((ArrayList) readValue(buffer)); - case (byte) 133: - return PigeonAuthCredential.fromList((ArrayList) readValue(buffer)); - case (byte) 134: - return PigeonFirebaseAuthSettings.fromList((ArrayList) readValue(buffer)); - case (byte) 135: - return PigeonIdTokenResult.fromList((ArrayList) readValue(buffer)); - case (byte) 136: - return PigeonMultiFactorInfo.fromList((ArrayList) readValue(buffer)); - case (byte) 137: - return PigeonMultiFactorSession.fromList((ArrayList) readValue(buffer)); - case (byte) 138: - return PigeonPhoneMultiFactorAssertion.fromList((ArrayList) readValue(buffer)); - case (byte) 139: - return PigeonSignInProvider.fromList((ArrayList) readValue(buffer)); - case (byte) 140: - return PigeonTotpSecret.fromList((ArrayList) readValue(buffer)); - case (byte) 141: - return PigeonUserCredential.fromList((ArrayList) readValue(buffer)); - case (byte) 142: - return PigeonUserDetails.fromList((ArrayList) readValue(buffer)); - case (byte) 143: - return PigeonUserInfo.fromList((ArrayList) readValue(buffer)); - case (byte) 144: - return PigeonUserProfile.fromList((ArrayList) readValue(buffer)); - case (byte) 145: - return PigeonVerifyPhoneNumberRequest.fromList((ArrayList) readValue(buffer)); - default: - return super.readValueOfType(type, buffer); - } - } + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; - @Override - protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof AuthPigeonFirebaseApp) { - stream.write(128); - writeValue(stream, ((AuthPigeonFirebaseApp) value).toList()); - } else if (value instanceof PigeonActionCodeInfo) { - stream.write(129); - writeValue(stream, ((PigeonActionCodeInfo) value).toList()); - } else if (value instanceof PigeonActionCodeInfoData) { - stream.write(130); - writeValue(stream, ((PigeonActionCodeInfoData) value).toList()); - } else if (value instanceof PigeonActionCodeSettings) { - stream.write(131); - writeValue(stream, ((PigeonActionCodeSettings) value).toList()); - } else if (value instanceof PigeonAdditionalUserInfo) { - stream.write(132); - writeValue(stream, ((PigeonAdditionalUserInfo) value).toList()); - } else if (value instanceof PigeonAuthCredential) { - stream.write(133); - writeValue(stream, ((PigeonAuthCredential) value).toList()); - } else if (value instanceof PigeonFirebaseAuthSettings) { - stream.write(134); - writeValue(stream, ((PigeonFirebaseAuthSettings) value).toList()); - } else if (value instanceof PigeonIdTokenResult) { - stream.write(135); - writeValue(stream, ((PigeonIdTokenResult) value).toList()); - } else if (value instanceof PigeonMultiFactorInfo) { - stream.write(136); - writeValue(stream, ((PigeonMultiFactorInfo) value).toList()); - } else if (value instanceof PigeonMultiFactorSession) { - stream.write(137); - writeValue(stream, ((PigeonMultiFactorSession) value).toList()); - } else if (value instanceof PigeonPhoneMultiFactorAssertion) { - stream.write(138); - writeValue(stream, ((PigeonPhoneMultiFactorAssertion) value).toList()); - } else if (value instanceof PigeonSignInProvider) { - stream.write(139); - writeValue(stream, ((PigeonSignInProvider) value).toList()); - } else if (value instanceof PigeonTotpSecret) { - stream.write(140); - writeValue(stream, ((PigeonTotpSecret) value).toList()); - } else if (value instanceof PigeonUserCredential) { - stream.write(141); - writeValue(stream, ((PigeonUserCredential) value).toList()); - } else if (value instanceof PigeonUserDetails) { - stream.write(142); - writeValue(stream, ((PigeonUserDetails) value).toList()); - } else if (value instanceof PigeonUserInfo) { - stream.write(143); - writeValue(stream, ((PigeonUserInfo) value).toList()); - } else if (value instanceof PigeonUserProfile) { - stream.write(144); - writeValue(stream, ((PigeonUserProfile) value).toList()); - } else if (value instanceof PigeonVerifyPhoneNumberRequest) { - stream.write(145); - writeValue(stream, ((PigeonVerifyPhoneNumberRequest) value).toList()); - } else { - super.writeValue(stream, value); + api.initializeRecaptchaConfig(appArg, resultCallback); + }); + } else { + channel.setMessageHandler(null); + } } } } @@ -3551,70 +4194,71 @@ public interface FirebaseAuthUserHostApi { void getIdToken( @NonNull AuthPigeonFirebaseApp app, @NonNull Boolean forceRefresh, - @NonNull Result result); + @NonNull Result result); void linkWithCredential( @NonNull AuthPigeonFirebaseApp app, @NonNull Map input, - @NonNull Result result); + @NonNull Result result); void linkWithProvider( @NonNull AuthPigeonFirebaseApp app, - @NonNull PigeonSignInProvider signInProvider, - @NonNull Result result); + @NonNull InternalSignInProvider signInProvider, + @NonNull Result result); void reauthenticateWithCredential( @NonNull AuthPigeonFirebaseApp app, @NonNull Map input, - @NonNull Result result); + @NonNull Result result); void reauthenticateWithProvider( @NonNull AuthPigeonFirebaseApp app, - @NonNull PigeonSignInProvider signInProvider, - @NonNull Result result); + @NonNull InternalSignInProvider signInProvider, + @NonNull Result result); - void reload(@NonNull AuthPigeonFirebaseApp app, @NonNull Result result); + void reload(@NonNull AuthPigeonFirebaseApp app, @NonNull Result result); void sendEmailVerification( @NonNull AuthPigeonFirebaseApp app, - @Nullable PigeonActionCodeSettings actionCodeSettings, + @Nullable InternalActionCodeSettings actionCodeSettings, @NonNull VoidResult result); void unlink( @NonNull AuthPigeonFirebaseApp app, @NonNull String providerId, - @NonNull Result result); + @NonNull Result result); void updateEmail( @NonNull AuthPigeonFirebaseApp app, @NonNull String newEmail, - @NonNull Result result); + @NonNull Result result); void updatePassword( @NonNull AuthPigeonFirebaseApp app, @NonNull String newPassword, - @NonNull Result result); + @NonNull Result result); void updatePhoneNumber( @NonNull AuthPigeonFirebaseApp app, @NonNull Map input, - @NonNull Result result); + @NonNull Result result); void updateProfile( @NonNull AuthPigeonFirebaseApp app, - @NonNull PigeonUserProfile profile, - @NonNull Result result); + @NonNull InternalUserProfile profile, + @NonNull Result result); void verifyBeforeUpdateEmail( @NonNull AuthPigeonFirebaseApp app, @NonNull String newEmail, - @Nullable PigeonActionCodeSettings actionCodeSettings, + @Nullable InternalActionCodeSettings actionCodeSettings, @NonNull VoidResult result); /** The codec used by FirebaseAuthUserHostApi. */ static @NonNull MessageCodec getCodec() { - return FirebaseAuthUserHostApiCodec.INSTANCE; + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `FirebaseAuthUserHostApi` to handle messages through the * `binaryMessenger`. @@ -3639,7 +4283,7 @@ static void setUp( if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); VoidResult resultCallback = @@ -3671,13 +4315,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); Boolean forceRefreshArg = (Boolean) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonIdTokenResult result) { + Result resultCallback = + new Result() { + public void success(InternalIdTokenResult result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3704,13 +4348,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); Map inputArg = (Map) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3737,13 +4381,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonSignInProvider signInProviderArg = (PigeonSignInProvider) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + InternalSignInProvider signInProviderArg = (InternalSignInProvider) args.get(1); + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3770,13 +4414,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); Map inputArg = (Map) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3803,13 +4447,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonSignInProvider signInProviderArg = (PigeonSignInProvider) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + InternalSignInProvider signInProviderArg = (InternalSignInProvider) args.get(1); + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3836,12 +4480,12 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(PigeonUserDetails result) { + Result resultCallback = + new Result() { + public void success(InternalUserDetails result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3868,11 +4512,11 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonActionCodeSettings actionCodeSettingsArg = - (PigeonActionCodeSettings) args.get(1); + InternalActionCodeSettings actionCodeSettingsArg = + (InternalActionCodeSettings) args.get(1); VoidResult resultCallback = new VoidResult() { public void success() { @@ -3902,13 +4546,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String providerIdArg = (String) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3935,13 +4579,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String newEmailArg = (String) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserDetails result) { + Result resultCallback = + new Result() { + public void success(InternalUserDetails result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -3968,13 +4612,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String newPasswordArg = (String) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserDetails result) { + Result resultCallback = + new Result() { + public void success(InternalUserDetails result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -4001,13 +4645,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); Map inputArg = (Map) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserDetails result) { + Result resultCallback = + new Result() { + public void success(InternalUserDetails result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -4034,13 +4678,13 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonUserProfile profileArg = (PigeonUserProfile) args.get(1); - Result resultCallback = - new Result() { - public void success(PigeonUserDetails result) { + InternalUserProfile profileArg = (InternalUserProfile) args.get(1); + Result resultCallback = + new Result() { + public void success(InternalUserDetails result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -4067,12 +4711,12 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String newEmailArg = (String) args.get(1); - PigeonActionCodeSettings actionCodeSettingsArg = - (PigeonActionCodeSettings) args.get(2); + InternalActionCodeSettings actionCodeSettingsArg = + (InternalActionCodeSettings) args.get(2); VoidResult resultCallback = new VoidResult() { public void success() { @@ -4096,53 +4740,12 @@ public void error(Throwable error) { } } - private static class MultiFactorUserHostApiCodec extends StandardMessageCodec { - public static final MultiFactorUserHostApiCodec INSTANCE = new MultiFactorUserHostApiCodec(); - - private MultiFactorUserHostApiCodec() {} - - @Override - protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { - switch (type) { - case (byte) 128: - return AuthPigeonFirebaseApp.fromList((ArrayList) readValue(buffer)); - case (byte) 129: - return PigeonMultiFactorInfo.fromList((ArrayList) readValue(buffer)); - case (byte) 130: - return PigeonMultiFactorSession.fromList((ArrayList) readValue(buffer)); - case (byte) 131: - return PigeonPhoneMultiFactorAssertion.fromList((ArrayList) readValue(buffer)); - default: - return super.readValueOfType(type, buffer); - } - } - - @Override - protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof AuthPigeonFirebaseApp) { - stream.write(128); - writeValue(stream, ((AuthPigeonFirebaseApp) value).toList()); - } else if (value instanceof PigeonMultiFactorInfo) { - stream.write(129); - writeValue(stream, ((PigeonMultiFactorInfo) value).toList()); - } else if (value instanceof PigeonMultiFactorSession) { - stream.write(130); - writeValue(stream, ((PigeonMultiFactorSession) value).toList()); - } else if (value instanceof PigeonPhoneMultiFactorAssertion) { - stream.write(131); - writeValue(stream, ((PigeonPhoneMultiFactorAssertion) value).toList()); - } else { - super.writeValue(stream, value); - } - } - } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface MultiFactorUserHostApi { void enrollPhone( @NonNull AuthPigeonFirebaseApp app, - @NonNull PigeonPhoneMultiFactorAssertion assertion, + @NonNull InternalPhoneMultiFactorAssertion assertion, @Nullable String displayName, @NonNull VoidResult result); @@ -4153,18 +4756,19 @@ void enrollTotp( @NonNull VoidResult result); void getSession( - @NonNull AuthPigeonFirebaseApp app, @NonNull Result result); + @NonNull AuthPigeonFirebaseApp app, @NonNull Result result); void unenroll( @NonNull AuthPigeonFirebaseApp app, @NonNull String factorUid, @NonNull VoidResult result); void getEnrolledFactors( - @NonNull AuthPigeonFirebaseApp app, @NonNull Result> result); + @NonNull AuthPigeonFirebaseApp app, @NonNull Result> result); /** The codec used by MultiFactorUserHostApi. */ static @NonNull MessageCodec getCodec() { - return MultiFactorUserHostApiCodec.INSTANCE; + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `MultiFactorUserHostApi` to handle messages through the * `binaryMessenger`. @@ -4189,11 +4793,11 @@ static void setUp( if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - PigeonPhoneMultiFactorAssertion assertionArg = - (PigeonPhoneMultiFactorAssertion) args.get(1); + InternalPhoneMultiFactorAssertion assertionArg = + (InternalPhoneMultiFactorAssertion) args.get(1); String displayNameArg = (String) args.get(2); VoidResult resultCallback = new VoidResult() { @@ -4224,7 +4828,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String assertionIdArg = (String) args.get(1); @@ -4258,12 +4862,12 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - Result resultCallback = - new Result() { - public void success(PigeonMultiFactorSession result) { + Result resultCallback = + new Result() { + public void success(InternalMultiFactorSession result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -4290,7 +4894,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); String factorUidArg = (String) args.get(1); @@ -4323,12 +4927,12 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; AuthPigeonFirebaseApp appArg = (AuthPigeonFirebaseApp) args.get(0); - Result> resultCallback = - new Result>() { - public void success(List result) { + Result> resultCallback = + new Result>() { + public void success(List result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -4348,71 +4952,20 @@ public void error(Throwable error) { } } - private static class MultiFactoResolverHostApiCodec extends StandardMessageCodec { - public static final MultiFactoResolverHostApiCodec INSTANCE = - new MultiFactoResolverHostApiCodec(); - - private MultiFactoResolverHostApiCodec() {} - - @Override - protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { - switch (type) { - case (byte) 128: - return PigeonAdditionalUserInfo.fromList((ArrayList) readValue(buffer)); - case (byte) 129: - return PigeonAuthCredential.fromList((ArrayList) readValue(buffer)); - case (byte) 130: - return PigeonPhoneMultiFactorAssertion.fromList((ArrayList) readValue(buffer)); - case (byte) 131: - return PigeonUserCredential.fromList((ArrayList) readValue(buffer)); - case (byte) 132: - return PigeonUserDetails.fromList((ArrayList) readValue(buffer)); - case (byte) 133: - return PigeonUserInfo.fromList((ArrayList) readValue(buffer)); - default: - return super.readValueOfType(type, buffer); - } - } - - @Override - protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof PigeonAdditionalUserInfo) { - stream.write(128); - writeValue(stream, ((PigeonAdditionalUserInfo) value).toList()); - } else if (value instanceof PigeonAuthCredential) { - stream.write(129); - writeValue(stream, ((PigeonAuthCredential) value).toList()); - } else if (value instanceof PigeonPhoneMultiFactorAssertion) { - stream.write(130); - writeValue(stream, ((PigeonPhoneMultiFactorAssertion) value).toList()); - } else if (value instanceof PigeonUserCredential) { - stream.write(131); - writeValue(stream, ((PigeonUserCredential) value).toList()); - } else if (value instanceof PigeonUserDetails) { - stream.write(132); - writeValue(stream, ((PigeonUserDetails) value).toList()); - } else if (value instanceof PigeonUserInfo) { - stream.write(133); - writeValue(stream, ((PigeonUserInfo) value).toList()); - } else { - super.writeValue(stream, value); - } - } - } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface MultiFactoResolverHostApi { void resolveSignIn( @NonNull String resolverId, - @Nullable PigeonPhoneMultiFactorAssertion assertion, + @Nullable InternalPhoneMultiFactorAssertion assertion, @Nullable String totpAssertionId, - @NonNull Result result); + @NonNull Result result); /** The codec used by MultiFactoResolverHostApi. */ static @NonNull MessageCodec getCodec() { - return MultiFactoResolverHostApiCodec.INSTANCE; + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `MultiFactoResolverHostApi` to handle messages through the * `binaryMessenger`. @@ -4437,15 +4990,15 @@ static void setUp( if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String resolverIdArg = (String) args.get(0); - PigeonPhoneMultiFactorAssertion assertionArg = - (PigeonPhoneMultiFactorAssertion) args.get(1); + InternalPhoneMultiFactorAssertion assertionArg = + (InternalPhoneMultiFactorAssertion) args.get(1); String totpAssertionIdArg = (String) args.get(2); - Result resultCallback = - new Result() { - public void success(PigeonUserCredential result) { + Result resultCallback = + new Result() { + public void success(InternalUserCredential result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -4465,36 +5018,10 @@ public void error(Throwable error) { } } - private static class MultiFactorTotpHostApiCodec extends StandardMessageCodec { - public static final MultiFactorTotpHostApiCodec INSTANCE = new MultiFactorTotpHostApiCodec(); - - private MultiFactorTotpHostApiCodec() {} - - @Override - protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { - switch (type) { - case (byte) 128: - return PigeonTotpSecret.fromList((ArrayList) readValue(buffer)); - default: - return super.readValueOfType(type, buffer); - } - } - - @Override - protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof PigeonTotpSecret) { - stream.write(128); - writeValue(stream, ((PigeonTotpSecret) value).toList()); - } else { - super.writeValue(stream, value); - } - } - } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface MultiFactorTotpHostApi { - void generateSecret(@NonNull String sessionId, @NonNull Result result); + void generateSecret(@NonNull String sessionId, @NonNull Result result); void getAssertionForEnrollment( @NonNull String secretKey, @NonNull String oneTimePassword, @NonNull Result result); @@ -4506,8 +5033,9 @@ void getAssertionForSignIn( /** The codec used by MultiFactorTotpHostApi. */ static @NonNull MessageCodec getCodec() { - return MultiFactorTotpHostApiCodec.INSTANCE; + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `MultiFactorTotpHostApi` to handle messages through the * `binaryMessenger`. @@ -4532,12 +5060,12 @@ static void setUp( if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String sessionIdArg = (String) args.get(0); - Result resultCallback = - new Result() { - public void success(PigeonTotpSecret result) { + Result resultCallback = + new Result() { + public void success(InternalTotpSecret result) { wrapped.add(0, result); reply.reply(wrapped); } @@ -4564,7 +5092,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String secretKeyArg = (String) args.get(0); String oneTimePasswordArg = (String) args.get(1); @@ -4597,7 +5125,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String enrollmentIdArg = (String) args.get(0); String oneTimePasswordArg = (String) args.get(1); @@ -4622,6 +5150,7 @@ public void error(Throwable error) { } } } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface MultiFactorTotpSecretHostApi { @@ -4636,8 +5165,9 @@ void openInOtpApp( /** The codec used by MultiFactorTotpSecretHostApi. */ static @NonNull MessageCodec getCodec() { - return new StandardMessageCodec(); + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `MultiFactorTotpSecretHostApi` to handle messages through the * `binaryMessenger`. @@ -4662,7 +5192,7 @@ static void setUp( if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String secretKeyArg = (String) args.get(0); String accountNameArg = (String) args.get(1); @@ -4696,7 +5226,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String secretKeyArg = (String) args.get(0); String qrCodeUrlArg = (String) args.get(1); @@ -4722,32 +5252,6 @@ public void error(Throwable error) { } } - private static class GenerateInterfacesCodec extends StandardMessageCodec { - public static final GenerateInterfacesCodec INSTANCE = new GenerateInterfacesCodec(); - - private GenerateInterfacesCodec() {} - - @Override - protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { - switch (type) { - case (byte) 128: - return PigeonMultiFactorInfo.fromList((ArrayList) readValue(buffer)); - default: - return super.readValueOfType(type, buffer); - } - } - - @Override - protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof PigeonMultiFactorInfo) { - stream.write(128); - writeValue(stream, ((PigeonMultiFactorInfo) value).toList()); - } else { - super.writeValue(stream, value); - } - } - } - /** * Only used to generate the object interface that are use outside of the Pigeon interface * @@ -4755,12 +5259,13 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { */ public interface GenerateInterfaces { - void pigeonInterface(@NonNull PigeonMultiFactorInfo info); + void pigeonInterface(@NonNull InternalMultiFactorInfo info); /** The codec used by GenerateInterfaces. */ static @NonNull MessageCodec getCodec() { - return GenerateInterfacesCodec.INSTANCE; + return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `GenerateInterfaces` to handle messages through the `binaryMessenger`. */ @@ -4783,15 +5288,14 @@ static void setUp( if (api != null) { channel.setMessageHandler( (message, reply) -> { - ArrayList wrapped = new ArrayList(); + ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; - PigeonMultiFactorInfo infoArg = (PigeonMultiFactorInfo) args.get(0); + InternalMultiFactorInfo infoArg = (InternalMultiFactorInfo) args.get(0); try { api.pigeonInterface(infoArg); wrapped.add(0, null); } catch (Throwable exception) { - ArrayList wrappedError = wrapError(exception); - wrapped = wrappedError; + wrapped = wrapError(exception); } reply.reply(wrapped); }); diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PhoneNumberVerificationStreamHandler.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PhoneNumberVerificationStreamHandler.java index f92e6f5b741d..a227be99a49d 100644 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PhoneNumberVerificationStreamHandler.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PhoneNumberVerificationStreamHandler.java @@ -50,7 +50,7 @@ interface OnCredentialsListener { public PhoneNumberVerificationStreamHandler( Activity activity, @NonNull GeneratedAndroidFirebaseAuth.AuthPigeonFirebaseApp app, - @NonNull GeneratedAndroidFirebaseAuth.PigeonVerifyPhoneNumberRequest request, + @NonNull GeneratedAndroidFirebaseAuth.InternalVerifyPhoneNumberRequest request, @Nullable MultiFactorSession multiFactorSession, @Nullable PhoneMultiFactorInfo multiFactorInfo, OnCredentialsListener onCredentialsListener) { diff --git a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PigeonParser.java b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PigeonParser.java index a94ae4d796a1..4c9e102a54ae 100644 --- a/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PigeonParser.java +++ b/packages/firebase_auth/firebase_auth/android/src/main/java/io/flutter/plugins/firebase/auth/PigeonParser.java @@ -39,17 +39,17 @@ public class PigeonParser { static List manuallyToList( - GeneratedAndroidFirebaseAuth.PigeonUserDetails pigeonUserDetails) { + GeneratedAndroidFirebaseAuth.InternalUserDetails pigeonUserDetails) { List output = new ArrayList<>(); output.add(pigeonUserDetails.getUserInfo().toList()); output.add(pigeonUserDetails.getProviderData()); return output; } - static GeneratedAndroidFirebaseAuth.PigeonUserCredential parseAuthResult( + static GeneratedAndroidFirebaseAuth.InternalUserCredential parseAuthResult( @NonNull AuthResult authResult) { - GeneratedAndroidFirebaseAuth.PigeonUserCredential.Builder builder = - new GeneratedAndroidFirebaseAuth.PigeonUserCredential.Builder(); + GeneratedAndroidFirebaseAuth.InternalUserCredential.Builder builder = + new GeneratedAndroidFirebaseAuth.InternalUserCredential.Builder(); builder.setAdditionalUserInfo(parseAdditionalUserInfo(authResult.getAdditionalUserInfo())); builder.setCredential(parseAuthCredential(authResult.getCredential())); @@ -58,14 +58,14 @@ static GeneratedAndroidFirebaseAuth.PigeonUserCredential parseAuthResult( return builder.build(); } - private static GeneratedAndroidFirebaseAuth.PigeonAdditionalUserInfo parseAdditionalUserInfo( + private static GeneratedAndroidFirebaseAuth.InternalAdditionalUserInfo parseAdditionalUserInfo( AdditionalUserInfo additionalUserInfo) { if (additionalUserInfo == null) { return null; } - GeneratedAndroidFirebaseAuth.PigeonAdditionalUserInfo.Builder builder = - new GeneratedAndroidFirebaseAuth.PigeonAdditionalUserInfo.Builder(); + GeneratedAndroidFirebaseAuth.InternalAdditionalUserInfo.Builder builder = + new GeneratedAndroidFirebaseAuth.InternalAdditionalUserInfo.Builder(); builder.setIsNewUser(additionalUserInfo.isNewUser()); builder.setProfile(additionalUserInfo.getProfile()); @@ -75,7 +75,7 @@ private static GeneratedAndroidFirebaseAuth.PigeonAdditionalUserInfo parseAdditi return builder.build(); } - static GeneratedAndroidFirebaseAuth.PigeonAuthCredential parseAuthCredential( + static GeneratedAndroidFirebaseAuth.InternalAuthCredential parseAuthCredential( AuthCredential authCredential) { if (authCredential == null) { return null; @@ -84,8 +84,8 @@ static GeneratedAndroidFirebaseAuth.PigeonAuthCredential parseAuthCredential( int authCredentialHashCode = authCredential.hashCode(); FlutterFirebaseAuthPlugin.authCredentials.put(authCredentialHashCode, authCredential); - GeneratedAndroidFirebaseAuth.PigeonAuthCredential.Builder builder = - new GeneratedAndroidFirebaseAuth.PigeonAuthCredential.Builder(); + GeneratedAndroidFirebaseAuth.InternalAuthCredential.Builder builder = + new GeneratedAndroidFirebaseAuth.InternalAuthCredential.Builder(); builder.setProviderId(authCredential.getProvider()); builder.setSignInMethod(authCredential.getSignInMethod()); @@ -97,17 +97,17 @@ static GeneratedAndroidFirebaseAuth.PigeonAuthCredential parseAuthCredential( return builder.build(); } - static GeneratedAndroidFirebaseAuth.PigeonUserDetails parseFirebaseUser( + static GeneratedAndroidFirebaseAuth.InternalUserDetails parseFirebaseUser( FirebaseUser firebaseUser) { if (firebaseUser == null) { return null; } - GeneratedAndroidFirebaseAuth.PigeonUserDetails.Builder builder = - new GeneratedAndroidFirebaseAuth.PigeonUserDetails.Builder(); + GeneratedAndroidFirebaseAuth.InternalUserDetails.Builder builder = + new GeneratedAndroidFirebaseAuth.InternalUserDetails.Builder(); - GeneratedAndroidFirebaseAuth.PigeonUserInfo.Builder builderInfo = - new GeneratedAndroidFirebaseAuth.PigeonUserInfo.Builder(); + GeneratedAndroidFirebaseAuth.InternalUserInfo.Builder builderInfo = + new GeneratedAndroidFirebaseAuth.InternalUserInfo.Builder(); builderInfo.setDisplayName(firebaseUser.getDisplayName()); builderInfo.setEmail(firebaseUser.getEmail()); @@ -179,7 +179,7 @@ private static String parsePhotoUrl(Uri photoUri) { static AuthCredential getCredential(Map credentialMap) { // If the credential map contains a token, it means a native one has been stored if (credentialMap.get(Constants.TOKEN) != null) { - int token = (int) credentialMap.get(Constants.TOKEN); + int token = ((Number) credentialMap.get(Constants.TOKEN)).intValue(); AuthCredential credential = FlutterFirebaseAuthPlugin.authCredentials.get(token); if (credential == null) { @@ -249,7 +249,7 @@ static AuthCredential getCredential(Map credentialMap) { } static ActionCodeSettings getActionCodeSettings( - @NonNull GeneratedAndroidFirebaseAuth.PigeonActionCodeSettings pigeonActionCodeSettings) { + @NonNull GeneratedAndroidFirebaseAuth.InternalActionCodeSettings pigeonActionCodeSettings) { ActionCodeSettings.Builder builder = ActionCodeSettings.newBuilder(); builder.setUrl(pigeonActionCodeSettings.getUrl()); @@ -278,13 +278,13 @@ static ActionCodeSettings getActionCodeSettings( return builder.build(); } - static List multiFactorInfoToPigeon( + static List multiFactorInfoToPigeon( List hints) { - List pigeonHints = new ArrayList<>(); + List pigeonHints = new ArrayList<>(); for (MultiFactorInfo info : hints) { if (info instanceof PhoneMultiFactorInfo) { pigeonHints.add( - new GeneratedAndroidFirebaseAuth.PigeonMultiFactorInfo.Builder() + new GeneratedAndroidFirebaseAuth.InternalMultiFactorInfo.Builder() .setPhoneNumber(((PhoneMultiFactorInfo) info).getPhoneNumber()) .setDisplayName(info.getDisplayName()) .setEnrollmentTimestamp((double) info.getEnrollmentTimestamp()) @@ -294,7 +294,7 @@ static List multiFactorInfoT } else { pigeonHints.add( - new GeneratedAndroidFirebaseAuth.PigeonMultiFactorInfo.Builder() + new GeneratedAndroidFirebaseAuth.InternalMultiFactorInfo.Builder() .setDisplayName(info.getDisplayName()) .setEnrollmentTimestamp((double) info.getEnrollmentTimestamp()) .setUid(info.getUid()) @@ -307,18 +307,19 @@ static List multiFactorInfoT static List> multiFactorInfoToMap(List hints) { List> pigeonHints = new ArrayList<>(); - for (GeneratedAndroidFirebaseAuth.PigeonMultiFactorInfo info : multiFactorInfoToPigeon(hints)) { + for (GeneratedAndroidFirebaseAuth.InternalMultiFactorInfo info : + multiFactorInfoToPigeon(hints)) { pigeonHints.add(info.toList()); } return pigeonHints; } - static GeneratedAndroidFirebaseAuth.PigeonActionCodeInfo parseActionCodeResult( + static GeneratedAndroidFirebaseAuth.InternalActionCodeInfo parseActionCodeResult( @NonNull ActionCodeResult actionCodeResult) { - GeneratedAndroidFirebaseAuth.PigeonActionCodeInfo.Builder builder = - new GeneratedAndroidFirebaseAuth.PigeonActionCodeInfo.Builder(); - GeneratedAndroidFirebaseAuth.PigeonActionCodeInfoData.Builder builderData = - new GeneratedAndroidFirebaseAuth.PigeonActionCodeInfoData.Builder(); + GeneratedAndroidFirebaseAuth.InternalActionCodeInfo.Builder builder = + new GeneratedAndroidFirebaseAuth.InternalActionCodeInfo.Builder(); + GeneratedAndroidFirebaseAuth.InternalActionCodeInfoData.Builder builderData = + new GeneratedAndroidFirebaseAuth.InternalActionCodeInfoData.Builder(); int operation = actionCodeResult.getOperation(); @@ -363,10 +364,10 @@ static GeneratedAndroidFirebaseAuth.PigeonActionCodeInfo parseActionCodeResult( return builder.build(); } - static GeneratedAndroidFirebaseAuth.PigeonIdTokenResult parseTokenResult( + static GeneratedAndroidFirebaseAuth.InternalIdTokenResult parseTokenResult( @NonNull GetTokenResult tokenResult) { - final GeneratedAndroidFirebaseAuth.PigeonIdTokenResult.Builder builder = - new GeneratedAndroidFirebaseAuth.PigeonIdTokenResult.Builder(); + final GeneratedAndroidFirebaseAuth.InternalIdTokenResult.Builder builder = + new GeneratedAndroidFirebaseAuth.InternalIdTokenResult.Builder(); builder.setToken(tokenResult.getToken()); builder.setSignInProvider(tokenResult.getSignInProvider()); diff --git a/packages/firebase_auth/firebase_auth/example/android/app/build.gradle b/packages/firebase_auth/firebase_auth/example/android/app/build.gradle index 75b0c370e4d4..db427be91ec5 100644 --- a/packages/firebase_auth/firebase_auth/example/android/app/build.gradle +++ b/packages/firebase_auth/firebase_auth/example/android/app/build.gradle @@ -42,7 +42,7 @@ android { applicationId = "io.flutter.plugins.firebase.auth.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = 23 + minSdkVersion = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName diff --git a/packages/firebase_auth/firebase_auth/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/auth/example/MainActivity.kt b/packages/firebase_auth/firebase_auth/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/auth/example/MainActivity.kt index e8ca8519f78f..2b88c507db23 100644 --- a/packages/firebase_auth/firebase_auth/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/auth/example/MainActivity.kt +++ b/packages/firebase_auth/firebase_auth/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/auth/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.auth.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_auth/firebase_auth/example/ios/Runner/AppDelegate.swift b/packages/firebase_auth/firebase_auth/example/ios/Runner/AppDelegate.swift index b6363034812b..626664468b89 100644 --- a/packages/firebase_auth/firebase_auth/example/ios/Runner/AppDelegate.swift +++ b/packages/firebase_auth/firebase_auth/example/ios/Runner/AppDelegate.swift @@ -1,5 +1,5 @@ -import UIKit import Flutter +import UIKit @main @objc class AppDelegate: FlutterAppDelegate { diff --git a/packages/firebase_auth/firebase_auth/example/macos/Runner.xcodeproj/project.pbxproj b/packages/firebase_auth/firebase_auth/example/macos/Runner.xcodeproj/project.pbxproj index 719671ab76f6..4848b09b04b9 100644 --- a/packages/firebase_auth/firebase_auth/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_auth/firebase_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 */; }; B6036D992F5B77F0D48D7883 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1026236A547BC5196614E954 /* Pods_Runner.framework */; }; C0151CEAF69E3751D6A8FA78 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2F30ED8EF0A1349EA81AEE1A /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ @@ -75,6 +76,7 @@ 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 = ""; }; 3D7BD4B06D0869EA1407E048 /* 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 = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ @@ -84,6 +86,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, B6036D992F5B77F0D48D7883 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -146,6 +149,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, @@ -198,6 +202,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* example.app */; productType = "com.apple.product-type.application"; @@ -237,6 +244,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -307,12 +317,10 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/firebase_messaging/firebase_messaging_Privacy.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}/firebase_messaging_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/google_sign_in_ios_privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; @@ -350,50 +358,20 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseAuthInterop/FirebaseAuthInterop.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", "${BUILT_PRODUCTS_DIR}/GTMAppAuth/GTMAppAuth.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", "${BUILT_PRODUCTS_DIR}/GoogleSignIn/GoogleSignIn.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/facebook_auth_desktop/facebook_auth_desktop.framework", "${BUILT_PRODUCTS_DIR}/flutter_secure_storage_macos/flutter_secure_storage_macos.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", - "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", - "${BUILT_PRODUCTS_DIR}/url_launcher_macos/url_launcher_macos.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAppCheckInterop.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuthInterop.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMAppAuth.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleSignIn.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/facebook_auth_desktop.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage_macos.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_macos.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -721,6 +699,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/firebase_auth/firebase_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_auth/firebase_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index bfbc9cb8ef3c..126b4eb8ea7d 100644 --- a/packages/firebase_auth/firebase_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/firebase_auth/firebase_auth/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/packages/firebase_auth/firebase_auth/example/pubspec.yaml b/packages/firebase_auth/firebase_auth/example/pubspec.yaml index 271adb256742..5068e9614644 100644 --- a/packages/firebase_auth/firebase_auth/example/pubspec.yaml +++ b/packages/firebase_auth/firebase_auth/example/pubspec.yaml @@ -1,14 +1,16 @@ name: firebase_auth_example description: Demonstrates how to use the firebase_auth plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: barcode_widget: ^2.0.4 - firebase_auth: ^6.3.0 - firebase_core: ^4.6.0 - firebase_messaging: ^16.1.3 + firebase_auth: ^6.5.4 + firebase_core: ^4.11.0 + firebase_messaging: ^16.4.1 flutter: sdk: flutter flutter_facebook_auth: ^7.1.5 diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Package.swift b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Package.swift index a250c3e74394..9793367d8229 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Package.swift +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "6.2.0" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "6.5.4" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_auth", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-auth", targets: ["firebase_auth"]), + .library(name: "firebase-auth", targets: ["firebase_auth"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,14 +30,14 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ - .headerSearchPath("include/firebase_auth/Private"), - .headerSearchPath("include/firebase_auth/Public"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .headerSearchPath("include/Private"), + .headerSearchPath("include/Public"), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-auth\""), ] - ), + ) ] ) diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTFirebaseAuthPlugin.m b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTFirebaseAuthPlugin.m index 7edbadb78910..606dda68176d 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTFirebaseAuthPlugin.m +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTFirebaseAuthPlugin.m @@ -95,8 +95,10 @@ @interface FLTFirebaseAuthPlugin () @property BOOL isReauthenticatingWithApple; @property(strong, nonatomic) NSString *currentNonce; @property(strong, nonatomic) void (^appleCompletion) - (PigeonUserCredential *_Nullable, FlutterError *_Nullable); + (InternalUserCredential *_Nullable, FlutterError *_Nullable); @property(strong, nonatomic) AuthPigeonFirebaseApp *appleArguments; +/// YES while an `ASAuthorizationController` Sign in with Apple flow is active. +@property(nonatomic, assign) BOOL appleSignInRequestInFlight; @end @@ -378,18 +380,27 @@ - (NSString *)stringBySha256HashingString:(NSString *)input { static void handleSignInWithApple(FLTFirebaseAuthPlugin *object, FIRAuthDataResult *authResult, NSString *authorizationCode, NSError *error) { - void (^completion)(PigeonUserCredential *_Nullable, FlutterError *_Nullable) = + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = object.appleCompletion; - if (completion == nil) return; + if (completion == nil) { + object.appleSignInRequestInFlight = NO; + return; + } if (error != nil) { if (error.code == FIRAuthErrorCodeSecondFactorRequired) { + object.appleCompletion = nil; + object.appleSignInRequestInFlight = NO; [object handleMultiFactorError:object.appleArguments completion:completion withError:error]; } else { + object.appleCompletion = nil; + object.appleSignInRequestInFlight = NO; completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); } return; } + object.appleCompletion = nil; + object.appleSignInRequestInFlight = NO; completion([PigeonParser getPigeonUserCredentialFromAuthResult:authResult authorizationCode:authorizationCode], nil); @@ -406,6 +417,15 @@ - (void)authorizationController:(ASAuthorizationController *)controller if (appleIDCredential.identityToken == nil) { NSLog(@"Unable to fetch identity token."); + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + self.appleCompletion = nil; + self.appleSignInRequestInFlight = NO; + if (completion != nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + } return; } @@ -428,7 +448,7 @@ - (void)authorizationController:(ASAuthorizationController *)controller if (self.isReauthenticatingWithApple == YES) { self.isReauthenticatingWithApple = NO; - void (^capturedCompletion)(PigeonUserCredential *_Nullable, FlutterError *_Nullable) = + void (^capturedCompletion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = self.appleCompletion; [[FIRAuth.auth currentUser] reauthenticateWithCredential:credential @@ -439,7 +459,7 @@ - (void)authorizationController:(ASAuthorizationController *)controller } else if (self.linkWithAppleUser != nil) { FIRUser *userToLink = self.linkWithAppleUser; - void (^capturedCompletion)(PigeonUserCredential *_Nullable, FlutterError *_Nullable) = + void (^capturedCompletion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = self.appleCompletion; [userToLink linkWithCredential:credential completion:^(FIRAuthDataResult *authResult, NSError *error) { @@ -450,7 +470,7 @@ - (void)authorizationController:(ASAuthorizationController *)controller } else { FIRAuth *signInAuth = self.signInWithAppleAuth != nil ? self.signInWithAppleAuth : FIRAuth.auth; - void (^capturedCompletion)(PigeonUserCredential *_Nullable, FlutterError *_Nullable) = + void (^capturedCompletion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = self.appleCompletion; [signInAuth signInWithCredential:credential completion:^(FIRAuthDataResult *_Nullable authResult, @@ -459,50 +479,65 @@ - (void)authorizationController:(ASAuthorizationController *)controller handleSignInWithApple(self, authResult, authorizationCode, error); }]; } + } else { + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + self.appleCompletion = nil; + self.appleSignInRequestInFlight = NO; + if (completion != nil) { + completion(nil, [FlutterError errorWithCode:kErrCodeInvalidCredential + message:kErrMsgInvalidCredential + details:nil]); + } } } - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(macos(10.15), ios(13.0)) { + void (^completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable) = + self.appleCompletion; + self.appleCompletion = nil; + self.appleSignInRequestInFlight = NO; + NSLog(@"Sign in with Apple errored: %@", error); + if (completion == nil) { + return; + } + switch (error.code) { case ASAuthorizationErrorCanceled: - self.appleCompletion( - nil, [FlutterError errorWithCode:@"canceled" - message:@"The user canceled the authorization attempt." - details:nil]); + completion(nil, [FlutterError errorWithCode:@"canceled" + message:@"The user canceled the authorization attempt." + details:nil]); break; case ASAuthorizationErrorInvalidResponse: - self.appleCompletion( - nil, - [FlutterError errorWithCode:@"invalid-response" - message:@"The authorization request received an invalid response." - details:nil]); + completion(nil, [FlutterError + errorWithCode:@"invalid-response" + message:@"The authorization request received an invalid response." + details:nil]); break; case ASAuthorizationErrorNotHandled: - self.appleCompletion(nil, - [FlutterError errorWithCode:@"not-handled" - message:@"The authorization request wasn’t handled." - details:nil]); + completion(nil, [FlutterError errorWithCode:@"not-handled" + message:@"The authorization request wasn’t handled." + details:nil]); break; case ASAuthorizationErrorFailed: - self.appleCompletion(nil, [FlutterError errorWithCode:@"failed" - message:@"The authorization attempt failed." - details:nil]); + completion(nil, [FlutterError errorWithCode:@"failed" + message:@"The authorization attempt failed." + details:nil]); break; case ASAuthorizationErrorUnknown: default: - self.appleCompletion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); + completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); break; } - self.appleCompletion = nil; } -- (void)handleInternalError:(nonnull void (^)(PigeonUserCredential *_Nullable, +- (void)handleInternalError:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion withError:(NSError *)error { const NSError *underlyingError = error.userInfo[@"NSUnderlyingError"]; @@ -520,7 +555,7 @@ - (void)handleInternalError:(nonnull void (^)(PigeonUserCredential *_Nullable, } - (void)handleMultiFactorError:(AuthPigeonFirebaseApp *)app - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion withError:(NSError *_Nullable)error { FIRMultiFactorResolver *resolver = @@ -544,7 +579,7 @@ - (void)handleMultiFactorError:(AuthPigeonFirebaseApp *)app phoneNumber = phoneFactorInfo.phoneNumber; } - PigeonMultiFactorInfo *object = [PigeonMultiFactorInfo + InternalMultiFactorInfo *object = [InternalMultiFactorInfo makeWithDisplayName:multiFactorInfo.displayName enrollmentTimestamp:multiFactorInfo.enrollmentDate.timeIntervalSince1970 factorId:multiFactorInfo.factorID @@ -566,14 +601,23 @@ - (void)handleMultiFactorError:(AuthPigeonFirebaseApp *)app } static void launchAppleSignInRequest(FLTFirebaseAuthPlugin *object, AuthPigeonFirebaseApp *app, - PigeonSignInProvider *signInProvider, - void (^_Nonnull completion)(PigeonUserCredential *_Nullable, + InternalSignInProvider *signInProvider, + void (^_Nonnull completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable)) { if (@available(iOS 13.0, macOS 10.15, *)) { + if (object.appleSignInRequestInFlight) { + completion(nil, + [FlutterError errorWithCode:@"operation-not-allowed" + message:@"A Sign in with Apple request is already in progress." + details:nil]); + return; + } + NSString *nonce = [object randomNonce:32]; object.currentNonce = nonce; object.appleCompletion = completion; object.appleArguments = app; + object.appleSignInRequestInFlight = YES; ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init]; @@ -601,7 +645,7 @@ static void launchAppleSignInRequest(FLTFirebaseAuthPlugin *object, AuthPigeonFi static void handleAppleAuthResult(FLTFirebaseAuthPlugin *object, AuthPigeonFirebaseApp *app, FIRAuth *auth, FIRAuthCredential *credentials, NSError *error, - void (^_Nonnull completion)(PigeonUserCredential *_Nullable, + void (^_Nonnull completion)(InternalUserCredential *_Nullable, FlutterError *_Nullable)) { if (error) { if (error.code == FIRAuthErrorCodeSecondFactorRequired) { @@ -884,7 +928,7 @@ - (nonnull ASPresentationAnchor)presentationAnchorForAuthorizationController: } - (void)enrollPhoneApp:(nonnull AuthPigeonFirebaseApp *)app - assertion:(nonnull PigeonPhoneMultiFactorAssertion *)assertion + assertion:(nonnull InternalPhoneMultiFactorAssertion *)assertion displayName:(nullable NSString *)displayName completion:(nonnull void (^)(FlutterError *_Nullable))completion { #if TARGET_OS_OSX @@ -918,13 +962,13 @@ - (void)enrollPhoneApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)getEnrolledFactorsApp:(nonnull AuthPigeonFirebaseApp *)app - completion:(nonnull void (^)(NSArray *_Nullable, + completion:(nonnull void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion { FIRMultiFactor *multiFactor = [self getAppMultiFactorFromPigeon:app]; NSArray *enrolledFactors = [multiFactor enrolledFactors]; - NSMutableArray *results = [NSMutableArray array]; + NSMutableArray *results = [NSMutableArray array]; for (FIRMultiFactorInfo *multiFactorInfo in enrolledFactors) { NSString *phoneNumber; @@ -933,7 +977,7 @@ - (void)getEnrolledFactorsApp:(nonnull AuthPigeonFirebaseApp *)app phoneNumber = phoneFactorInfo.phoneNumber; } - [results addObject:[PigeonMultiFactorInfo + [results addObject:[InternalMultiFactorInfo makeWithDisplayName:multiFactorInfo.displayName enrollmentTimestamp:multiFactorInfo.enrollmentDate.timeIntervalSince1970 factorId:multiFactorInfo.factorID @@ -945,7 +989,7 @@ - (void)getEnrolledFactorsApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)getSessionApp:(nonnull AuthPigeonFirebaseApp *)app - completion:(nonnull void (^)(PigeonMultiFactorSession *_Nullable, + completion:(nonnull void (^)(InternalMultiFactorSession *_Nullable, FlutterError *_Nullable))completion { FIRMultiFactor *multiFactor = [self getAppMultiFactorFromPigeon:app]; [multiFactor getSessionWithCompletion:^(FIRMultiFactorSession *_Nullable session, @@ -953,7 +997,7 @@ - (void)getSessionApp:(nonnull AuthPigeonFirebaseApp *)app NSString *UUID = [[NSUUID UUID] UUIDString]; self->_multiFactorSessionMap[UUID] = session; - PigeonMultiFactorSession *pigeonSession = [PigeonMultiFactorSession makeWithId:UUID]; + InternalMultiFactorSession *pigeonSession = [InternalMultiFactorSession makeWithId:UUID]; completion(pigeonSession, nil); }]; } @@ -996,9 +1040,9 @@ - (void)enrollTotpApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)resolveSignInResolverId:(nonnull NSString *)resolverId - assertion:(nullable PigeonPhoneMultiFactorAssertion *)assertion + assertion:(nullable InternalPhoneMultiFactorAssertion *)assertion totpAssertionId:(nullable NSString *)totpAssertionId - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRMultiFactorResolver *resolver = _multiFactorResolverMap[resolverId]; @@ -1038,7 +1082,7 @@ - (void)resolveSignInResolverId:(nonnull NSString *)resolverId } - (void)generateSecretSessionId:(nonnull NSString *)sessionId - completion:(nonnull void (^)(PigeonTotpSecret *_Nullable, + completion:(nonnull void (^)(InternalTotpSecret *_Nullable, FlutterError *_Nullable))completion { FIRMultiFactorSession *multiFactorSession = _multiFactorSessionMap[sessionId]; @@ -1131,9 +1175,20 @@ - (void)revokeTokenWithAuthorizationCodeApp:(nonnull AuthPigeonFirebaseApp *)app }]; } +- (void)revokeAccessTokenApp:(nonnull AuthPigeonFirebaseApp *)app + accessToken:(nonnull NSString *)accessToken + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + // `revokeAccessToken(_:)` is currently Android-only on the Firebase SDK. + // On Apple platforms use `revokeTokenWithAuthorizationCode:` instead. + completion([FlutterError errorWithCode:@"unsupported-platform-operation" + message:@"revokeAccessToken is not supported on iOS/macOS. " + @"Use revokeTokenWithAuthorizationCode instead." + details:nil]); +} + - (void)checkActionCodeApp:(nonnull AuthPigeonFirebaseApp *)app code:(nonnull NSString *)code - completion:(nonnull void (^)(PigeonActionCodeInfo *_Nullable, + completion:(nonnull void (^)(InternalActionCodeInfo *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; [auth checkActionCode:code @@ -1141,7 +1196,7 @@ - (void)checkActionCodeApp:(nonnull AuthPigeonFirebaseApp *)app if (error != nil) { completion(nil, [FLTFirebaseAuthPlugin convertToFlutterError:error]); } else { - PigeonActionCodeInfo *result = [self parseActionCode:info]; + InternalActionCodeInfo *result = [self parseActionCode:info]; if (result.operation == ActionCodeInfoOperationUnknown) { // Workaround: Firebase iOS SDK >=11.12.0 returns .unknown because // actionCodeOperation(forRequestType:) only matches camelCase but the @@ -1159,9 +1214,9 @@ - (void)checkActionCodeApp:(nonnull AuthPigeonFirebaseApp *)app }]; } -- (PigeonActionCodeInfo *_Nullable)parseActionCode:(nonnull FIRActionCodeInfo *)info { - PigeonActionCodeInfoData *data = [PigeonActionCodeInfoData makeWithEmail:info.email - previousEmail:info.previousEmail]; +- (InternalActionCodeInfo *_Nullable)parseActionCode:(nonnull FIRActionCodeInfo *)info { + InternalActionCodeInfoData *data = [InternalActionCodeInfoData makeWithEmail:info.email + previousEmail:info.previousEmail]; ActionCodeInfoOperation operation; @@ -1181,7 +1236,7 @@ - (PigeonActionCodeInfo *_Nullable)parseActionCode:(nonnull FIRActionCodeInfo *) operation = ActionCodeInfoOperationUnknown; } - return [PigeonActionCodeInfo makeWithOperation:operation data:data]; + return [InternalActionCodeInfo makeWithOperation:operation data:data]; } /// Maps a raw requestType string (either camelCase or SCREAMING_SNAKE_CASE) to @@ -1215,8 +1270,8 @@ + (ActionCodeInfoOperation)operationFromRequestType:(nullable NSString *)request /// result if the REST call fails for any reason. - (void)resolveActionCodeOperationForApp:(nonnull AuthPigeonFirebaseApp *)app code:(nonnull NSString *)code - fallbackInfo:(nonnull PigeonActionCodeInfo *)fallbackInfo - completion:(nonnull void (^)(PigeonActionCodeInfo *_Nullable, + fallbackInfo:(nonnull InternalActionCodeInfo *)fallbackInfo + completion:(nonnull void (^)(InternalActionCodeInfo *_Nullable, FlutterError *_Nullable))completion { FIRApp *firebaseApp = [FLTFirebasePlugin firebaseAppNamed:app.appName]; NSString *apiKey = firebaseApp.options.APIKey; @@ -1260,7 +1315,7 @@ - (void)resolveActionCodeOperationForApp:(nonnull AuthPigeonFirebaseApp *)app [FLTFirebaseAuthPlugin operationFromRequestType:json[@"requestType"]]; if (operation != ActionCodeInfoOperationUnknown) { - completion([PigeonActionCodeInfo makeWithOperation:operation data:fallbackInfo.data], + completion([InternalActionCodeInfo makeWithOperation:operation data:fallbackInfo.data], nil); } else { completion(fallbackInfo, nil); @@ -1288,7 +1343,7 @@ - (void)confirmPasswordResetApp:(nonnull AuthPigeonFirebaseApp *)app - (void)createUserWithEmailAndPasswordApp:(nonnull AuthPigeonFirebaseApp *)app email:(nonnull NSString *)email password:(nonnull NSString *)password - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; [auth createUserWithEmail:email @@ -1367,7 +1422,7 @@ - (void)registerIdTokenListenerApp:(nonnull AuthPigeonFirebaseApp *)app - (void)sendPasswordResetEmailApp:(nonnull AuthPigeonFirebaseApp *)app email:(nonnull NSString *)email - actionCodeSettings:(nullable PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings completion:(nonnull void (^)(FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; if (actionCodeSettings != nil) { @@ -1395,7 +1450,7 @@ - (void)sendPasswordResetEmailApp:(nonnull AuthPigeonFirebaseApp *)app - (void)sendSignInLinkToEmailApp:(nonnull AuthPigeonFirebaseApp *)app email:(nonnull NSString *)email - actionCodeSettings:(nonnull PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(nonnull InternalActionCodeSettings *)actionCodeSettings completion:(nonnull void (^)(FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; [auth sendSignInLinkToEmail:email @@ -1404,7 +1459,7 @@ - (void)sendSignInLinkToEmailApp:(nonnull AuthPigeonFirebaseApp *)app if (error != nil) { if (error.code == FIRAuthErrorCodeInternalError) { [self - handleInternalError:^(PigeonUserCredential *_Nullable creds, + handleInternalError:^(InternalUserCredential *_Nullable creds, FlutterError *_Nullable internalError) { completion(internalError); } @@ -1434,7 +1489,7 @@ - (void)setLanguageCodeApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)setSettingsApp:(nonnull AuthPigeonFirebaseApp *)app - settings:(nonnull PigeonFirebaseAuthSettings *)settings + settings:(nonnull InternalFirebaseAuthSettings *)settings completion:(nonnull void (^)(FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; @@ -1462,7 +1517,7 @@ - (void)setSettingsApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)signInAnonymouslyApp:(nonnull AuthPigeonFirebaseApp *)app - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; [auth signInAnonymouslyWithCompletion:^(FIRAuthDataResult *authResult, NSError *error) { @@ -1478,7 +1533,7 @@ - (void)signInAnonymouslyApp:(nonnull AuthPigeonFirebaseApp *)app - (void)signInWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app input:(nonnull NSDictionary *)input - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; [self @@ -1559,7 +1614,7 @@ - (void)signInWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app - (void)signInWithCustomTokenApp:(nonnull AuthPigeonFirebaseApp *)app token:(nonnull NSString *)token - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; @@ -1584,7 +1639,7 @@ - (void)signInWithCustomTokenApp:(nonnull AuthPigeonFirebaseApp *)app - (void)signInWithEmailAndPasswordApp:(nonnull AuthPigeonFirebaseApp *)app email:(nonnull NSString *)email password:(nonnull NSString *)password - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; [auth signInWithEmail:email @@ -1609,7 +1664,7 @@ - (void)signInWithEmailAndPasswordApp:(nonnull AuthPigeonFirebaseApp *)app - (void)signInWithEmailLinkApp:(nonnull AuthPigeonFirebaseApp *)app email:(nonnull NSString *)email emailLink:(nonnull NSString *)emailLink - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; [auth signInWithEmail:email @@ -1632,8 +1687,8 @@ - (void)signInWithEmailLinkApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)signInWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app - signInProvider:(nonnull PigeonSignInProvider *)signInProvider - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + signInProvider:(nonnull InternalSignInProvider *)signInProvider + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; @@ -1723,7 +1778,7 @@ - (void)verifyPasswordResetCodeApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)verifyPhoneNumberApp:(nonnull AuthPigeonFirebaseApp *)app - request:(nonnull PigeonVerifyPhoneNumberRequest *)request + request:(nonnull InternalVerifyPhoneNumberRequest *)request completion: (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { #if TARGET_OS_OSX @@ -1802,7 +1857,7 @@ - (void)deleteApp:(nonnull AuthPigeonFirebaseApp *)app - (void)getIdTokenApp:(nonnull AuthPigeonFirebaseApp *)app forceRefresh:(BOOL)forceRefresh - completion:(nonnull void (^)(PigeonIdTokenResult *_Nullable, + completion:(nonnull void (^)(InternalIdTokenResult *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -1827,7 +1882,7 @@ - (void)getIdTokenApp:(nonnull AuthPigeonFirebaseApp *)app - (void)linkWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app input:(nonnull NSDictionary *)input - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -1882,8 +1937,8 @@ - (void)linkWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)linkWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app - signInProvider:(nonnull PigeonSignInProvider *)signInProvider - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + signInProvider:(nonnull InternalSignInProvider *)signInProvider + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -1935,7 +1990,7 @@ - (void)linkWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app - (void)reauthenticateWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app input:(nonnull NSDictionary *)input - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -1993,8 +2048,8 @@ - (void)reauthenticateWithCredentialApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)reauthenticateWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app - signInProvider:(nonnull PigeonSignInProvider *)signInProvider - completion:(nonnull void (^)(PigeonUserCredential *_Nullable, + signInProvider:(nonnull InternalSignInProvider *)signInProvider + completion:(nonnull void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -2036,7 +2091,7 @@ - (void)reauthenticateWithProviderApp:(nonnull AuthPigeonFirebaseApp *)app - (void)reloadApp:(nonnull AuthPigeonFirebaseApp *)app completion: - (nonnull void (^)(PigeonUserDetails *_Nullable, FlutterError *_Nullable))completion { + (nonnull void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; if (currentUser == nil) { @@ -2056,7 +2111,7 @@ - (void)reloadApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)sendEmailVerificationApp:(nonnull AuthPigeonFirebaseApp *)app - actionCodeSettings:(nullable PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings completion:(nonnull void (^)(FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -2083,8 +2138,8 @@ - (void)sendEmailVerificationApp:(nonnull AuthPigeonFirebaseApp *)app - (void)unlinkApp:(nonnull AuthPigeonFirebaseApp *)app providerId:(nonnull NSString *)providerId - completion: - (nonnull void (^)(PigeonUserCredential *_Nullable, FlutterError *_Nullable))completion { + completion:(nonnull void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; if (currentUser == nil) { @@ -2106,7 +2161,7 @@ - (void)unlinkApp:(nonnull AuthPigeonFirebaseApp *)app - (void)updateEmailApp:(nonnull AuthPigeonFirebaseApp *)app newEmail:(nonnull NSString *)newEmail - completion:(nonnull void (^)(PigeonUserDetails *_Nullable, + completion:(nonnull void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -2135,7 +2190,7 @@ - (void)updateEmailApp:(nonnull AuthPigeonFirebaseApp *)app - (void)updatePasswordApp:(nonnull AuthPigeonFirebaseApp *)app newPassword:(nonnull NSString *)newPassword - completion:(nonnull void (^)(PigeonUserDetails *_Nullable, + completion:(nonnull void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -2165,7 +2220,7 @@ - (void)updatePasswordApp:(nonnull AuthPigeonFirebaseApp *)app - (void)updatePhoneNumberApp:(nonnull AuthPigeonFirebaseApp *)app input:(nonnull NSDictionary *)input - completion:(nonnull void (^)(PigeonUserDetails *_Nullable, + completion:(nonnull void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion { #if TARGET_OS_IPHONE FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; @@ -2229,8 +2284,8 @@ - (void)updatePhoneNumberApp:(nonnull AuthPigeonFirebaseApp *)app } - (void)updateProfileApp:(nonnull AuthPigeonFirebaseApp *)app - profile:(nonnull PigeonUserProfile *)profile - completion:(nonnull void (^)(PigeonUserDetails *_Nullable, + profile:(nonnull InternalUserProfile *)profile + completion:(nonnull void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; @@ -2275,7 +2330,7 @@ - (void)updateProfileApp:(nonnull AuthPigeonFirebaseApp *)app - (void)verifyBeforeUpdateEmailApp:(nonnull AuthPigeonFirebaseApp *)app newEmail:(nonnull NSString *)newEmail - actionCodeSettings:(nullable PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings completion:(nonnull void (^)(FlutterError *_Nullable))completion { FIRAuth *auth = [self getFIRAuthFromAppNameFromPigeon:app]; FIRUser *currentUser = auth.currentUser; diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTPhoneNumberVerificationStreamHandler.m b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTPhoneNumberVerificationStreamHandler.m index 69b9d7dea14d..511d2caa8841 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTPhoneNumberVerificationStreamHandler.m +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/FLTPhoneNumberVerificationStreamHandler.m @@ -18,7 +18,7 @@ @implementation FLTPhoneNumberVerificationStreamHandler { } #if TARGET_OS_OSX -- (instancetype)initWithAuth:(id)auth request:(PigeonVerifyPhoneNumberRequest *)request { +- (instancetype)initWithAuth:(id)auth request:(InternalVerifyPhoneNumberRequest *)request { self = [super init]; if (self) { _auth = auth; @@ -28,7 +28,7 @@ - (instancetype)initWithAuth:(id)auth request:(PigeonVerifyPhoneNumberRequest *) } #else - (instancetype)initWithAuth:(id)auth - request:(PigeonVerifyPhoneNumberRequest *)request + request:(InternalVerifyPhoneNumberRequest *)request session:(FIRMultiFactorSession *)session factorInfo:(FIRPhoneMultiFactorInfo *)factorInfo { self = [super init]; diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/PigeonParser.m b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/PigeonParser.m index f8ef1b77493c..8d7a7b1c2f0e 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/PigeonParser.m +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/PigeonParser.m @@ -10,30 +10,30 @@ @implementation PigeonParser -+ (PigeonUserCredential *) ++ (InternalUserCredential *) getPigeonUserCredentialFromAuthResult:(nonnull FIRAuthDataResult *)authResult authorizationCode:(nullable NSString *)authorizationCode { - return [PigeonUserCredential + return [InternalUserCredential makeWithUser:[self getPigeonDetails:authResult.user] additionalUserInfo:[self getPigeonAdditionalUserInfo:authResult.additionalUserInfo authorizationCode:authorizationCode] credential:[self getPigeonAuthCredential:authResult.credential token:nil]]; } -+ (PigeonUserCredential *)getPigeonUserCredentialFromFIRUser:(nonnull FIRUser *)user { - return [PigeonUserCredential makeWithUser:[self getPigeonDetails:user] - additionalUserInfo:nil - credential:nil]; ++ (InternalUserCredential *)getPigeonUserCredentialFromFIRUser:(nonnull FIRUser *)user { + return [InternalUserCredential makeWithUser:[self getPigeonDetails:user] + additionalUserInfo:nil + credential:nil]; } -+ (PigeonUserDetails *)getPigeonDetails:(nonnull FIRUser *)user { - return [PigeonUserDetails makeWithUserInfo:[self getPigeonUserInfo:user] - providerData:[self getProviderData:user.providerData]]; ++ (InternalUserDetails *)getPigeonDetails:(nonnull FIRUser *)user { + return [InternalUserDetails makeWithUserInfo:[self getPigeonUserInfo:user] + providerData:[self getProviderData:user.providerData]]; } -+ (PigeonUserInfo *)getPigeonUserInfo:(nonnull FIRUser *)user { ++ (InternalUserInfo *)getPigeonUserInfo:(nonnull FIRUser *)user { NSString *photoUrlString = user.photoURL.absoluteString; - return [PigeonUserInfo + return [InternalUserInfo makeWithUid:user.uid email:user.email displayName:user.displayName @@ -74,25 +74,26 @@ + (PigeonUserInfo *)getPigeonUserInfo:(nonnull FIRUser *)user { return [dataArray copy]; } -+ (PigeonAdditionalUserInfo *)getPigeonAdditionalUserInfo:(nonnull FIRAdditionalUserInfo *)userInfo - authorizationCode:(nullable NSString *)authorizationCode { - return [PigeonAdditionalUserInfo makeWithIsNewUser:userInfo.isNewUser - providerId:userInfo.providerID - username:userInfo.username - authorizationCode:authorizationCode - profile:userInfo.profile]; ++ (InternalAdditionalUserInfo *)getPigeonAdditionalUserInfo: + (nonnull FIRAdditionalUserInfo *)userInfo + authorizationCode:(nullable NSString *)authorizationCode { + return [InternalAdditionalUserInfo makeWithIsNewUser:userInfo.isNewUser + providerId:userInfo.providerID + username:userInfo.username + authorizationCode:authorizationCode + profile:userInfo.profile]; } -+ (PigeonTotpSecret *)getPigeonTotpSecret:(FIRTOTPSecret *)secret { - return [PigeonTotpSecret makeWithCodeIntervalSeconds:nil - codeLength:nil - enrollmentCompletionDeadline:nil - hashingAlgorithm:nil - secretKey:secret.sharedSecretKey]; ++ (InternalTotpSecret *)getPigeonTotpSecret:(FIRTOTPSecret *)secret { + return [InternalTotpSecret makeWithCodeIntervalSeconds:nil + codeLength:nil + enrollmentCompletionDeadline:nil + hashingAlgorithm:nil + secretKey:secret.sharedSecretKey]; } -+ (PigeonAuthCredential *)getPigeonAuthCredential:(FIRAuthCredential *)authCredential - token:(NSNumber *_Nullable)token { ++ (InternalAuthCredential *)getPigeonAuthCredential:(FIRAuthCredential *)authCredential + token:(NSNumber *_Nullable)token { if (authCredential == nil) { return nil; } @@ -110,14 +111,14 @@ + (PigeonAuthCredential *)getPigeonAuthCredential:(FIRAuthCredential *)authCrede NSUInteger nativeId = token != nil ? [token unsignedLongValue] : (NSUInteger)[authCredential hash]; - return [PigeonAuthCredential makeWithProviderId:authCredential.provider - signInMethod:authCredential.provider - nativeId:nativeId - accessToken:accessToken ?: nil]; + return [InternalAuthCredential makeWithProviderId:authCredential.provider + signInMethod:authCredential.provider + nativeId:nativeId + accessToken:accessToken ?: nil]; } + (FIRActionCodeSettings *_Nullable)parseActionCodeSettings: - (nullable PigeonActionCodeSettings *)settings { + (nullable InternalActionCodeSettings *)settings { if (settings == nil) { return nil; } @@ -141,21 +142,21 @@ + (FIRActionCodeSettings *_Nullable)parseActionCodeSettings: return codeSettings; } -+ (PigeonIdTokenResult *)parseIdTokenResult:(FIRAuthTokenResult *)tokenResult { ++ (InternalIdTokenResult *)parseIdTokenResult:(FIRAuthTokenResult *)tokenResult { long expirationTimestamp = (long)[tokenResult.expirationDate timeIntervalSince1970] * 1000; long authTimestamp = (long)[tokenResult.authDate timeIntervalSince1970] * 1000; long issuedAtTimestamp = (long)[tokenResult.issuedAtDate timeIntervalSince1970] * 1000; - return [PigeonIdTokenResult makeWithToken:tokenResult.token - expirationTimestamp:@(expirationTimestamp) - authTimestamp:@(authTimestamp) - issuedAtTimestamp:@(issuedAtTimestamp) - signInProvider:tokenResult.signInProvider - claims:tokenResult.claims - signInSecondFactor:tokenResult.signInSecondFactor]; + return [InternalIdTokenResult makeWithToken:tokenResult.token + expirationTimestamp:@(expirationTimestamp) + authTimestamp:@(authTimestamp) + issuedAtTimestamp:@(issuedAtTimestamp) + signInProvider:tokenResult.signInProvider + claims:tokenResult.claims + signInSecondFactor:tokenResult.signInSecondFactor]; } -+ (NSArray *_Nonnull)getManualList:(nonnull PigeonUserDetails *)userDetails { ++ (NSArray *_Nonnull)getManualList:(nonnull InternalUserDetails *)userDetails { NSMutableArray *output = [NSMutableArray array]; id userInfoList = [[userDetails userInfo] toList]; diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/firebase_auth_messages.g.m b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/firebase_auth_messages.g.m index 365ff70d690c..82ae8cfcccc7 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/firebase_auth_messages.g.m +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/firebase_auth_messages.g.m @@ -1,22 +1,109 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v19.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "include/Public/firebase_auth_messages.g.h" #if TARGET_OS_OSX -#import +@import FlutterMacOS; #else -#import +@import Flutter; #endif -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. -#endif +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return + [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} -static NSArray *wrapResult(id result, FlutterError *error) { +static NSArray *wrapResult(id result, FlutterError *error) { if (error) { return @[ error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] @@ -25,7 +112,7 @@ return @[ result ?: [NSNull null] ]; } -static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { id result = array[key]; return (result == [NSNull null]) ? nil : result; } @@ -42,167 +129,209 @@ - (instancetype)initWithValue:(ActionCodeInfoOperation)value { } @end -@interface PigeonMultiFactorSession () -+ (PigeonMultiFactorSession *)fromList:(NSArray *)list; -+ (nullable PigeonMultiFactorSession *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalMultiFactorSession () ++ (InternalMultiFactorSession *)fromList:(NSArray *)list; ++ (nullable InternalMultiFactorSession *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonPhoneMultiFactorAssertion () -+ (PigeonPhoneMultiFactorAssertion *)fromList:(NSArray *)list; -+ (nullable PigeonPhoneMultiFactorAssertion *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalPhoneMultiFactorAssertion () ++ (InternalPhoneMultiFactorAssertion *)fromList:(NSArray *)list; ++ (nullable InternalPhoneMultiFactorAssertion *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonMultiFactorInfo () -+ (PigeonMultiFactorInfo *)fromList:(NSArray *)list; -+ (nullable PigeonMultiFactorInfo *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalMultiFactorInfo () ++ (InternalMultiFactorInfo *)fromList:(NSArray *)list; ++ (nullable InternalMultiFactorInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end @interface AuthPigeonFirebaseApp () -+ (AuthPigeonFirebaseApp *)fromList:(NSArray *)list; -+ (nullable AuthPigeonFirebaseApp *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; ++ (AuthPigeonFirebaseApp *)fromList:(NSArray *)list; ++ (nullable AuthPigeonFirebaseApp *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface InternalActionCodeInfoData () ++ (InternalActionCodeInfoData *)fromList:(NSArray *)list; ++ (nullable InternalActionCodeInfoData *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonActionCodeInfoData () -+ (PigeonActionCodeInfoData *)fromList:(NSArray *)list; -+ (nullable PigeonActionCodeInfoData *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalActionCodeInfo () ++ (InternalActionCodeInfo *)fromList:(NSArray *)list; ++ (nullable InternalActionCodeInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonActionCodeInfo () -+ (PigeonActionCodeInfo *)fromList:(NSArray *)list; -+ (nullable PigeonActionCodeInfo *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalAdditionalUserInfo () ++ (InternalAdditionalUserInfo *)fromList:(NSArray *)list; ++ (nullable InternalAdditionalUserInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonAdditionalUserInfo () -+ (PigeonAdditionalUserInfo *)fromList:(NSArray *)list; -+ (nullable PigeonAdditionalUserInfo *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalAuthCredential () ++ (InternalAuthCredential *)fromList:(NSArray *)list; ++ (nullable InternalAuthCredential *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonAuthCredential () -+ (PigeonAuthCredential *)fromList:(NSArray *)list; -+ (nullable PigeonAuthCredential *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalUserInfo () ++ (InternalUserInfo *)fromList:(NSArray *)list; ++ (nullable InternalUserInfo *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonUserInfo () -+ (PigeonUserInfo *)fromList:(NSArray *)list; -+ (nullable PigeonUserInfo *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalUserDetails () ++ (InternalUserDetails *)fromList:(NSArray *)list; ++ (nullable InternalUserDetails *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonUserDetails () -+ (PigeonUserDetails *)fromList:(NSArray *)list; -+ (nullable PigeonUserDetails *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalUserCredential () ++ (InternalUserCredential *)fromList:(NSArray *)list; ++ (nullable InternalUserCredential *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonUserCredential () -+ (PigeonUserCredential *)fromList:(NSArray *)list; -+ (nullable PigeonUserCredential *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalAuthCredentialInput () ++ (InternalAuthCredentialInput *)fromList:(NSArray *)list; ++ (nullable InternalAuthCredentialInput *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonActionCodeSettings () -+ (PigeonActionCodeSettings *)fromList:(NSArray *)list; -+ (nullable PigeonActionCodeSettings *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalActionCodeSettings () ++ (InternalActionCodeSettings *)fromList:(NSArray *)list; ++ (nullable InternalActionCodeSettings *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonFirebaseAuthSettings () -+ (PigeonFirebaseAuthSettings *)fromList:(NSArray *)list; -+ (nullable PigeonFirebaseAuthSettings *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalFirebaseAuthSettings () ++ (InternalFirebaseAuthSettings *)fromList:(NSArray *)list; ++ (nullable InternalFirebaseAuthSettings *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonSignInProvider () -+ (PigeonSignInProvider *)fromList:(NSArray *)list; -+ (nullable PigeonSignInProvider *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalSignInProvider () ++ (InternalSignInProvider *)fromList:(NSArray *)list; ++ (nullable InternalSignInProvider *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonVerifyPhoneNumberRequest () -+ (PigeonVerifyPhoneNumberRequest *)fromList:(NSArray *)list; -+ (nullable PigeonVerifyPhoneNumberRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalVerifyPhoneNumberRequest () ++ (InternalVerifyPhoneNumberRequest *)fromList:(NSArray *)list; ++ (nullable InternalVerifyPhoneNumberRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonIdTokenResult () -+ (PigeonIdTokenResult *)fromList:(NSArray *)list; -+ (nullable PigeonIdTokenResult *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalIdTokenResult () ++ (InternalIdTokenResult *)fromList:(NSArray *)list; ++ (nullable InternalIdTokenResult *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonUserProfile () -+ (PigeonUserProfile *)fromList:(NSArray *)list; -+ (nullable PigeonUserProfile *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalUserProfile () ++ (InternalUserProfile *)fromList:(NSArray *)list; ++ (nullable InternalUserProfile *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@interface PigeonTotpSecret () -+ (PigeonTotpSecret *)fromList:(NSArray *)list; -+ (nullable PigeonTotpSecret *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; +@interface InternalTotpSecret () ++ (InternalTotpSecret *)fromList:(NSArray *)list; ++ (nullable InternalTotpSecret *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; @end -@implementation PigeonMultiFactorSession +@implementation InternalMultiFactorSession + (instancetype)makeWithId:(NSString *)id { - PigeonMultiFactorSession *pigeonResult = [[PigeonMultiFactorSession alloc] init]; + InternalMultiFactorSession *pigeonResult = [[InternalMultiFactorSession alloc] init]; pigeonResult.id = id; return pigeonResult; } -+ (PigeonMultiFactorSession *)fromList:(NSArray *)list { - PigeonMultiFactorSession *pigeonResult = [[PigeonMultiFactorSession alloc] init]; ++ (InternalMultiFactorSession *)fromList:(NSArray *)list { + InternalMultiFactorSession *pigeonResult = [[InternalMultiFactorSession alloc] init]; pigeonResult.id = GetNullableObjectAtIndex(list, 0); return pigeonResult; } -+ (nullable PigeonMultiFactorSession *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonMultiFactorSession fromList:list] : nil; ++ (nullable InternalMultiFactorSession *)nullableFromList:(NSArray *)list { + return (list) ? [InternalMultiFactorSession fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.id ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalMultiFactorSession *other = (InternalMultiFactorSession *)object; + return FLTPigeonDeepEquals(self.id, other.id); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.id); + return result; +} @end -@implementation PigeonPhoneMultiFactorAssertion +@implementation InternalPhoneMultiFactorAssertion + (instancetype)makeWithVerificationId:(NSString *)verificationId verificationCode:(NSString *)verificationCode { - PigeonPhoneMultiFactorAssertion *pigeonResult = [[PigeonPhoneMultiFactorAssertion alloc] init]; + InternalPhoneMultiFactorAssertion *pigeonResult = + [[InternalPhoneMultiFactorAssertion alloc] init]; pigeonResult.verificationId = verificationId; pigeonResult.verificationCode = verificationCode; return pigeonResult; } -+ (PigeonPhoneMultiFactorAssertion *)fromList:(NSArray *)list { - PigeonPhoneMultiFactorAssertion *pigeonResult = [[PigeonPhoneMultiFactorAssertion alloc] init]; ++ (InternalPhoneMultiFactorAssertion *)fromList:(NSArray *)list { + InternalPhoneMultiFactorAssertion *pigeonResult = + [[InternalPhoneMultiFactorAssertion alloc] init]; pigeonResult.verificationId = GetNullableObjectAtIndex(list, 0); pigeonResult.verificationCode = GetNullableObjectAtIndex(list, 1); return pigeonResult; } -+ (nullable PigeonPhoneMultiFactorAssertion *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonPhoneMultiFactorAssertion fromList:list] : nil; ++ (nullable InternalPhoneMultiFactorAssertion *)nullableFromList:(NSArray *)list { + return (list) ? [InternalPhoneMultiFactorAssertion fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.verificationId ?: [NSNull null], self.verificationCode ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalPhoneMultiFactorAssertion *other = (InternalPhoneMultiFactorAssertion *)object; + return FLTPigeonDeepEquals(self.verificationId, other.verificationId) && + FLTPigeonDeepEquals(self.verificationCode, other.verificationCode); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.verificationId); + result = result * 31 + FLTPigeonDeepHash(self.verificationCode); + return result; +} @end -@implementation PigeonMultiFactorInfo +@implementation InternalMultiFactorInfo + (instancetype)makeWithDisplayName:(nullable NSString *)displayName enrollmentTimestamp:(double)enrollmentTimestamp factorId:(nullable NSString *)factorId uid:(NSString *)uid phoneNumber:(nullable NSString *)phoneNumber { - PigeonMultiFactorInfo *pigeonResult = [[PigeonMultiFactorInfo alloc] init]; + InternalMultiFactorInfo *pigeonResult = [[InternalMultiFactorInfo alloc] init]; pigeonResult.displayName = displayName; pigeonResult.enrollmentTimestamp = enrollmentTimestamp; pigeonResult.factorId = factorId; @@ -210,8 +339,8 @@ + (instancetype)makeWithDisplayName:(nullable NSString *)displayName pigeonResult.phoneNumber = phoneNumber; return pigeonResult; } -+ (PigeonMultiFactorInfo *)fromList:(NSArray *)list { - PigeonMultiFactorInfo *pigeonResult = [[PigeonMultiFactorInfo alloc] init]; ++ (InternalMultiFactorInfo *)fromList:(NSArray *)list { + InternalMultiFactorInfo *pigeonResult = [[InternalMultiFactorInfo alloc] init]; pigeonResult.displayName = GetNullableObjectAtIndex(list, 0); pigeonResult.enrollmentTimestamp = [GetNullableObjectAtIndex(list, 1) doubleValue]; pigeonResult.factorId = GetNullableObjectAtIndex(list, 2); @@ -219,10 +348,10 @@ + (PigeonMultiFactorInfo *)fromList:(NSArray *)list { pigeonResult.phoneNumber = GetNullableObjectAtIndex(list, 4); return pigeonResult; } -+ (nullable PigeonMultiFactorInfo *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonMultiFactorInfo fromList:list] : nil; ++ (nullable InternalMultiFactorInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalMultiFactorInfo fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.displayName ?: [NSNull null], @(self.enrollmentTimestamp), @@ -231,6 +360,32 @@ - (NSArray *)toList { self.phoneNumber ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalMultiFactorInfo *other = (InternalMultiFactorInfo *)object; + return FLTPigeonDeepEquals(self.displayName, other.displayName) && + (self.enrollmentTimestamp == other.enrollmentTimestamp || + (isnan(self.enrollmentTimestamp) && isnan(other.enrollmentTimestamp))) && + FLTPigeonDeepEquals(self.factorId, other.factorId) && + FLTPigeonDeepEquals(self.uid, other.uid) && + FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.displayName); + result = result * 31 + (isnan(self.enrollmentTimestamp) ? (NSUInteger)0x7FF8000000000000 + : @(self.enrollmentTimestamp).hash); + result = result * 31 + FLTPigeonDeepHash(self.factorId); + result = result * 31 + FLTPigeonDeepHash(self.uid); + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + return result; +} @end @implementation AuthPigeonFirebaseApp @@ -243,82 +398,138 @@ + (instancetype)makeWithAppName:(NSString *)appName pigeonResult.customAuthDomain = customAuthDomain; return pigeonResult; } -+ (AuthPigeonFirebaseApp *)fromList:(NSArray *)list { ++ (AuthPigeonFirebaseApp *)fromList:(NSArray *)list { AuthPigeonFirebaseApp *pigeonResult = [[AuthPigeonFirebaseApp alloc] init]; pigeonResult.appName = GetNullableObjectAtIndex(list, 0); pigeonResult.tenantId = GetNullableObjectAtIndex(list, 1); pigeonResult.customAuthDomain = GetNullableObjectAtIndex(list, 2); return pigeonResult; } -+ (nullable AuthPigeonFirebaseApp *)nullableFromList:(NSArray *)list { ++ (nullable AuthPigeonFirebaseApp *)nullableFromList:(NSArray *)list { return (list) ? [AuthPigeonFirebaseApp fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.appName ?: [NSNull null], self.tenantId ?: [NSNull null], self.customAuthDomain ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + AuthPigeonFirebaseApp *other = (AuthPigeonFirebaseApp *)object; + return FLTPigeonDeepEquals(self.appName, other.appName) && + FLTPigeonDeepEquals(self.tenantId, other.tenantId) && + FLTPigeonDeepEquals(self.customAuthDomain, other.customAuthDomain); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.appName); + result = result * 31 + FLTPigeonDeepHash(self.tenantId); + result = result * 31 + FLTPigeonDeepHash(self.customAuthDomain); + return result; +} @end -@implementation PigeonActionCodeInfoData +@implementation InternalActionCodeInfoData + (instancetype)makeWithEmail:(nullable NSString *)email previousEmail:(nullable NSString *)previousEmail { - PigeonActionCodeInfoData *pigeonResult = [[PigeonActionCodeInfoData alloc] init]; + InternalActionCodeInfoData *pigeonResult = [[InternalActionCodeInfoData alloc] init]; pigeonResult.email = email; pigeonResult.previousEmail = previousEmail; return pigeonResult; } -+ (PigeonActionCodeInfoData *)fromList:(NSArray *)list { - PigeonActionCodeInfoData *pigeonResult = [[PigeonActionCodeInfoData alloc] init]; ++ (InternalActionCodeInfoData *)fromList:(NSArray *)list { + InternalActionCodeInfoData *pigeonResult = [[InternalActionCodeInfoData alloc] init]; pigeonResult.email = GetNullableObjectAtIndex(list, 0); pigeonResult.previousEmail = GetNullableObjectAtIndex(list, 1); return pigeonResult; } -+ (nullable PigeonActionCodeInfoData *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonActionCodeInfoData fromList:list] : nil; ++ (nullable InternalActionCodeInfoData *)nullableFromList:(NSArray *)list { + return (list) ? [InternalActionCodeInfoData fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.email ?: [NSNull null], self.previousEmail ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalActionCodeInfoData *other = (InternalActionCodeInfoData *)object; + return FLTPigeonDeepEquals(self.email, other.email) && + FLTPigeonDeepEquals(self.previousEmail, other.previousEmail); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.email); + result = result * 31 + FLTPigeonDeepHash(self.previousEmail); + return result; +} @end -@implementation PigeonActionCodeInfo +@implementation InternalActionCodeInfo + (instancetype)makeWithOperation:(ActionCodeInfoOperation)operation - data:(PigeonActionCodeInfoData *)data { - PigeonActionCodeInfo *pigeonResult = [[PigeonActionCodeInfo alloc] init]; + data:(InternalActionCodeInfoData *)data { + InternalActionCodeInfo *pigeonResult = [[InternalActionCodeInfo alloc] init]; pigeonResult.operation = operation; pigeonResult.data = data; return pigeonResult; } -+ (PigeonActionCodeInfo *)fromList:(NSArray *)list { - PigeonActionCodeInfo *pigeonResult = [[PigeonActionCodeInfo alloc] init]; - pigeonResult.operation = [GetNullableObjectAtIndex(list, 0) integerValue]; ++ (InternalActionCodeInfo *)fromList:(NSArray *)list { + InternalActionCodeInfo *pigeonResult = [[InternalActionCodeInfo alloc] init]; + ActionCodeInfoOperationBox *boxedActionCodeInfoOperation = GetNullableObjectAtIndex(list, 0); + pigeonResult.operation = boxedActionCodeInfoOperation.value; pigeonResult.data = GetNullableObjectAtIndex(list, 1); return pigeonResult; } -+ (nullable PigeonActionCodeInfo *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonActionCodeInfo fromList:list] : nil; ++ (nullable InternalActionCodeInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalActionCodeInfo fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ - @(self.operation), + [[ActionCodeInfoOperationBox alloc] initWithValue:self.operation], self.data ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalActionCodeInfo *other = (InternalActionCodeInfo *)object; + return self.operation == other.operation && FLTPigeonDeepEquals(self.data, other.data); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.operation).hash; + result = result * 31 + FLTPigeonDeepHash(self.data); + return result; +} @end -@implementation PigeonAdditionalUserInfo +@implementation InternalAdditionalUserInfo + (instancetype)makeWithIsNewUser:(BOOL)isNewUser providerId:(nullable NSString *)providerId username:(nullable NSString *)username authorizationCode:(nullable NSString *)authorizationCode profile:(nullable NSDictionary *)profile { - PigeonAdditionalUserInfo *pigeonResult = [[PigeonAdditionalUserInfo alloc] init]; + InternalAdditionalUserInfo *pigeonResult = [[InternalAdditionalUserInfo alloc] init]; pigeonResult.isNewUser = isNewUser; pigeonResult.providerId = providerId; pigeonResult.username = username; @@ -326,8 +537,8 @@ + (instancetype)makeWithIsNewUser:(BOOL)isNewUser pigeonResult.profile = profile; return pigeonResult; } -+ (PigeonAdditionalUserInfo *)fromList:(NSArray *)list { - PigeonAdditionalUserInfo *pigeonResult = [[PigeonAdditionalUserInfo alloc] init]; ++ (InternalAdditionalUserInfo *)fromList:(NSArray *)list { + InternalAdditionalUserInfo *pigeonResult = [[InternalAdditionalUserInfo alloc] init]; pigeonResult.isNewUser = [GetNullableObjectAtIndex(list, 0) boolValue]; pigeonResult.providerId = GetNullableObjectAtIndex(list, 1); pigeonResult.username = GetNullableObjectAtIndex(list, 2); @@ -335,10 +546,10 @@ + (PigeonAdditionalUserInfo *)fromList:(NSArray *)list { pigeonResult.profile = GetNullableObjectAtIndex(list, 4); return pigeonResult; } -+ (nullable PigeonAdditionalUserInfo *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonAdditionalUserInfo fromList:list] : nil; ++ (nullable InternalAdditionalUserInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalAdditionalUserInfo fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ @(self.isNewUser), self.providerId ?: [NSNull null], @@ -347,32 +558,56 @@ - (NSArray *)toList { self.profile ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalAdditionalUserInfo *other = (InternalAdditionalUserInfo *)object; + return self.isNewUser == other.isNewUser && + FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.username, other.username) && + FLTPigeonDeepEquals(self.authorizationCode, other.authorizationCode) && + FLTPigeonDeepEquals(self.profile, other.profile); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.isNewUser).hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.username); + result = result * 31 + FLTPigeonDeepHash(self.authorizationCode); + result = result * 31 + FLTPigeonDeepHash(self.profile); + return result; +} @end -@implementation PigeonAuthCredential +@implementation InternalAuthCredential + (instancetype)makeWithProviderId:(NSString *)providerId signInMethod:(NSString *)signInMethod nativeId:(NSInteger)nativeId accessToken:(nullable NSString *)accessToken { - PigeonAuthCredential *pigeonResult = [[PigeonAuthCredential alloc] init]; + InternalAuthCredential *pigeonResult = [[InternalAuthCredential alloc] init]; pigeonResult.providerId = providerId; pigeonResult.signInMethod = signInMethod; pigeonResult.nativeId = nativeId; pigeonResult.accessToken = accessToken; return pigeonResult; } -+ (PigeonAuthCredential *)fromList:(NSArray *)list { - PigeonAuthCredential *pigeonResult = [[PigeonAuthCredential alloc] init]; ++ (InternalAuthCredential *)fromList:(NSArray *)list { + InternalAuthCredential *pigeonResult = [[InternalAuthCredential alloc] init]; pigeonResult.providerId = GetNullableObjectAtIndex(list, 0); pigeonResult.signInMethod = GetNullableObjectAtIndex(list, 1); pigeonResult.nativeId = [GetNullableObjectAtIndex(list, 2) integerValue]; pigeonResult.accessToken = GetNullableObjectAtIndex(list, 3); return pigeonResult; } -+ (nullable PigeonAuthCredential *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonAuthCredential fromList:list] : nil; ++ (nullable InternalAuthCredential *)nullableFromList:(NSArray *)list { + return (list) ? [InternalAuthCredential fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.providerId ?: [NSNull null], self.signInMethod ?: [NSNull null], @@ -380,9 +615,31 @@ - (NSArray *)toList { self.accessToken ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalAuthCredential *other = (InternalAuthCredential *)object; + return FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.signInMethod, other.signInMethod) && + self.nativeId == other.nativeId && + FLTPigeonDeepEquals(self.accessToken, other.accessToken); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.signInMethod); + result = result * 31 + @(self.nativeId).hash; + result = result * 31 + FLTPigeonDeepHash(self.accessToken); + return result; +} @end -@implementation PigeonUserInfo +@implementation InternalUserInfo + (instancetype)makeWithUid:(NSString *)uid email:(nullable NSString *)email displayName:(nullable NSString *)displayName @@ -395,7 +652,7 @@ + (instancetype)makeWithUid:(NSString *)uid refreshToken:(nullable NSString *)refreshToken creationTimestamp:(nullable NSNumber *)creationTimestamp lastSignInTimestamp:(nullable NSNumber *)lastSignInTimestamp { - PigeonUserInfo *pigeonResult = [[PigeonUserInfo alloc] init]; + InternalUserInfo *pigeonResult = [[InternalUserInfo alloc] init]; pigeonResult.uid = uid; pigeonResult.email = email; pigeonResult.displayName = displayName; @@ -410,8 +667,8 @@ + (instancetype)makeWithUid:(NSString *)uid pigeonResult.lastSignInTimestamp = lastSignInTimestamp; return pigeonResult; } -+ (PigeonUserInfo *)fromList:(NSArray *)list { - PigeonUserInfo *pigeonResult = [[PigeonUserInfo alloc] init]; ++ (InternalUserInfo *)fromList:(NSArray *)list { + InternalUserInfo *pigeonResult = [[InternalUserInfo alloc] init]; pigeonResult.uid = GetNullableObjectAtIndex(list, 0); pigeonResult.email = GetNullableObjectAtIndex(list, 1); pigeonResult.displayName = GetNullableObjectAtIndex(list, 2); @@ -426,10 +683,10 @@ + (PigeonUserInfo *)fromList:(NSArray *)list { pigeonResult.lastSignInTimestamp = GetNullableObjectAtIndex(list, 11); return pigeonResult; } -+ (nullable PigeonUserInfo *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonUserInfo fromList:list] : nil; ++ (nullable InternalUserInfo *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserInfo fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.uid ?: [NSNull null], self.email ?: [NSNull null], @@ -445,63 +702,192 @@ - (NSArray *)toList { self.lastSignInTimestamp ?: [NSNull null], ]; } -@end - -@implementation PigeonUserDetails -+ (instancetype)makeWithUserInfo:(PigeonUserInfo *)userInfo +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserInfo *other = (InternalUserInfo *)object; + return FLTPigeonDeepEquals(self.uid, other.uid) && FLTPigeonDeepEquals(self.email, other.email) && + FLTPigeonDeepEquals(self.displayName, other.displayName) && + FLTPigeonDeepEquals(self.photoUrl, other.photoUrl) && + FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber) && + self.isAnonymous == other.isAnonymous && self.isEmailVerified == other.isEmailVerified && + FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.tenantId, other.tenantId) && + FLTPigeonDeepEquals(self.refreshToken, other.refreshToken) && + FLTPigeonDeepEquals(self.creationTimestamp, other.creationTimestamp) && + FLTPigeonDeepEquals(self.lastSignInTimestamp, other.lastSignInTimestamp); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.uid); + result = result * 31 + FLTPigeonDeepHash(self.email); + result = result * 31 + FLTPigeonDeepHash(self.displayName); + result = result * 31 + FLTPigeonDeepHash(self.photoUrl); + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + result = result * 31 + @(self.isAnonymous).hash; + result = result * 31 + @(self.isEmailVerified).hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.tenantId); + result = result * 31 + FLTPigeonDeepHash(self.refreshToken); + result = result * 31 + FLTPigeonDeepHash(self.creationTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.lastSignInTimestamp); + return result; +} +@end + +@implementation InternalUserDetails ++ (instancetype)makeWithUserInfo:(InternalUserInfo *)userInfo providerData:(NSArray *> *)providerData { - PigeonUserDetails *pigeonResult = [[PigeonUserDetails alloc] init]; + InternalUserDetails *pigeonResult = [[InternalUserDetails alloc] init]; pigeonResult.userInfo = userInfo; pigeonResult.providerData = providerData; return pigeonResult; } -+ (PigeonUserDetails *)fromList:(NSArray *)list { - PigeonUserDetails *pigeonResult = [[PigeonUserDetails alloc] init]; ++ (InternalUserDetails *)fromList:(NSArray *)list { + InternalUserDetails *pigeonResult = [[InternalUserDetails alloc] init]; pigeonResult.userInfo = GetNullableObjectAtIndex(list, 0); pigeonResult.providerData = GetNullableObjectAtIndex(list, 1); return pigeonResult; } -+ (nullable PigeonUserDetails *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonUserDetails fromList:list] : nil; ++ (nullable InternalUserDetails *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserDetails fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.userInfo ?: [NSNull null], self.providerData ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserDetails *other = (InternalUserDetails *)object; + return FLTPigeonDeepEquals(self.userInfo, other.userInfo) && + FLTPigeonDeepEquals(self.providerData, other.providerData); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.userInfo); + result = result * 31 + FLTPigeonDeepHash(self.providerData); + return result; +} @end -@implementation PigeonUserCredential -+ (instancetype)makeWithUser:(nullable PigeonUserDetails *)user - additionalUserInfo:(nullable PigeonAdditionalUserInfo *)additionalUserInfo - credential:(nullable PigeonAuthCredential *)credential { - PigeonUserCredential *pigeonResult = [[PigeonUserCredential alloc] init]; +@implementation InternalUserCredential ++ (instancetype)makeWithUser:(nullable InternalUserDetails *)user + additionalUserInfo:(nullable InternalAdditionalUserInfo *)additionalUserInfo + credential:(nullable InternalAuthCredential *)credential { + InternalUserCredential *pigeonResult = [[InternalUserCredential alloc] init]; pigeonResult.user = user; pigeonResult.additionalUserInfo = additionalUserInfo; pigeonResult.credential = credential; return pigeonResult; } -+ (PigeonUserCredential *)fromList:(NSArray *)list { - PigeonUserCredential *pigeonResult = [[PigeonUserCredential alloc] init]; ++ (InternalUserCredential *)fromList:(NSArray *)list { + InternalUserCredential *pigeonResult = [[InternalUserCredential alloc] init]; pigeonResult.user = GetNullableObjectAtIndex(list, 0); pigeonResult.additionalUserInfo = GetNullableObjectAtIndex(list, 1); pigeonResult.credential = GetNullableObjectAtIndex(list, 2); return pigeonResult; } -+ (nullable PigeonUserCredential *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonUserCredential fromList:list] : nil; ++ (nullable InternalUserCredential *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserCredential fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.user ?: [NSNull null], self.additionalUserInfo ?: [NSNull null], self.credential ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserCredential *other = (InternalUserCredential *)object; + return FLTPigeonDeepEquals(self.user, other.user) && + FLTPigeonDeepEquals(self.additionalUserInfo, other.additionalUserInfo) && + FLTPigeonDeepEquals(self.credential, other.credential); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.user); + result = result * 31 + FLTPigeonDeepHash(self.additionalUserInfo); + result = result * 31 + FLTPigeonDeepHash(self.credential); + return result; +} +@end + +@implementation InternalAuthCredentialInput ++ (instancetype)makeWithProviderId:(NSString *)providerId + signInMethod:(NSString *)signInMethod + token:(nullable NSString *)token + accessToken:(nullable NSString *)accessToken { + InternalAuthCredentialInput *pigeonResult = [[InternalAuthCredentialInput alloc] init]; + pigeonResult.providerId = providerId; + pigeonResult.signInMethod = signInMethod; + pigeonResult.token = token; + pigeonResult.accessToken = accessToken; + return pigeonResult; +} ++ (InternalAuthCredentialInput *)fromList:(NSArray *)list { + InternalAuthCredentialInput *pigeonResult = [[InternalAuthCredentialInput alloc] init]; + pigeonResult.providerId = GetNullableObjectAtIndex(list, 0); + pigeonResult.signInMethod = GetNullableObjectAtIndex(list, 1); + pigeonResult.token = GetNullableObjectAtIndex(list, 2); + pigeonResult.accessToken = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable InternalAuthCredentialInput *)nullableFromList:(NSArray *)list { + return (list) ? [InternalAuthCredentialInput fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.providerId ?: [NSNull null], + self.signInMethod ?: [NSNull null], + self.token ?: [NSNull null], + self.accessToken ?: [NSNull null], + ]; +} +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalAuthCredentialInput *other = (InternalAuthCredentialInput *)object; + return FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.signInMethod, other.signInMethod) && + FLTPigeonDeepEquals(self.token, other.token) && + FLTPigeonDeepEquals(self.accessToken, other.accessToken); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.signInMethod); + result = result * 31 + FLTPigeonDeepHash(self.token); + result = result * 31 + FLTPigeonDeepHash(self.accessToken); + return result; +} @end -@implementation PigeonActionCodeSettings +@implementation InternalActionCodeSettings + (instancetype)makeWithUrl:(NSString *)url dynamicLinkDomain:(nullable NSString *)dynamicLinkDomain handleCodeInApp:(BOOL)handleCodeInApp @@ -510,7 +896,7 @@ + (instancetype)makeWithUrl:(NSString *)url androidInstallApp:(BOOL)androidInstallApp androidMinimumVersion:(nullable NSString *)androidMinimumVersion linkDomain:(nullable NSString *)linkDomain { - PigeonActionCodeSettings *pigeonResult = [[PigeonActionCodeSettings alloc] init]; + InternalActionCodeSettings *pigeonResult = [[InternalActionCodeSettings alloc] init]; pigeonResult.url = url; pigeonResult.dynamicLinkDomain = dynamicLinkDomain; pigeonResult.handleCodeInApp = handleCodeInApp; @@ -521,8 +907,8 @@ + (instancetype)makeWithUrl:(NSString *)url pigeonResult.linkDomain = linkDomain; return pigeonResult; } -+ (PigeonActionCodeSettings *)fromList:(NSArray *)list { - PigeonActionCodeSettings *pigeonResult = [[PigeonActionCodeSettings alloc] init]; ++ (InternalActionCodeSettings *)fromList:(NSArray *)list { + InternalActionCodeSettings *pigeonResult = [[InternalActionCodeSettings alloc] init]; pigeonResult.url = GetNullableObjectAtIndex(list, 0); pigeonResult.dynamicLinkDomain = GetNullableObjectAtIndex(list, 1); pigeonResult.handleCodeInApp = [GetNullableObjectAtIndex(list, 2) boolValue]; @@ -533,10 +919,10 @@ + (PigeonActionCodeSettings *)fromList:(NSArray *)list { pigeonResult.linkDomain = GetNullableObjectAtIndex(list, 7); return pigeonResult; } -+ (nullable PigeonActionCodeSettings *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonActionCodeSettings fromList:list] : nil; ++ (nullable InternalActionCodeSettings *)nullableFromList:(NSArray *)list { + return (list) ? [InternalActionCodeSettings fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.url ?: [NSNull null], self.dynamicLinkDomain ?: [NSNull null], @@ -548,15 +934,45 @@ - (NSArray *)toList { self.linkDomain ?: [NSNull null], ]; } -@end - -@implementation PigeonFirebaseAuthSettings +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalActionCodeSettings *other = (InternalActionCodeSettings *)object; + return FLTPigeonDeepEquals(self.url, other.url) && + FLTPigeonDeepEquals(self.dynamicLinkDomain, other.dynamicLinkDomain) && + self.handleCodeInApp == other.handleCodeInApp && + FLTPigeonDeepEquals(self.iOSBundleId, other.iOSBundleId) && + FLTPigeonDeepEquals(self.androidPackageName, other.androidPackageName) && + self.androidInstallApp == other.androidInstallApp && + FLTPigeonDeepEquals(self.androidMinimumVersion, other.androidMinimumVersion) && + FLTPigeonDeepEquals(self.linkDomain, other.linkDomain); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.url); + result = result * 31 + FLTPigeonDeepHash(self.dynamicLinkDomain); + result = result * 31 + @(self.handleCodeInApp).hash; + result = result * 31 + FLTPigeonDeepHash(self.iOSBundleId); + result = result * 31 + FLTPigeonDeepHash(self.androidPackageName); + result = result * 31 + @(self.androidInstallApp).hash; + result = result * 31 + FLTPigeonDeepHash(self.androidMinimumVersion); + result = result * 31 + FLTPigeonDeepHash(self.linkDomain); + return result; +} +@end + +@implementation InternalFirebaseAuthSettings + (instancetype)makeWithAppVerificationDisabledForTesting:(BOOL)appVerificationDisabledForTesting userAccessGroup:(nullable NSString *)userAccessGroup phoneNumber:(nullable NSString *)phoneNumber smsCode:(nullable NSString *)smsCode forceRecaptchaFlow:(nullable NSNumber *)forceRecaptchaFlow { - PigeonFirebaseAuthSettings *pigeonResult = [[PigeonFirebaseAuthSettings alloc] init]; + InternalFirebaseAuthSettings *pigeonResult = [[InternalFirebaseAuthSettings alloc] init]; pigeonResult.appVerificationDisabledForTesting = appVerificationDisabledForTesting; pigeonResult.userAccessGroup = userAccessGroup; pigeonResult.phoneNumber = phoneNumber; @@ -564,8 +980,8 @@ + (instancetype)makeWithAppVerificationDisabledForTesting:(BOOL)appVerificationD pigeonResult.forceRecaptchaFlow = forceRecaptchaFlow; return pigeonResult; } -+ (PigeonFirebaseAuthSettings *)fromList:(NSArray *)list { - PigeonFirebaseAuthSettings *pigeonResult = [[PigeonFirebaseAuthSettings alloc] init]; ++ (InternalFirebaseAuthSettings *)fromList:(NSArray *)list { + InternalFirebaseAuthSettings *pigeonResult = [[InternalFirebaseAuthSettings alloc] init]; pigeonResult.appVerificationDisabledForTesting = [GetNullableObjectAtIndex(list, 0) boolValue]; pigeonResult.userAccessGroup = GetNullableObjectAtIndex(list, 1); pigeonResult.phoneNumber = GetNullableObjectAtIndex(list, 2); @@ -573,10 +989,10 @@ + (PigeonFirebaseAuthSettings *)fromList:(NSArray *)list { pigeonResult.forceRecaptchaFlow = GetNullableObjectAtIndex(list, 4); return pigeonResult; } -+ (nullable PigeonFirebaseAuthSettings *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonFirebaseAuthSettings fromList:list] : nil; ++ (nullable InternalFirebaseAuthSettings *)nullableFromList:(NSArray *)list { + return (list) ? [InternalFirebaseAuthSettings fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ @(self.appVerificationDisabledForTesting), self.userAccessGroup ?: [NSNull null], @@ -585,46 +1001,90 @@ - (NSArray *)toList { self.forceRecaptchaFlow ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalFirebaseAuthSettings *other = (InternalFirebaseAuthSettings *)object; + return self.appVerificationDisabledForTesting == other.appVerificationDisabledForTesting && + FLTPigeonDeepEquals(self.userAccessGroup, other.userAccessGroup) && + FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber) && + FLTPigeonDeepEquals(self.smsCode, other.smsCode) && + FLTPigeonDeepEquals(self.forceRecaptchaFlow, other.forceRecaptchaFlow); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.appVerificationDisabledForTesting).hash; + result = result * 31 + FLTPigeonDeepHash(self.userAccessGroup); + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + result = result * 31 + FLTPigeonDeepHash(self.smsCode); + result = result * 31 + FLTPigeonDeepHash(self.forceRecaptchaFlow); + return result; +} @end -@implementation PigeonSignInProvider +@implementation InternalSignInProvider + (instancetype)makeWithProviderId:(NSString *)providerId scopes:(nullable NSArray *)scopes customParameters: (nullable NSDictionary *)customParameters { - PigeonSignInProvider *pigeonResult = [[PigeonSignInProvider alloc] init]; + InternalSignInProvider *pigeonResult = [[InternalSignInProvider alloc] init]; pigeonResult.providerId = providerId; pigeonResult.scopes = scopes; pigeonResult.customParameters = customParameters; return pigeonResult; } -+ (PigeonSignInProvider *)fromList:(NSArray *)list { - PigeonSignInProvider *pigeonResult = [[PigeonSignInProvider alloc] init]; ++ (InternalSignInProvider *)fromList:(NSArray *)list { + InternalSignInProvider *pigeonResult = [[InternalSignInProvider alloc] init]; pigeonResult.providerId = GetNullableObjectAtIndex(list, 0); pigeonResult.scopes = GetNullableObjectAtIndex(list, 1); pigeonResult.customParameters = GetNullableObjectAtIndex(list, 2); return pigeonResult; } -+ (nullable PigeonSignInProvider *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonSignInProvider fromList:list] : nil; ++ (nullable InternalSignInProvider *)nullableFromList:(NSArray *)list { + return (list) ? [InternalSignInProvider fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.providerId ?: [NSNull null], self.scopes ?: [NSNull null], self.customParameters ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalSignInProvider *other = (InternalSignInProvider *)object; + return FLTPigeonDeepEquals(self.providerId, other.providerId) && + FLTPigeonDeepEquals(self.scopes, other.scopes) && + FLTPigeonDeepEquals(self.customParameters, other.customParameters); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.providerId); + result = result * 31 + FLTPigeonDeepHash(self.scopes); + result = result * 31 + FLTPigeonDeepHash(self.customParameters); + return result; +} @end -@implementation PigeonVerifyPhoneNumberRequest +@implementation InternalVerifyPhoneNumberRequest + (instancetype)makeWithPhoneNumber:(nullable NSString *)phoneNumber timeout:(NSInteger)timeout forceResendingToken:(nullable NSNumber *)forceResendingToken autoRetrievedSmsCodeForTesting:(nullable NSString *)autoRetrievedSmsCodeForTesting multiFactorInfoId:(nullable NSString *)multiFactorInfoId multiFactorSessionId:(nullable NSString *)multiFactorSessionId { - PigeonVerifyPhoneNumberRequest *pigeonResult = [[PigeonVerifyPhoneNumberRequest alloc] init]; + InternalVerifyPhoneNumberRequest *pigeonResult = [[InternalVerifyPhoneNumberRequest alloc] init]; pigeonResult.phoneNumber = phoneNumber; pigeonResult.timeout = timeout; pigeonResult.forceResendingToken = forceResendingToken; @@ -633,8 +1093,8 @@ + (instancetype)makeWithPhoneNumber:(nullable NSString *)phoneNumber pigeonResult.multiFactorSessionId = multiFactorSessionId; return pigeonResult; } -+ (PigeonVerifyPhoneNumberRequest *)fromList:(NSArray *)list { - PigeonVerifyPhoneNumberRequest *pigeonResult = [[PigeonVerifyPhoneNumberRequest alloc] init]; ++ (InternalVerifyPhoneNumberRequest *)fromList:(NSArray *)list { + InternalVerifyPhoneNumberRequest *pigeonResult = [[InternalVerifyPhoneNumberRequest alloc] init]; pigeonResult.phoneNumber = GetNullableObjectAtIndex(list, 0); pigeonResult.timeout = [GetNullableObjectAtIndex(list, 1) integerValue]; pigeonResult.forceResendingToken = GetNullableObjectAtIndex(list, 2); @@ -643,10 +1103,10 @@ + (PigeonVerifyPhoneNumberRequest *)fromList:(NSArray *)list { pigeonResult.multiFactorSessionId = GetNullableObjectAtIndex(list, 5); return pigeonResult; } -+ (nullable PigeonVerifyPhoneNumberRequest *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonVerifyPhoneNumberRequest fromList:list] : nil; ++ (nullable InternalVerifyPhoneNumberRequest *)nullableFromList:(NSArray *)list { + return (list) ? [InternalVerifyPhoneNumberRequest fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.phoneNumber ?: [NSNull null], @(self.timeout), @@ -656,9 +1116,36 @@ - (NSArray *)toList { self.multiFactorSessionId ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalVerifyPhoneNumberRequest *other = (InternalVerifyPhoneNumberRequest *)object; + return FLTPigeonDeepEquals(self.phoneNumber, other.phoneNumber) && + self.timeout == other.timeout && + FLTPigeonDeepEquals(self.forceResendingToken, other.forceResendingToken) && + FLTPigeonDeepEquals(self.autoRetrievedSmsCodeForTesting, + other.autoRetrievedSmsCodeForTesting) && + FLTPigeonDeepEquals(self.multiFactorInfoId, other.multiFactorInfoId) && + FLTPigeonDeepEquals(self.multiFactorSessionId, other.multiFactorSessionId); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.phoneNumber); + result = result * 31 + @(self.timeout).hash; + result = result * 31 + FLTPigeonDeepHash(self.forceResendingToken); + result = result * 31 + FLTPigeonDeepHash(self.autoRetrievedSmsCodeForTesting); + result = result * 31 + FLTPigeonDeepHash(self.multiFactorInfoId); + result = result * 31 + FLTPigeonDeepHash(self.multiFactorSessionId); + return result; +} @end -@implementation PigeonIdTokenResult +@implementation InternalIdTokenResult + (instancetype)makeWithToken:(nullable NSString *)token expirationTimestamp:(nullable NSNumber *)expirationTimestamp authTimestamp:(nullable NSNumber *)authTimestamp @@ -666,7 +1153,7 @@ + (instancetype)makeWithToken:(nullable NSString *)token signInProvider:(nullable NSString *)signInProvider claims:(nullable NSDictionary *)claims signInSecondFactor:(nullable NSString *)signInSecondFactor { - PigeonIdTokenResult *pigeonResult = [[PigeonIdTokenResult alloc] init]; + InternalIdTokenResult *pigeonResult = [[InternalIdTokenResult alloc] init]; pigeonResult.token = token; pigeonResult.expirationTimestamp = expirationTimestamp; pigeonResult.authTimestamp = authTimestamp; @@ -676,8 +1163,8 @@ + (instancetype)makeWithToken:(nullable NSString *)token pigeonResult.signInSecondFactor = signInSecondFactor; return pigeonResult; } -+ (PigeonIdTokenResult *)fromList:(NSArray *)list { - PigeonIdTokenResult *pigeonResult = [[PigeonIdTokenResult alloc] init]; ++ (InternalIdTokenResult *)fromList:(NSArray *)list { + InternalIdTokenResult *pigeonResult = [[InternalIdTokenResult alloc] init]; pigeonResult.token = GetNullableObjectAtIndex(list, 0); pigeonResult.expirationTimestamp = GetNullableObjectAtIndex(list, 1); pigeonResult.authTimestamp = GetNullableObjectAtIndex(list, 2); @@ -687,10 +1174,10 @@ + (PigeonIdTokenResult *)fromList:(NSArray *)list { pigeonResult.signInSecondFactor = GetNullableObjectAtIndex(list, 6); return pigeonResult; } -+ (nullable PigeonIdTokenResult *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonIdTokenResult fromList:list] : nil; ++ (nullable InternalIdTokenResult *)nullableFromList:(NSArray *)list { + return (list) ? [InternalIdTokenResult fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.token ?: [NSNull null], self.expirationTimestamp ?: [NSNull null], @@ -701,32 +1188,60 @@ - (NSArray *)toList { self.signInSecondFactor ?: [NSNull null], ]; } -@end - -@implementation PigeonUserProfile +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalIdTokenResult *other = (InternalIdTokenResult *)object; + return FLTPigeonDeepEquals(self.token, other.token) && + FLTPigeonDeepEquals(self.expirationTimestamp, other.expirationTimestamp) && + FLTPigeonDeepEquals(self.authTimestamp, other.authTimestamp) && + FLTPigeonDeepEquals(self.issuedAtTimestamp, other.issuedAtTimestamp) && + FLTPigeonDeepEquals(self.signInProvider, other.signInProvider) && + FLTPigeonDeepEquals(self.claims, other.claims) && + FLTPigeonDeepEquals(self.signInSecondFactor, other.signInSecondFactor); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.token); + result = result * 31 + FLTPigeonDeepHash(self.expirationTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.authTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.issuedAtTimestamp); + result = result * 31 + FLTPigeonDeepHash(self.signInProvider); + result = result * 31 + FLTPigeonDeepHash(self.claims); + result = result * 31 + FLTPigeonDeepHash(self.signInSecondFactor); + return result; +} +@end + +@implementation InternalUserProfile + (instancetype)makeWithDisplayName:(nullable NSString *)displayName photoUrl:(nullable NSString *)photoUrl displayNameChanged:(BOOL)displayNameChanged photoUrlChanged:(BOOL)photoUrlChanged { - PigeonUserProfile *pigeonResult = [[PigeonUserProfile alloc] init]; + InternalUserProfile *pigeonResult = [[InternalUserProfile alloc] init]; pigeonResult.displayName = displayName; pigeonResult.photoUrl = photoUrl; pigeonResult.displayNameChanged = displayNameChanged; pigeonResult.photoUrlChanged = photoUrlChanged; return pigeonResult; } -+ (PigeonUserProfile *)fromList:(NSArray *)list { - PigeonUserProfile *pigeonResult = [[PigeonUserProfile alloc] init]; ++ (InternalUserProfile *)fromList:(NSArray *)list { + InternalUserProfile *pigeonResult = [[InternalUserProfile alloc] init]; pigeonResult.displayName = GetNullableObjectAtIndex(list, 0); pigeonResult.photoUrl = GetNullableObjectAtIndex(list, 1); pigeonResult.displayNameChanged = [GetNullableObjectAtIndex(list, 2) boolValue]; pigeonResult.photoUrlChanged = [GetNullableObjectAtIndex(list, 3) boolValue]; return pigeonResult; } -+ (nullable PigeonUserProfile *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonUserProfile fromList:list] : nil; ++ (nullable InternalUserProfile *)nullableFromList:(NSArray *)list { + return (list) ? [InternalUserProfile fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.displayName ?: [NSNull null], self.photoUrl ?: [NSNull null], @@ -734,15 +1249,37 @@ - (NSArray *)toList { @(self.photoUrlChanged), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalUserProfile *other = (InternalUserProfile *)object; + return FLTPigeonDeepEquals(self.displayName, other.displayName) && + FLTPigeonDeepEquals(self.photoUrl, other.photoUrl) && + self.displayNameChanged == other.displayNameChanged && + self.photoUrlChanged == other.photoUrlChanged; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.displayName); + result = result * 31 + FLTPigeonDeepHash(self.photoUrl); + result = result * 31 + @(self.displayNameChanged).hash; + result = result * 31 + @(self.photoUrlChanged).hash; + return result; +} @end -@implementation PigeonTotpSecret +@implementation InternalTotpSecret + (instancetype)makeWithCodeIntervalSeconds:(nullable NSNumber *)codeIntervalSeconds codeLength:(nullable NSNumber *)codeLength enrollmentCompletionDeadline:(nullable NSNumber *)enrollmentCompletionDeadline hashingAlgorithm:(nullable NSString *)hashingAlgorithm secretKey:(NSString *)secretKey { - PigeonTotpSecret *pigeonResult = [[PigeonTotpSecret alloc] init]; + InternalTotpSecret *pigeonResult = [[InternalTotpSecret alloc] init]; pigeonResult.codeIntervalSeconds = codeIntervalSeconds; pigeonResult.codeLength = codeLength; pigeonResult.enrollmentCompletionDeadline = enrollmentCompletionDeadline; @@ -750,8 +1287,8 @@ + (instancetype)makeWithCodeIntervalSeconds:(nullable NSNumber *)codeIntervalSec pigeonResult.secretKey = secretKey; return pigeonResult; } -+ (PigeonTotpSecret *)fromList:(NSArray *)list { - PigeonTotpSecret *pigeonResult = [[PigeonTotpSecret alloc] init]; ++ (InternalTotpSecret *)fromList:(NSArray *)list { + InternalTotpSecret *pigeonResult = [[InternalTotpSecret alloc] init]; pigeonResult.codeIntervalSeconds = GetNullableObjectAtIndex(list, 0); pigeonResult.codeLength = GetNullableObjectAtIndex(list, 1); pigeonResult.enrollmentCompletionDeadline = GetNullableObjectAtIndex(list, 2); @@ -759,10 +1296,10 @@ + (PigeonTotpSecret *)fromList:(NSArray *)list { pigeonResult.secretKey = GetNullableObjectAtIndex(list, 4); return pigeonResult; } -+ (nullable PigeonTotpSecret *)nullableFromList:(NSArray *)list { - return (list) ? [PigeonTotpSecret fromList:list] : nil; ++ (nullable InternalTotpSecret *)nullableFromList:(NSArray *)list { + return (list) ? [InternalTotpSecret fromList:list] : nil; } -- (NSArray *)toList { +- (NSArray *)toList { return @[ self.codeIntervalSeconds ?: [NSNull null], self.codeLength ?: [NSNull null], @@ -771,141 +1308,180 @@ - (NSArray *)toList { self.secretKey ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + InternalTotpSecret *other = (InternalTotpSecret *)object; + return FLTPigeonDeepEquals(self.codeIntervalSeconds, other.codeIntervalSeconds) && + FLTPigeonDeepEquals(self.codeLength, other.codeLength) && + FLTPigeonDeepEquals(self.enrollmentCompletionDeadline, + other.enrollmentCompletionDeadline) && + FLTPigeonDeepEquals(self.hashingAlgorithm, other.hashingAlgorithm) && + FLTPigeonDeepEquals(self.secretKey, other.secretKey); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.codeIntervalSeconds); + result = result * 31 + FLTPigeonDeepHash(self.codeLength); + result = result * 31 + FLTPigeonDeepHash(self.enrollmentCompletionDeadline); + result = result * 31 + FLTPigeonDeepHash(self.hashingAlgorithm); + result = result * 31 + FLTPigeonDeepHash(self.secretKey); + return result; +} @end -@interface FirebaseAuthHostApiCodecReader : FlutterStandardReader +@interface nullFirebaseAuthMessagesPigeonCodecReader : FlutterStandardReader @end -@implementation FirebaseAuthHostApiCodecReader +@implementation nullFirebaseAuthMessagesPigeonCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: - return [AuthPigeonFirebaseApp fromList:[self readValue]]; - case 129: - return [PigeonActionCodeInfo fromList:[self readValue]]; + case 129: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil + ? nil + : [[ActionCodeInfoOperationBox alloc] initWithValue:[enumAsNumber integerValue]]; + } case 130: - return [PigeonActionCodeInfoData fromList:[self readValue]]; + return [InternalMultiFactorSession fromList:[self readValue]]; case 131: - return [PigeonActionCodeSettings fromList:[self readValue]]; + return [InternalPhoneMultiFactorAssertion fromList:[self readValue]]; case 132: - return [PigeonAdditionalUserInfo fromList:[self readValue]]; + return [InternalMultiFactorInfo fromList:[self readValue]]; case 133: - return [PigeonAuthCredential fromList:[self readValue]]; + return [AuthPigeonFirebaseApp fromList:[self readValue]]; case 134: - return [PigeonFirebaseAuthSettings fromList:[self readValue]]; + return [InternalActionCodeInfoData fromList:[self readValue]]; case 135: - return [PigeonIdTokenResult fromList:[self readValue]]; + return [InternalActionCodeInfo fromList:[self readValue]]; case 136: - return [PigeonMultiFactorInfo fromList:[self readValue]]; + return [InternalAdditionalUserInfo fromList:[self readValue]]; case 137: - return [PigeonMultiFactorSession fromList:[self readValue]]; + return [InternalAuthCredential fromList:[self readValue]]; case 138: - return [PigeonPhoneMultiFactorAssertion fromList:[self readValue]]; + return [InternalUserInfo fromList:[self readValue]]; case 139: - return [PigeonSignInProvider fromList:[self readValue]]; + return [InternalUserDetails fromList:[self readValue]]; case 140: - return [PigeonTotpSecret fromList:[self readValue]]; + return [InternalUserCredential fromList:[self readValue]]; case 141: - return [PigeonUserCredential fromList:[self readValue]]; + return [InternalAuthCredentialInput fromList:[self readValue]]; case 142: - return [PigeonUserDetails fromList:[self readValue]]; + return [InternalActionCodeSettings fromList:[self readValue]]; case 143: - return [PigeonUserInfo fromList:[self readValue]]; + return [InternalFirebaseAuthSettings fromList:[self readValue]]; case 144: - return [PigeonUserProfile fromList:[self readValue]]; + return [InternalSignInProvider fromList:[self readValue]]; case 145: - return [PigeonVerifyPhoneNumberRequest fromList:[self readValue]]; + return [InternalVerifyPhoneNumberRequest fromList:[self readValue]]; + case 146: + return [InternalIdTokenResult fromList:[self readValue]]; + case 147: + return [InternalUserProfile fromList:[self readValue]]; + case 148: + return [InternalTotpSecret fromList:[self readValue]]; default: return [super readValueOfType:type]; } } @end -@interface FirebaseAuthHostApiCodecWriter : FlutterStandardWriter +@interface nullFirebaseAuthMessagesPigeonCodecWriter : FlutterStandardWriter @end -@implementation FirebaseAuthHostApiCodecWriter +@implementation nullFirebaseAuthMessagesPigeonCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[AuthPigeonFirebaseApp class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonActionCodeInfo class]]) { + if ([value isKindOfClass:[ActionCodeInfoOperationBox class]]) { + ActionCodeInfoOperationBox *box = (ActionCodeInfoOperationBox *)value; [self writeByte:129]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonActionCodeInfoData class]]) { + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[InternalMultiFactorSession class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonActionCodeSettings class]]) { + } else if ([value isKindOfClass:[InternalPhoneMultiFactorAssertion class]]) { [self writeByte:131]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonAdditionalUserInfo class]]) { + } else if ([value isKindOfClass:[InternalMultiFactorInfo class]]) { [self writeByte:132]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonAuthCredential class]]) { + } else if ([value isKindOfClass:[AuthPigeonFirebaseApp class]]) { [self writeByte:133]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonFirebaseAuthSettings class]]) { + } else if ([value isKindOfClass:[InternalActionCodeInfoData class]]) { [self writeByte:134]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonIdTokenResult class]]) { + } else if ([value isKindOfClass:[InternalActionCodeInfo class]]) { [self writeByte:135]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonMultiFactorInfo class]]) { + } else if ([value isKindOfClass:[InternalAdditionalUserInfo class]]) { [self writeByte:136]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonMultiFactorSession class]]) { + } else if ([value isKindOfClass:[InternalAuthCredential class]]) { [self writeByte:137]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonPhoneMultiFactorAssertion class]]) { + } else if ([value isKindOfClass:[InternalUserInfo class]]) { [self writeByte:138]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonSignInProvider class]]) { + } else if ([value isKindOfClass:[InternalUserDetails class]]) { [self writeByte:139]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonTotpSecret class]]) { + } else if ([value isKindOfClass:[InternalUserCredential class]]) { [self writeByte:140]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserCredential class]]) { + } else if ([value isKindOfClass:[InternalAuthCredentialInput class]]) { [self writeByte:141]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserDetails class]]) { + } else if ([value isKindOfClass:[InternalActionCodeSettings class]]) { [self writeByte:142]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserInfo class]]) { + } else if ([value isKindOfClass:[InternalFirebaseAuthSettings class]]) { [self writeByte:143]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserProfile class]]) { + } else if ([value isKindOfClass:[InternalSignInProvider class]]) { [self writeByte:144]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonVerifyPhoneNumberRequest class]]) { + } else if ([value isKindOfClass:[InternalVerifyPhoneNumberRequest class]]) { [self writeByte:145]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalIdTokenResult class]]) { + [self writeByte:146]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalUserProfile class]]) { + [self writeByte:147]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[InternalTotpSecret class]]) { + [self writeByte:148]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } } @end -@interface FirebaseAuthHostApiCodecReaderWriter : FlutterStandardReaderWriter +@interface nullFirebaseAuthMessagesPigeonCodecReaderWriter : FlutterStandardReaderWriter @end -@implementation FirebaseAuthHostApiCodecReaderWriter +@implementation nullFirebaseAuthMessagesPigeonCodecReaderWriter - (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[FirebaseAuthHostApiCodecWriter alloc] initWithData:data]; + return [[nullFirebaseAuthMessagesPigeonCodecWriter alloc] initWithData:data]; } - (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[FirebaseAuthHostApiCodecReader alloc] initWithData:data]; + return [[nullFirebaseAuthMessagesPigeonCodecReader alloc] initWithData:data]; } @end -NSObject *FirebaseAuthHostApiGetCodec(void) { +NSObject *nullGetFirebaseAuthMessagesCodec(void) { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - FirebaseAuthHostApiCodecReaderWriter *readerWriter = - [[FirebaseAuthHostApiCodecReaderWriter alloc] init]; + nullFirebaseAuthMessagesPigeonCodecReaderWriter *readerWriter = + [[nullFirebaseAuthMessagesPigeonCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } - void SetUpFirebaseAuthHostApi(id binaryMessenger, NSObject *api) { SetUpFirebaseAuthHostApiWithSuffix(binaryMessenger, api, @""); @@ -925,14 +1501,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.registerIdTokenListener", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(registerIdTokenListenerApp:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(registerIdTokenListenerApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api registerIdTokenListenerApp:arg_app completion:^(NSString *_Nullable output, @@ -952,14 +1528,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.registerAuthStateListener", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(registerAuthStateListenerApp:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(registerAuthStateListenerApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api registerAuthStateListenerApp:arg_app completion:^(NSString *_Nullable output, @@ -978,14 +1554,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"interface.FirebaseAuthHostApi.useEmulator", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(useEmulatorApp:host:port:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(useEmulatorApp:host:port:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_host = GetNullableObjectAtIndex(args, 1); NSInteger arg_port = [GetNullableObjectAtIndex(args, 2) integerValue]; @@ -1007,14 +1583,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"interface.FirebaseAuthHostApi.applyActionCode", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(applyActionCodeApp:code:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(applyActionCodeApp:code:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_code = GetNullableObjectAtIndex(args, 1); [api applyActionCodeApp:arg_app @@ -1034,19 +1610,19 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"interface.FirebaseAuthHostApi.checkActionCode", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(checkActionCodeApp:code:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(checkActionCodeApp:code:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_code = GetNullableObjectAtIndex(args, 1); [api checkActionCodeApp:arg_app code:arg_code - completion:^(PigeonActionCodeInfo *_Nullable output, + completion:^(InternalActionCodeInfo *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1063,7 +1639,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.confirmPasswordReset", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(confirmPasswordResetApp:code:newPassword:completion:)], @@ -1071,7 +1647,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(confirmPasswordResetApp:code:newPassword:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_code = GetNullableObjectAtIndex(args, 1); NSString *arg_newPassword = GetNullableObjectAtIndex(args, 2); @@ -1094,7 +1670,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.createUserWithEmailAndPassword", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api @@ -1104,14 +1680,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(createUserWithEmailAndPasswordApp:email:password:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_email = GetNullableObjectAtIndex(args, 1); NSString *arg_password = GetNullableObjectAtIndex(args, 2); [api createUserWithEmailAndPasswordApp:arg_app email:arg_email password:arg_password - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1128,17 +1704,17 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.signInAnonymously", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(signInAnonymouslyApp:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(signInAnonymouslyApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api signInAnonymouslyApp:arg_app - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1155,19 +1731,19 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.signInWithCredential", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(signInWithCredentialApp:input:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(signInWithCredentialApp:input:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); [api signInWithCredentialApp:arg_app input:arg_input - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1184,19 +1760,19 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.signInWithCustomToken", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(signInWithCustomTokenApp:token:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(signInWithCustomTokenApp:token:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_token = GetNullableObjectAtIndex(args, 1); [api signInWithCustomTokenApp:arg_app token:arg_token - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1213,7 +1789,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.signInWithEmailAndPassword", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector( @@ -1222,14 +1798,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(signInWithEmailAndPasswordApp:email:password:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_email = GetNullableObjectAtIndex(args, 1); NSString *arg_password = GetNullableObjectAtIndex(args, 2); [api signInWithEmailAndPasswordApp:arg_app email:arg_email password:arg_password - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1246,7 +1822,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.signInWithEmailLink", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(signInWithEmailLinkApp:email:emailLink:completion:)], @@ -1254,14 +1830,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(signInWithEmailLinkApp:email:emailLink:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_email = GetNullableObjectAtIndex(args, 1); NSString *arg_emailLink = GetNullableObjectAtIndex(args, 2); [api signInWithEmailLinkApp:arg_app email:arg_email emailLink:arg_emailLink - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1278,7 +1854,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.signInWithProvider", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(signInWithProviderApp:signInProvider:completion:)], @@ -1286,12 +1862,12 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(signInWithProviderApp:signInProvider:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); + InternalSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); [api signInWithProviderApp:arg_app signInProvider:arg_signInProvider - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1307,14 +1883,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"interface.FirebaseAuthHostApi.signOut", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(signOutApp:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to @selector(signOutApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api signOutApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -1333,14 +1909,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.fetchSignInMethodsForEmail", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(fetchSignInMethodsForEmailApp:email:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(fetchSignInMethodsForEmailApp:email:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_email = GetNullableObjectAtIndex(args, 1); [api fetchSignInMethodsForEmailApp:arg_app @@ -1362,7 +1938,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.sendPasswordResetEmail", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector: @selector(sendPasswordResetEmailApp:email:actionCodeSettings:completion:)], @@ -1370,10 +1946,10 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(sendPasswordResetEmailApp:email:actionCodeSettings:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_email = GetNullableObjectAtIndex(args, 1); - PigeonActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); [api sendPasswordResetEmailApp:arg_app email:arg_email actionCodeSettings:arg_actionCodeSettings @@ -1393,7 +1969,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.sendSignInLinkToEmail", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector: @selector(sendSignInLinkToEmailApp:email:actionCodeSettings:completion:)], @@ -1401,10 +1977,10 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(sendSignInLinkToEmailApp:email:actionCodeSettings:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_email = GetNullableObjectAtIndex(args, 1); - PigeonActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); [api sendSignInLinkToEmailApp:arg_app email:arg_email actionCodeSettings:arg_actionCodeSettings @@ -1423,14 +1999,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"interface.FirebaseAuthHostApi.setLanguageCode", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(setLanguageCodeApp:languageCode:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(setLanguageCodeApp:languageCode:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_languageCode = GetNullableObjectAtIndex(args, 1); [api setLanguageCodeApp:arg_app @@ -1450,16 +2026,16 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"interface.FirebaseAuthHostApi.setSettings", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(setSettingsApp:settings:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(setSettingsApp:settings:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonFirebaseAuthSettings *arg_settings = GetNullableObjectAtIndex(args, 1); + InternalFirebaseAuthSettings *arg_settings = GetNullableObjectAtIndex(args, 1); [api setSettingsApp:arg_app settings:arg_settings completion:^(FlutterError *_Nullable error) { @@ -1478,14 +2054,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.verifyPasswordResetCode", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(verifyPasswordResetCodeApp:code:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(verifyPasswordResetCodeApp:code:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_code = GetNullableObjectAtIndex(args, 1); [api verifyPasswordResetCodeApp:arg_app @@ -1507,16 +2083,16 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.verifyPhoneNumber", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(verifyPhoneNumberApp:request:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(verifyPhoneNumberApp:request:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonVerifyPhoneNumberRequest *arg_request = GetNullableObjectAtIndex(args, 1); + InternalVerifyPhoneNumberRequest *arg_request = GetNullableObjectAtIndex(args, 1); [api verifyPhoneNumberApp:arg_app request:arg_request completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { @@ -1535,7 +2111,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.revokeTokenWithAuthorizationCode", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(revokeTokenWithAuthorizationCodeApp: authorizationCode:completion:)], @@ -1543,7 +2119,7 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"@selector(revokeTokenWithAuthorizationCodeApp:authorizationCode:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_authorizationCode = GetNullableObjectAtIndex(args, 1); [api revokeTokenWithAuthorizationCodeApp:arg_app @@ -1556,6 +2132,34 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.firebase_auth_platform_interface." + @"FirebaseAuthHostApi.revokeAccessToken", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetFirebaseAuthMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(revokeAccessTokenApp:accessToken:completion:)], + @"FirebaseAuthHostApi api (%@) doesn't respond to " + @"@selector(revokeAccessTokenApp:accessToken:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); + NSString *arg_accessToken = GetNullableObjectAtIndex(args, 1); + [api revokeAccessTokenApp:arg_app + accessToken:arg_accessToken + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:[NSString @@ -1564,14 +2168,14 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng @"FirebaseAuthHostApi.initializeRecaptchaConfig", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(initializeRecaptchaConfigApp:completion:)], @"FirebaseAuthHostApi api (%@) doesn't respond to " @"@selector(initializeRecaptchaConfigApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api initializeRecaptchaConfigApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -1583,139 +2187,6 @@ void SetUpFirebaseAuthHostApiWithSuffix(id binaryMesseng } } } -@interface FirebaseAuthUserHostApiCodecReader : FlutterStandardReader -@end -@implementation FirebaseAuthUserHostApiCodecReader -- (nullable id)readValueOfType:(UInt8)type { - switch (type) { - case 128: - return [AuthPigeonFirebaseApp fromList:[self readValue]]; - case 129: - return [PigeonActionCodeInfo fromList:[self readValue]]; - case 130: - return [PigeonActionCodeInfoData fromList:[self readValue]]; - case 131: - return [PigeonActionCodeSettings fromList:[self readValue]]; - case 132: - return [PigeonAdditionalUserInfo fromList:[self readValue]]; - case 133: - return [PigeonAuthCredential fromList:[self readValue]]; - case 134: - return [PigeonFirebaseAuthSettings fromList:[self readValue]]; - case 135: - return [PigeonIdTokenResult fromList:[self readValue]]; - case 136: - return [PigeonMultiFactorInfo fromList:[self readValue]]; - case 137: - return [PigeonMultiFactorSession fromList:[self readValue]]; - case 138: - return [PigeonPhoneMultiFactorAssertion fromList:[self readValue]]; - case 139: - return [PigeonSignInProvider fromList:[self readValue]]; - case 140: - return [PigeonTotpSecret fromList:[self readValue]]; - case 141: - return [PigeonUserCredential fromList:[self readValue]]; - case 142: - return [PigeonUserDetails fromList:[self readValue]]; - case 143: - return [PigeonUserInfo fromList:[self readValue]]; - case 144: - return [PigeonUserProfile fromList:[self readValue]]; - case 145: - return [PigeonVerifyPhoneNumberRequest fromList:[self readValue]]; - default: - return [super readValueOfType:type]; - } -} -@end - -@interface FirebaseAuthUserHostApiCodecWriter : FlutterStandardWriter -@end -@implementation FirebaseAuthUserHostApiCodecWriter -- (void)writeValue:(id)value { - if ([value isKindOfClass:[AuthPigeonFirebaseApp class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonActionCodeInfo class]]) { - [self writeByte:129]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonActionCodeInfoData class]]) { - [self writeByte:130]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonActionCodeSettings class]]) { - [self writeByte:131]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonAdditionalUserInfo class]]) { - [self writeByte:132]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonAuthCredential class]]) { - [self writeByte:133]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonFirebaseAuthSettings class]]) { - [self writeByte:134]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonIdTokenResult class]]) { - [self writeByte:135]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonMultiFactorInfo class]]) { - [self writeByte:136]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonMultiFactorSession class]]) { - [self writeByte:137]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonPhoneMultiFactorAssertion class]]) { - [self writeByte:138]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonSignInProvider class]]) { - [self writeByte:139]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonTotpSecret class]]) { - [self writeByte:140]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserCredential class]]) { - [self writeByte:141]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserDetails class]]) { - [self writeByte:142]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserInfo class]]) { - [self writeByte:143]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserProfile class]]) { - [self writeByte:144]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonVerifyPhoneNumberRequest class]]) { - [self writeByte:145]; - [self writeValue:[value toList]]; - } else { - [super writeValue:value]; - } -} -@end - -@interface FirebaseAuthUserHostApiCodecReaderWriter : FlutterStandardReaderWriter -@end -@implementation FirebaseAuthUserHostApiCodecReaderWriter -- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[FirebaseAuthUserHostApiCodecWriter alloc] initWithData:data]; -} -- (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[FirebaseAuthUserHostApiCodecReader alloc] initWithData:data]; -} -@end - -NSObject *FirebaseAuthUserHostApiGetCodec(void) { - static FlutterStandardMessageCodec *sSharedObject = nil; - static dispatch_once_t sPred = 0; - dispatch_once(&sPred, ^{ - FirebaseAuthUserHostApiCodecReaderWriter *readerWriter = - [[FirebaseAuthUserHostApiCodecReaderWriter alloc] init]; - sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; - }); - return sSharedObject; -} - void SetUpFirebaseAuthUserHostApi(id binaryMessenger, NSObject *api) { SetUpFirebaseAuthUserHostApiWithSuffix(binaryMessenger, api, @""); @@ -1734,14 +2205,14 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"interface.FirebaseAuthUserHostApi.delete", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(deleteApp:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to @selector(deleteApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api deleteApp:arg_app completion:^(FlutterError *_Nullable error) { @@ -1759,19 +2230,20 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"interface.FirebaseAuthUserHostApi.getIdToken", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(getIdTokenApp:forceRefresh:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(getIdTokenApp:forceRefresh:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); BOOL arg_forceRefresh = [GetNullableObjectAtIndex(args, 1) boolValue]; [api getIdTokenApp:arg_app forceRefresh:arg_forceRefresh - completion:^(PigeonIdTokenResult *_Nullable output, FlutterError *_Nullable error) { + completion:^(InternalIdTokenResult *_Nullable output, + FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; }]; @@ -1787,19 +2259,19 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.linkWithCredential", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(linkWithCredentialApp:input:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(linkWithCredentialApp:input:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); [api linkWithCredentialApp:arg_app input:arg_input - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1816,19 +2288,19 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.linkWithProvider", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(linkWithProviderApp:signInProvider:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(linkWithProviderApp:signInProvider:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); + InternalSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); [api linkWithProviderApp:arg_app signInProvider:arg_signInProvider - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1845,7 +2317,7 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.reauthenticateWithCredential", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(reauthenticateWithCredentialApp:input:completion:)], @@ -1853,12 +2325,12 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"@selector(reauthenticateWithCredentialApp:input:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); [api reauthenticateWithCredentialApp:arg_app input:arg_input - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1875,7 +2347,7 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.reauthenticateWithProvider", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector( @@ -1884,12 +2356,12 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"@selector(reauthenticateWithProviderApp:signInProvider:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); + InternalSignInProvider *arg_signInProvider = GetNullableObjectAtIndex(args, 1); [api reauthenticateWithProviderApp:arg_app signInProvider:arg_signInProvider - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1905,17 +2377,17 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"interface.FirebaseAuthUserHostApi.reload", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(reloadApp:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to @selector(reloadApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api reloadApp:arg_app - completion:^(PigeonUserDetails *_Nullable output, FlutterError *_Nullable error) { + completion:^(InternalUserDetails *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; }]; @@ -1931,7 +2403,7 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.sendEmailVerification", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector( @@ -1940,9 +2412,9 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"@selector(sendEmailVerificationApp:actionCodeSettings:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 1); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 1); [api sendEmailVerificationApp:arg_app actionCodeSettings:arg_actionCodeSettings completion:^(FlutterError *_Nullable error) { @@ -1960,19 +2432,19 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"interface.FirebaseAuthUserHostApi.unlink", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(unlinkApp:providerId:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(unlinkApp:providerId:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_providerId = GetNullableObjectAtIndex(args, 1); [api unlinkApp:arg_app providerId:arg_providerId - completion:^(PigeonUserCredential *_Nullable output, FlutterError *_Nullable error) { + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; }]; @@ -1987,21 +2459,22 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"interface.FirebaseAuthUserHostApi.updateEmail", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(updateEmailApp:newEmail:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(updateEmailApp:newEmail:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_newEmail = GetNullableObjectAtIndex(args, 1); - [api updateEmailApp:arg_app - newEmail:arg_newEmail - completion:^(PigeonUserDetails *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api + updateEmailApp:arg_app + newEmail:arg_newEmail + completion:^(InternalUserDetails *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; @@ -2015,19 +2488,19 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.updatePassword", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(updatePasswordApp:newPassword:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(updatePasswordApp:newPassword:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_newPassword = GetNullableObjectAtIndex(args, 1); [api updatePasswordApp:arg_app newPassword:arg_newPassword - completion:^(PigeonUserDetails *_Nullable output, + completion:^(InternalUserDetails *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -2044,19 +2517,19 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.updatePhoneNumber", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(updatePhoneNumberApp:input:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(updatePhoneNumberApp:input:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSDictionary *arg_input = GetNullableObjectAtIndex(args, 1); [api updatePhoneNumberApp:arg_app input:arg_input - completion:^(PigeonUserDetails *_Nullable output, + completion:^(InternalUserDetails *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -2073,22 +2546,22 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.updateProfile", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(updateProfileApp:profile:completion:)], @"FirebaseAuthUserHostApi api (%@) doesn't respond to " @"@selector(updateProfileApp:profile:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonUserProfile *arg_profile = GetNullableObjectAtIndex(args, 1); - [api - updateProfileApp:arg_app - profile:arg_profile - completion:^(PigeonUserDetails *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + InternalUserProfile *arg_profile = GetNullableObjectAtIndex(args, 1); + [api updateProfileApp:arg_app + profile:arg_profile + completion:^(InternalUserDetails *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; @@ -2102,7 +2575,7 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"FirebaseAuthUserHostApi.verifyBeforeUpdateEmail", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FirebaseAuthUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(verifyBeforeUpdateEmailApp:newEmail: actionCodeSettings:completion:)], @@ -2110,10 +2583,10 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes @"@selector(verifyBeforeUpdateEmailApp:newEmail:actionCodeSettings:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_newEmail = GetNullableObjectAtIndex(args, 1); - PigeonActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); + InternalActionCodeSettings *arg_actionCodeSettings = GetNullableObjectAtIndex(args, 2); [api verifyBeforeUpdateEmailApp:arg_app newEmail:arg_newEmail actionCodeSettings:arg_actionCodeSettings @@ -2126,69 +2599,6 @@ void SetUpFirebaseAuthUserHostApiWithSuffix(id binaryMes } } } -@interface MultiFactorUserHostApiCodecReader : FlutterStandardReader -@end -@implementation MultiFactorUserHostApiCodecReader -- (nullable id)readValueOfType:(UInt8)type { - switch (type) { - case 128: - return [AuthPigeonFirebaseApp fromList:[self readValue]]; - case 129: - return [PigeonMultiFactorInfo fromList:[self readValue]]; - case 130: - return [PigeonMultiFactorSession fromList:[self readValue]]; - case 131: - return [PigeonPhoneMultiFactorAssertion fromList:[self readValue]]; - default: - return [super readValueOfType:type]; - } -} -@end - -@interface MultiFactorUserHostApiCodecWriter : FlutterStandardWriter -@end -@implementation MultiFactorUserHostApiCodecWriter -- (void)writeValue:(id)value { - if ([value isKindOfClass:[AuthPigeonFirebaseApp class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonMultiFactorInfo class]]) { - [self writeByte:129]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonMultiFactorSession class]]) { - [self writeByte:130]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonPhoneMultiFactorAssertion class]]) { - [self writeByte:131]; - [self writeValue:[value toList]]; - } else { - [super writeValue:value]; - } -} -@end - -@interface MultiFactorUserHostApiCodecReaderWriter : FlutterStandardReaderWriter -@end -@implementation MultiFactorUserHostApiCodecReaderWriter -- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[MultiFactorUserHostApiCodecWriter alloc] initWithData:data]; -} -- (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[MultiFactorUserHostApiCodecReader alloc] initWithData:data]; -} -@end - -NSObject *MultiFactorUserHostApiGetCodec(void) { - static FlutterStandardMessageCodec *sSharedObject = nil; - static dispatch_once_t sPred = 0; - dispatch_once(&sPred, ^{ - MultiFactorUserHostApiCodecReaderWriter *readerWriter = - [[MultiFactorUserHostApiCodecReaderWriter alloc] init]; - sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; - }); - return sSharedObject; -} - void SetUpMultiFactorUserHostApi(id binaryMessenger, NSObject *api) { SetUpMultiFactorUserHostApiWithSuffix(binaryMessenger, api, @""); @@ -2207,7 +2617,7 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess @"interface.MultiFactorUserHostApi.enrollPhone", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(enrollPhoneApp:assertion:displayName:completion:)], @@ -2215,9 +2625,9 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess @"@selector(enrollPhoneApp:assertion:displayName:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); - PigeonPhoneMultiFactorAssertion *arg_assertion = GetNullableObjectAtIndex(args, 1); + InternalPhoneMultiFactorAssertion *arg_assertion = GetNullableObjectAtIndex(args, 1); NSString *arg_displayName = GetNullableObjectAtIndex(args, 2); [api enrollPhoneApp:arg_app assertion:arg_assertion @@ -2237,7 +2647,7 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess @"interface.MultiFactorUserHostApi.enrollTotp", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(enrollTotpApp:assertionId:displayName:completion:)], @@ -2245,7 +2655,7 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess @"@selector(enrollTotpApp:assertionId:displayName:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_assertionId = GetNullableObjectAtIndex(args, 1); NSString *arg_displayName = GetNullableObjectAtIndex(args, 2); @@ -2267,17 +2677,17 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess @"interface.MultiFactorUserHostApi.getSession", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(getSessionApp:completion:)], @"MultiFactorUserHostApi api (%@) doesn't respond to " @"@selector(getSessionApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api getSessionApp:arg_app - completion:^(PigeonMultiFactorSession *_Nullable output, + completion:^(InternalMultiFactorSession *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -2293,14 +2703,14 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess @"interface.MultiFactorUserHostApi.unenroll", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(unenrollApp:factorUid:completion:)], @"MultiFactorUserHostApi api (%@) doesn't respond to " @"@selector(unenrollApp:factorUid:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); NSString *arg_factorUid = GetNullableObjectAtIndex(args, 1); [api unenrollApp:arg_app @@ -2321,17 +2731,17 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess @"MultiFactorUserHostApi.getEnrolledFactors", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorUserHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(getEnrolledFactorsApp:completion:)], @"MultiFactorUserHostApi api (%@) doesn't respond to " @"@selector(getEnrolledFactorsApp:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; AuthPigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0); [api getEnrolledFactorsApp:arg_app - completion:^(NSArray *_Nullable output, + completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -2341,79 +2751,6 @@ void SetUpMultiFactorUserHostApiWithSuffix(id binaryMess } } } -@interface MultiFactoResolverHostApiCodecReader : FlutterStandardReader -@end -@implementation MultiFactoResolverHostApiCodecReader -- (nullable id)readValueOfType:(UInt8)type { - switch (type) { - case 128: - return [PigeonAdditionalUserInfo fromList:[self readValue]]; - case 129: - return [PigeonAuthCredential fromList:[self readValue]]; - case 130: - return [PigeonPhoneMultiFactorAssertion fromList:[self readValue]]; - case 131: - return [PigeonUserCredential fromList:[self readValue]]; - case 132: - return [PigeonUserDetails fromList:[self readValue]]; - case 133: - return [PigeonUserInfo fromList:[self readValue]]; - default: - return [super readValueOfType:type]; - } -} -@end - -@interface MultiFactoResolverHostApiCodecWriter : FlutterStandardWriter -@end -@implementation MultiFactoResolverHostApiCodecWriter -- (void)writeValue:(id)value { - if ([value isKindOfClass:[PigeonAdditionalUserInfo class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonAuthCredential class]]) { - [self writeByte:129]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonPhoneMultiFactorAssertion class]]) { - [self writeByte:130]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserCredential class]]) { - [self writeByte:131]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserDetails class]]) { - [self writeByte:132]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[PigeonUserInfo class]]) { - [self writeByte:133]; - [self writeValue:[value toList]]; - } else { - [super writeValue:value]; - } -} -@end - -@interface MultiFactoResolverHostApiCodecReaderWriter : FlutterStandardReaderWriter -@end -@implementation MultiFactoResolverHostApiCodecReaderWriter -- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[MultiFactoResolverHostApiCodecWriter alloc] initWithData:data]; -} -- (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[MultiFactoResolverHostApiCodecReader alloc] initWithData:data]; -} -@end - -NSObject *MultiFactoResolverHostApiGetCodec(void) { - static FlutterStandardMessageCodec *sSharedObject = nil; - static dispatch_once_t sPred = 0; - dispatch_once(&sPred, ^{ - MultiFactoResolverHostApiCodecReaderWriter *readerWriter = - [[MultiFactoResolverHostApiCodecReaderWriter alloc] init]; - sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; - }); - return sSharedObject; -} - void SetUpMultiFactoResolverHostApi(id binaryMessenger, NSObject *api) { SetUpMultiFactoResolverHostApiWithSuffix(binaryMessenger, api, @""); @@ -2433,7 +2770,7 @@ void SetUpMultiFactoResolverHostApiWithSuffix(id binaryM @"MultiFactoResolverHostApi.resolveSignIn", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactoResolverHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector: @selector(resolveSignInResolverId:assertion:totpAssertionId:completion:)], @@ -2441,14 +2778,14 @@ void SetUpMultiFactoResolverHostApiWithSuffix(id binaryM @"@selector(resolveSignInResolverId:assertion:totpAssertionId:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; NSString *arg_resolverId = GetNullableObjectAtIndex(args, 0); - PigeonPhoneMultiFactorAssertion *arg_assertion = GetNullableObjectAtIndex(args, 1); + InternalPhoneMultiFactorAssertion *arg_assertion = GetNullableObjectAtIndex(args, 1); NSString *arg_totpAssertionId = GetNullableObjectAtIndex(args, 2); [api resolveSignInResolverId:arg_resolverId assertion:arg_assertion totpAssertionId:arg_totpAssertionId - completion:^(PigeonUserCredential *_Nullable output, + completion:^(InternalUserCredential *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -2458,54 +2795,6 @@ void SetUpMultiFactoResolverHostApiWithSuffix(id binaryM } } } -@interface MultiFactorTotpHostApiCodecReader : FlutterStandardReader -@end -@implementation MultiFactorTotpHostApiCodecReader -- (nullable id)readValueOfType:(UInt8)type { - switch (type) { - case 128: - return [PigeonTotpSecret fromList:[self readValue]]; - default: - return [super readValueOfType:type]; - } -} -@end - -@interface MultiFactorTotpHostApiCodecWriter : FlutterStandardWriter -@end -@implementation MultiFactorTotpHostApiCodecWriter -- (void)writeValue:(id)value { - if ([value isKindOfClass:[PigeonTotpSecret class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else { - [super writeValue:value]; - } -} -@end - -@interface MultiFactorTotpHostApiCodecReaderWriter : FlutterStandardReaderWriter -@end -@implementation MultiFactorTotpHostApiCodecReaderWriter -- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[MultiFactorTotpHostApiCodecWriter alloc] initWithData:data]; -} -- (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[MultiFactorTotpHostApiCodecReader alloc] initWithData:data]; -} -@end - -NSObject *MultiFactorTotpHostApiGetCodec(void) { - static FlutterStandardMessageCodec *sSharedObject = nil; - static dispatch_once_t sPred = 0; - dispatch_once(&sPred, ^{ - MultiFactorTotpHostApiCodecReaderWriter *readerWriter = - [[MultiFactorTotpHostApiCodecReaderWriter alloc] init]; - sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; - }); - return sSharedObject; -} - void SetUpMultiFactorTotpHostApi(id binaryMessenger, NSObject *api) { SetUpMultiFactorTotpHostApiWithSuffix(binaryMessenger, api, @""); @@ -2525,17 +2814,17 @@ void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMess @"MultiFactorTotpHostApi.generateSecret", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorTotpHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(generateSecretSessionId:completion:)], @"MultiFactorTotpHostApi api (%@) doesn't respond to " @"@selector(generateSecretSessionId:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; NSString *arg_sessionId = GetNullableObjectAtIndex(args, 0); [api generateSecretSessionId:arg_sessionId - completion:^(PigeonTotpSecret *_Nullable output, + completion:^(InternalTotpSecret *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -2552,7 +2841,7 @@ void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMess @"MultiFactorTotpHostApi.getAssertionForEnrollment", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorTotpHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector: @selector(getAssertionForEnrollmentSecretKey:oneTimePassword:completion:)], @@ -2560,7 +2849,7 @@ void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMess @"@selector(getAssertionForEnrollmentSecretKey:oneTimePassword:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; NSString *arg_secretKey = GetNullableObjectAtIndex(args, 0); NSString *arg_oneTimePassword = GetNullableObjectAtIndex(args, 1); [api getAssertionForEnrollmentSecretKey:arg_secretKey @@ -2582,7 +2871,7 @@ void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMess @"MultiFactorTotpHostApi.getAssertionForSignIn", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorTotpHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector: @selector(getAssertionForSignInEnrollmentId:oneTimePassword:completion:)], @@ -2590,7 +2879,7 @@ void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMess @"@selector(getAssertionForSignInEnrollmentId:oneTimePassword:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; NSString *arg_enrollmentId = GetNullableObjectAtIndex(args, 0); NSString *arg_oneTimePassword = GetNullableObjectAtIndex(args, 1); [api getAssertionForSignInEnrollmentId:arg_enrollmentId @@ -2605,12 +2894,6 @@ void SetUpMultiFactorTotpHostApiWithSuffix(id binaryMess } } } -NSObject *MultiFactorTotpSecretHostApiGetCodec(void) { - static FlutterStandardMessageCodec *sSharedObject = nil; - sSharedObject = [FlutterStandardMessageCodec sharedInstance]; - return sSharedObject; -} - void SetUpMultiFactorTotpSecretHostApi(id binaryMessenger, NSObject *api) { SetUpMultiFactorTotpSecretHostApiWithSuffix(binaryMessenger, api, @""); @@ -2630,7 +2913,7 @@ void SetUpMultiFactorTotpSecretHostApiWithSuffix(id bina @"MultiFactorTotpSecretHostApi.generateQrCodeUrl", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorTotpSecretHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector( @@ -2639,7 +2922,7 @@ void SetUpMultiFactorTotpSecretHostApiWithSuffix(id bina @"@selector(generateQrCodeUrlSecretKey:accountName:issuer:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; NSString *arg_secretKey = GetNullableObjectAtIndex(args, 0); NSString *arg_accountName = GetNullableObjectAtIndex(args, 1); NSString *arg_issuer = GetNullableObjectAtIndex(args, 2); @@ -2663,14 +2946,14 @@ void SetUpMultiFactorTotpSecretHostApiWithSuffix(id bina @"MultiFactorTotpSecretHostApi.openInOtpApp", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:MultiFactorTotpSecretHostApiGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(openInOtpAppSecretKey:qrCodeUrl:completion:)], @"MultiFactorTotpSecretHostApi api (%@) doesn't respond to " @"@selector(openInOtpAppSecretKey:qrCodeUrl:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; + NSArray *args = message; NSString *arg_secretKey = GetNullableObjectAtIndex(args, 0); NSString *arg_qrCodeUrl = GetNullableObjectAtIndex(args, 1); [api openInOtpAppSecretKey:arg_secretKey @@ -2684,54 +2967,6 @@ void SetUpMultiFactorTotpSecretHostApiWithSuffix(id bina } } } -@interface GenerateInterfacesCodecReader : FlutterStandardReader -@end -@implementation GenerateInterfacesCodecReader -- (nullable id)readValueOfType:(UInt8)type { - switch (type) { - case 128: - return [PigeonMultiFactorInfo fromList:[self readValue]]; - default: - return [super readValueOfType:type]; - } -} -@end - -@interface GenerateInterfacesCodecWriter : FlutterStandardWriter -@end -@implementation GenerateInterfacesCodecWriter -- (void)writeValue:(id)value { - if ([value isKindOfClass:[PigeonMultiFactorInfo class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else { - [super writeValue:value]; - } -} -@end - -@interface GenerateInterfacesCodecReaderWriter : FlutterStandardReaderWriter -@end -@implementation GenerateInterfacesCodecReaderWriter -- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[GenerateInterfacesCodecWriter alloc] initWithData:data]; -} -- (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[GenerateInterfacesCodecReader alloc] initWithData:data]; -} -@end - -NSObject *GenerateInterfacesGetCodec(void) { - static FlutterStandardMessageCodec *sSharedObject = nil; - static dispatch_once_t sPred = 0; - dispatch_once(&sPred, ^{ - GenerateInterfacesCodecReaderWriter *readerWriter = - [[GenerateInterfacesCodecReaderWriter alloc] init]; - sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; - }); - return sSharedObject; -} - void SetUpGenerateInterfaces(id binaryMessenger, NSObject *api) { SetUpGenerateInterfacesWithSuffix(binaryMessenger, api, @""); @@ -2750,15 +2985,15 @@ void SetUpGenerateInterfacesWithSuffix(id binaryMessenge @"interface.GenerateInterfaces.pigeonInterface", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:GenerateInterfacesGetCodec()]; + codec:nullGetFirebaseAuthMessagesCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(pigeonInterfaceInfo:error:)], @"GenerateInterfaces api (%@) doesn't respond to @selector(pigeonInterfaceInfo:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - PigeonMultiFactorInfo *arg_info = GetNullableObjectAtIndex(args, 0); + NSArray *args = message; + InternalMultiFactorInfo *arg_info = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api pigeonInterfaceInfo:arg_info error:&error]; callback(wrapResult(nil, error)); diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/FLTPhoneNumberVerificationStreamHandler.h b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/FLTPhoneNumberVerificationStreamHandler.h index a576f233e99a..53e5f28cea90 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/FLTPhoneNumberVerificationStreamHandler.h +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/FLTPhoneNumberVerificationStreamHandler.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithAuth:(FIRAuth *)auth arguments:(NSDictionary *)arguments; #else - (instancetype)initWithAuth:(FIRAuth *)auth - request:(PigeonVerifyPhoneNumberRequest *)request + request:(InternalVerifyPhoneNumberRequest *)request session:(FIRMultiFactorSession *)session factorInfo:(FIRPhoneMultiFactorInfo *)factorInfo; #endif diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/PigeonParser.h b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/PigeonParser.h index 515a61a895b5..b500fd2c878e 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/PigeonParser.h +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Private/PigeonParser.h @@ -16,18 +16,18 @@ @interface PigeonParser : NSObject -+ (NSArray *_Nonnull)getManualList:(nonnull PigeonUserDetails *)userDetails; -+ (PigeonUserCredential *_Nullable) ++ (NSArray *_Nonnull)getManualList:(nonnull InternalUserDetails *)userDetails; ++ (InternalUserCredential *_Nullable) getPigeonUserCredentialFromAuthResult:(nonnull FIRAuthDataResult *)authResult authorizationCode:(nullable NSString *)authorizationCode; -+ (PigeonUserDetails *_Nullable)getPigeonDetails:(nonnull FIRUser *)user; -+ (PigeonUserInfo *_Nullable)getPigeonUserInfo:(nonnull FIRUser *)user; ++ (InternalUserDetails *_Nullable)getPigeonDetails:(nonnull FIRUser *)user; ++ (InternalUserInfo *_Nullable)getPigeonUserInfo:(nonnull FIRUser *)user; + (FIRActionCodeSettings *_Nullable)parseActionCodeSettings: - (nullable PigeonActionCodeSettings *)settings; -+ (PigeonUserCredential *_Nullable)getPigeonUserCredentialFromFIRUser:(nonnull FIRUser *)user; -+ (PigeonIdTokenResult *_Nonnull)parseIdTokenResult:(nonnull FIRAuthTokenResult *)tokenResult; -+ (PigeonTotpSecret *_Nonnull)getPigeonTotpSecret:(nonnull FIRTOTPSecret *)secret; -+ (PigeonAuthCredential *_Nullable)getPigeonAuthCredential: - (FIRAuthCredential *_Nullable)authCredentialToken - token:(NSNumber *_Nullable)token; + (nullable InternalActionCodeSettings *)settings; ++ (InternalUserCredential *_Nullable)getPigeonUserCredentialFromFIRUser:(nonnull FIRUser *)user; ++ (InternalIdTokenResult *_Nonnull)parseIdTokenResult:(nonnull FIRAuthTokenResult *)tokenResult; ++ (InternalTotpSecret *_Nonnull)getPigeonTotpSecret:(nonnull FIRTOTPSecret *)secret; ++ (InternalAuthCredential *_Nullable)getPigeonAuthCredential: + (FIRAuthCredential *_Nullable)authCredentialToken + token:(NSNumber *_Nullable)token; @end diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/CustomPigeonHeader.h b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/CustomPigeonHeader.h index 17111415acb6..d32a6b451629 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/CustomPigeonHeader.h +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/CustomPigeonHeader.h @@ -3,14 +3,14 @@ // found in the LICENSE file. #import "firebase_auth_messages.g.h" -@interface PigeonMultiFactorInfo (Map) +@interface InternalMultiFactorInfo (Map) - (NSDictionary *)toList; @end -@interface PigeonUserDetails (Map) +@interface InternalUserDetails (Map) - (NSDictionary *)toList; @end -@interface PigeonUserInfo (Map) +@interface InternalUserInfo (Map) - (NSDictionary *)toList; @end diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/FLTFirebaseAuthPlugin.h b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/FLTFirebaseAuthPlugin.h index 552728aab958..53e20eca48b6 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/FLTFirebaseAuthPlugin.h +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/FLTFirebaseAuthPlugin.h @@ -34,8 +34,7 @@ ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding #if !TARGET_OS_OSX -#if __has_include() || \ - defined(FlutterSceneLifeCycleDelegate) +#if __has_include() , FlutterSceneLifeCycleDelegate #endif diff --git a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/firebase_auth_messages.g.h b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/firebase_auth_messages.g.h index fe48f267f2af..f83da7e34a3b 100644 --- a/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/firebase_auth_messages.g.h +++ b/packages/firebase_auth/firebase_auth/ios/firebase_auth/Sources/firebase_auth/include/Public/firebase_auth_messages.g.h @@ -1,10 +1,10 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v19.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import +@import Foundation; @protocol FlutterBinaryMessenger; @protocol FlutterMessageCodec; @@ -38,33 +38,34 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { - (instancetype)initWithValue:(ActionCodeInfoOperation)value; @end -@class PigeonMultiFactorSession; -@class PigeonPhoneMultiFactorAssertion; -@class PigeonMultiFactorInfo; +@class InternalMultiFactorSession; +@class InternalPhoneMultiFactorAssertion; +@class InternalMultiFactorInfo; @class AuthPigeonFirebaseApp; -@class PigeonActionCodeInfoData; -@class PigeonActionCodeInfo; -@class PigeonAdditionalUserInfo; -@class PigeonAuthCredential; -@class PigeonUserInfo; -@class PigeonUserDetails; -@class PigeonUserCredential; -@class PigeonActionCodeSettings; -@class PigeonFirebaseAuthSettings; -@class PigeonSignInProvider; -@class PigeonVerifyPhoneNumberRequest; -@class PigeonIdTokenResult; -@class PigeonUserProfile; -@class PigeonTotpSecret; - -@interface PigeonMultiFactorSession : NSObject +@class InternalActionCodeInfoData; +@class InternalActionCodeInfo; +@class InternalAdditionalUserInfo; +@class InternalAuthCredential; +@class InternalUserInfo; +@class InternalUserDetails; +@class InternalUserCredential; +@class InternalAuthCredentialInput; +@class InternalActionCodeSettings; +@class InternalFirebaseAuthSettings; +@class InternalSignInProvider; +@class InternalVerifyPhoneNumberRequest; +@class InternalIdTokenResult; +@class InternalUserProfile; +@class InternalTotpSecret; + +@interface InternalMultiFactorSession : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithId:(NSString *)id; @property(nonatomic, copy) NSString *id; @end -@interface PigeonPhoneMultiFactorAssertion : NSObject +@interface InternalPhoneMultiFactorAssertion : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithVerificationId:(NSString *)verificationId @@ -73,7 +74,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy) NSString *verificationCode; @end -@interface PigeonMultiFactorInfo : NSObject +@interface InternalMultiFactorInfo : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithDisplayName:(nullable NSString *)displayName @@ -99,23 +100,23 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy, nullable) NSString *customAuthDomain; @end -@interface PigeonActionCodeInfoData : NSObject +@interface InternalActionCodeInfoData : NSObject + (instancetype)makeWithEmail:(nullable NSString *)email previousEmail:(nullable NSString *)previousEmail; @property(nonatomic, copy, nullable) NSString *email; @property(nonatomic, copy, nullable) NSString *previousEmail; @end -@interface PigeonActionCodeInfo : NSObject +@interface InternalActionCodeInfo : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithOperation:(ActionCodeInfoOperation)operation - data:(PigeonActionCodeInfoData *)data; + data:(InternalActionCodeInfoData *)data; @property(nonatomic, assign) ActionCodeInfoOperation operation; -@property(nonatomic, strong) PigeonActionCodeInfoData *data; +@property(nonatomic, strong) InternalActionCodeInfoData *data; @end -@interface PigeonAdditionalUserInfo : NSObject +@interface InternalAdditionalUserInfo : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithIsNewUser:(BOOL)isNewUser @@ -130,7 +131,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy, nullable) NSDictionary *profile; @end -@interface PigeonAuthCredential : NSObject +@interface InternalAuthCredential : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProviderId:(NSString *)providerId @@ -143,7 +144,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy, nullable) NSString *accessToken; @end -@interface PigeonUserInfo : NSObject +@interface InternalUserInfo : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithUid:(NSString *)uid @@ -172,25 +173,38 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, strong, nullable) NSNumber *lastSignInTimestamp; @end -@interface PigeonUserDetails : NSObject +@interface InternalUserDetails : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithUserInfo:(PigeonUserInfo *)userInfo ++ (instancetype)makeWithUserInfo:(InternalUserInfo *)userInfo providerData:(NSArray *> *)providerData; -@property(nonatomic, strong) PigeonUserInfo *userInfo; +@property(nonatomic, strong) InternalUserInfo *userInfo; @property(nonatomic, copy) NSArray *> *providerData; @end -@interface PigeonUserCredential : NSObject -+ (instancetype)makeWithUser:(nullable PigeonUserDetails *)user - additionalUserInfo:(nullable PigeonAdditionalUserInfo *)additionalUserInfo - credential:(nullable PigeonAuthCredential *)credential; -@property(nonatomic, strong, nullable) PigeonUserDetails *user; -@property(nonatomic, strong, nullable) PigeonAdditionalUserInfo *additionalUserInfo; -@property(nonatomic, strong, nullable) PigeonAuthCredential *credential; +@interface InternalUserCredential : NSObject ++ (instancetype)makeWithUser:(nullable InternalUserDetails *)user + additionalUserInfo:(nullable InternalAdditionalUserInfo *)additionalUserInfo + credential:(nullable InternalAuthCredential *)credential; +@property(nonatomic, strong, nullable) InternalUserDetails *user; +@property(nonatomic, strong, nullable) InternalAdditionalUserInfo *additionalUserInfo; +@property(nonatomic, strong, nullable) InternalAuthCredential *credential; @end -@interface PigeonActionCodeSettings : NSObject +@interface InternalAuthCredentialInput : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithProviderId:(NSString *)providerId + signInMethod:(NSString *)signInMethod + token:(nullable NSString *)token + accessToken:(nullable NSString *)accessToken; +@property(nonatomic, copy) NSString *providerId; +@property(nonatomic, copy) NSString *signInMethod; +@property(nonatomic, copy, nullable) NSString *token; +@property(nonatomic, copy, nullable) NSString *accessToken; +@end + +@interface InternalActionCodeSettings : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithUrl:(NSString *)url @@ -211,7 +225,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy, nullable) NSString *linkDomain; @end -@interface PigeonFirebaseAuthSettings : NSObject +@interface InternalFirebaseAuthSettings : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithAppVerificationDisabledForTesting:(BOOL)appVerificationDisabledForTesting @@ -226,7 +240,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, strong, nullable) NSNumber *forceRecaptchaFlow; @end -@interface PigeonSignInProvider : NSObject +@interface InternalSignInProvider : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithProviderId:(NSString *)providerId @@ -238,7 +252,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy, nullable) NSDictionary *customParameters; @end -@interface PigeonVerifyPhoneNumberRequest : NSObject +@interface InternalVerifyPhoneNumberRequest : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithPhoneNumber:(nullable NSString *)phoneNumber @@ -255,7 +269,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy, nullable) NSString *multiFactorSessionId; @end -@interface PigeonIdTokenResult : NSObject +@interface InternalIdTokenResult : NSObject + (instancetype)makeWithToken:(nullable NSString *)token expirationTimestamp:(nullable NSNumber *)expirationTimestamp authTimestamp:(nullable NSNumber *)authTimestamp @@ -272,7 +286,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy, nullable) NSString *signInSecondFactor; @end -@interface PigeonUserProfile : NSObject +@interface InternalUserProfile : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithDisplayName:(nullable NSString *)displayName @@ -285,7 +299,7 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, assign) BOOL photoUrlChanged; @end -@interface PigeonTotpSecret : NSObject +@interface InternalTotpSecret : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithCodeIntervalSeconds:(nullable NSNumber *)codeIntervalSeconds @@ -300,8 +314,8 @@ typedef NS_ENUM(NSUInteger, ActionCodeInfoOperation) { @property(nonatomic, copy) NSString *secretKey; @end -/// The codec used by FirebaseAuthHostApi. -NSObject *FirebaseAuthHostApiGetCodec(void); +/// The codec used by all APIs. +NSObject *nullGetFirebaseAuthMessagesCodec(void); @protocol FirebaseAuthHostApi - (void)registerIdTokenListenerApp:(AuthPigeonFirebaseApp *)app @@ -319,8 +333,8 @@ NSObject *FirebaseAuthHostApiGetCodec(void); completion:(void (^)(FlutterError *_Nullable))completion; - (void)checkActionCodeApp:(AuthPigeonFirebaseApp *)app code:(NSString *)code - completion: - (void (^)(PigeonActionCodeInfo *_Nullable, FlutterError *_Nullable))completion; + completion:(void (^)(InternalActionCodeInfo *_Nullable, + FlutterError *_Nullable))completion; - (void)confirmPasswordResetApp:(AuthPigeonFirebaseApp *)app code:(NSString *)code newPassword:(NSString *)newPassword @@ -328,32 +342,32 @@ NSObject *FirebaseAuthHostApiGetCodec(void); - (void)createUserWithEmailAndPasswordApp:(AuthPigeonFirebaseApp *)app email:(NSString *)email password:(NSString *)password - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)signInAnonymouslyApp:(AuthPigeonFirebaseApp *)app - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)signInWithCredentialApp:(AuthPigeonFirebaseApp *)app input:(NSDictionary *)input - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)signInWithCustomTokenApp:(AuthPigeonFirebaseApp *)app token:(NSString *)token - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)signInWithEmailAndPasswordApp:(AuthPigeonFirebaseApp *)app email:(NSString *)email password:(NSString *)password - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)signInWithEmailLinkApp:(AuthPigeonFirebaseApp *)app email:(NSString *)email emailLink:(NSString *)emailLink - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)signInWithProviderApp:(AuthPigeonFirebaseApp *)app - signInProvider:(PigeonSignInProvider *)signInProvider - completion:(void (^)(PigeonUserCredential *_Nullable, + signInProvider:(InternalSignInProvider *)signInProvider + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)signOutApp:(AuthPigeonFirebaseApp *)app completion:(void (^)(FlutterError *_Nullable))completion; @@ -363,28 +377,31 @@ NSObject *FirebaseAuthHostApiGetCodec(void); FlutterError *_Nullable))completion; - (void)sendPasswordResetEmailApp:(AuthPigeonFirebaseApp *)app email:(NSString *)email - actionCodeSettings:(nullable PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings completion:(void (^)(FlutterError *_Nullable))completion; - (void)sendSignInLinkToEmailApp:(AuthPigeonFirebaseApp *)app email:(NSString *)email - actionCodeSettings:(PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(InternalActionCodeSettings *)actionCodeSettings completion:(void (^)(FlutterError *_Nullable))completion; - (void)setLanguageCodeApp:(AuthPigeonFirebaseApp *)app languageCode:(nullable NSString *)languageCode completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)setSettingsApp:(AuthPigeonFirebaseApp *)app - settings:(PigeonFirebaseAuthSettings *)settings + settings:(InternalFirebaseAuthSettings *)settings completion:(void (^)(FlutterError *_Nullable))completion; - (void)verifyPasswordResetCodeApp:(AuthPigeonFirebaseApp *)app code:(NSString *)code completion: (void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)verifyPhoneNumberApp:(AuthPigeonFirebaseApp *)app - request:(PigeonVerifyPhoneNumberRequest *)request + request:(InternalVerifyPhoneNumberRequest *)request completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)revokeTokenWithAuthorizationCodeApp:(AuthPigeonFirebaseApp *)app authorizationCode:(NSString *)authorizationCode completion:(void (^)(FlutterError *_Nullable))completion; +- (void)revokeAccessTokenApp:(AuthPigeonFirebaseApp *)app + accessToken:(NSString *)accessToken + completion:(void (^)(FlutterError *_Nullable))completion; - (void)initializeRecaptchaConfigApp:(AuthPigeonFirebaseApp *)app completion:(void (^)(FlutterError *_Nullable))completion; @end @@ -396,57 +413,56 @@ extern void SetUpFirebaseAuthHostApiWithSuffix(id binary NSObject *_Nullable api, NSString *messageChannelSuffix); -/// The codec used by FirebaseAuthUserHostApi. -NSObject *FirebaseAuthUserHostApiGetCodec(void); - @protocol FirebaseAuthUserHostApi - (void)deleteApp:(AuthPigeonFirebaseApp *)app completion:(void (^)(FlutterError *_Nullable))completion; - (void)getIdTokenApp:(AuthPigeonFirebaseApp *)app forceRefresh:(BOOL)forceRefresh - completion:(void (^)(PigeonIdTokenResult *_Nullable, FlutterError *_Nullable))completion; + completion: + (void (^)(InternalIdTokenResult *_Nullable, FlutterError *_Nullable))completion; - (void)linkWithCredentialApp:(AuthPigeonFirebaseApp *)app input:(NSDictionary *)input - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)linkWithProviderApp:(AuthPigeonFirebaseApp *)app - signInProvider:(PigeonSignInProvider *)signInProvider - completion: - (void (^)(PigeonUserCredential *_Nullable, FlutterError *_Nullable))completion; + signInProvider:(InternalSignInProvider *)signInProvider + completion:(void (^)(InternalUserCredential *_Nullable, + FlutterError *_Nullable))completion; - (void)reauthenticateWithCredentialApp:(AuthPigeonFirebaseApp *)app input:(NSDictionary *)input - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)reauthenticateWithProviderApp:(AuthPigeonFirebaseApp *)app - signInProvider:(PigeonSignInProvider *)signInProvider - completion:(void (^)(PigeonUserCredential *_Nullable, + signInProvider:(InternalSignInProvider *)signInProvider + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)reloadApp:(AuthPigeonFirebaseApp *)app - completion:(void (^)(PigeonUserDetails *_Nullable, FlutterError *_Nullable))completion; + completion:(void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; - (void)sendEmailVerificationApp:(AuthPigeonFirebaseApp *)app - actionCodeSettings:(nullable PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings completion:(void (^)(FlutterError *_Nullable))completion; - (void)unlinkApp:(AuthPigeonFirebaseApp *)app providerId:(NSString *)providerId - completion:(void (^)(PigeonUserCredential *_Nullable, FlutterError *_Nullable))completion; + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; - (void)updateEmailApp:(AuthPigeonFirebaseApp *)app newEmail:(NSString *)newEmail - completion:(void (^)(PigeonUserDetails *_Nullable, FlutterError *_Nullable))completion; + completion: + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; - (void)updatePasswordApp:(AuthPigeonFirebaseApp *)app newPassword:(NSString *)newPassword completion: - (void (^)(PigeonUserDetails *_Nullable, FlutterError *_Nullable))completion; + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; - (void)updatePhoneNumberApp:(AuthPigeonFirebaseApp *)app input:(NSDictionary *)input completion: - (void (^)(PigeonUserDetails *_Nullable, FlutterError *_Nullable))completion; + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; - (void)updateProfileApp:(AuthPigeonFirebaseApp *)app - profile:(PigeonUserProfile *)profile + profile:(InternalUserProfile *)profile completion: - (void (^)(PigeonUserDetails *_Nullable, FlutterError *_Nullable))completion; + (void (^)(InternalUserDetails *_Nullable, FlutterError *_Nullable))completion; - (void)verifyBeforeUpdateEmailApp:(AuthPigeonFirebaseApp *)app newEmail:(NSString *)newEmail - actionCodeSettings:(nullable PigeonActionCodeSettings *)actionCodeSettings + actionCodeSettings:(nullable InternalActionCodeSettings *)actionCodeSettings completion:(void (^)(FlutterError *_Nullable))completion; @end @@ -457,12 +473,9 @@ extern void SetUpFirebaseAuthUserHostApiWithSuffix(id bi NSObject *_Nullable api, NSString *messageChannelSuffix); -/// The codec used by MultiFactorUserHostApi. -NSObject *MultiFactorUserHostApiGetCodec(void); - @protocol MultiFactorUserHostApi - (void)enrollPhoneApp:(AuthPigeonFirebaseApp *)app - assertion:(PigeonPhoneMultiFactorAssertion *)assertion + assertion:(InternalPhoneMultiFactorAssertion *)assertion displayName:(nullable NSString *)displayName completion:(void (^)(FlutterError *_Nullable))completion; - (void)enrollTotpApp:(AuthPigeonFirebaseApp *)app @@ -471,12 +484,12 @@ NSObject *MultiFactorUserHostApiGetCodec(void); completion:(void (^)(FlutterError *_Nullable))completion; - (void)getSessionApp:(AuthPigeonFirebaseApp *)app completion: - (void (^)(PigeonMultiFactorSession *_Nullable, FlutterError *_Nullable))completion; + (void (^)(InternalMultiFactorSession *_Nullable, FlutterError *_Nullable))completion; - (void)unenrollApp:(AuthPigeonFirebaseApp *)app factorUid:(NSString *)factorUid completion:(void (^)(FlutterError *_Nullable))completion; - (void)getEnrolledFactorsApp:(AuthPigeonFirebaseApp *)app - completion:(void (^)(NSArray *_Nullable, + completion:(void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion; @end @@ -487,14 +500,11 @@ extern void SetUpMultiFactorUserHostApiWithSuffix(id bin NSObject *_Nullable api, NSString *messageChannelSuffix); -/// The codec used by MultiFactoResolverHostApi. -NSObject *MultiFactoResolverHostApiGetCodec(void); - @protocol MultiFactoResolverHostApi - (void)resolveSignInResolverId:(NSString *)resolverId - assertion:(nullable PigeonPhoneMultiFactorAssertion *)assertion + assertion:(nullable InternalPhoneMultiFactorAssertion *)assertion totpAssertionId:(nullable NSString *)totpAssertionId - completion:(void (^)(PigeonUserCredential *_Nullable, + completion:(void (^)(InternalUserCredential *_Nullable, FlutterError *_Nullable))completion; @end @@ -505,13 +515,10 @@ extern void SetUpMultiFactoResolverHostApiWithSuffix( id binaryMessenger, NSObject *_Nullable api, NSString *messageChannelSuffix); -/// The codec used by MultiFactorTotpHostApi. -NSObject *MultiFactorTotpHostApiGetCodec(void); - @protocol MultiFactorTotpHostApi - (void)generateSecretSessionId:(NSString *)sessionId - completion: - (void (^)(PigeonTotpSecret *_Nullable, FlutterError *_Nullable))completion; + completion:(void (^)(InternalTotpSecret *_Nullable, + FlutterError *_Nullable))completion; - (void)getAssertionForEnrollmentSecretKey:(NSString *)secretKey oneTimePassword:(NSString *)oneTimePassword completion:(void (^)(NSString *_Nullable, @@ -529,9 +536,6 @@ extern void SetUpMultiFactorTotpHostApiWithSuffix(id bin NSObject *_Nullable api, NSString *messageChannelSuffix); -/// The codec used by MultiFactorTotpSecretHostApi. -NSObject *MultiFactorTotpSecretHostApiGetCodec(void); - @protocol MultiFactorTotpSecretHostApi - (void)generateQrCodeUrlSecretKey:(NSString *)secretKey accountName:(nullable NSString *)accountName @@ -551,12 +555,9 @@ extern void SetUpMultiFactorTotpSecretHostApiWithSuffix( id binaryMessenger, NSObject *_Nullable api, NSString *messageChannelSuffix); -/// The codec used by GenerateInterfaces. -NSObject *GenerateInterfacesGetCodec(void); - /// Only used to generate the object interface that are use outside of the Pigeon interface @protocol GenerateInterfaces -- (void)pigeonInterfaceInfo:(PigeonMultiFactorInfo *)info +- (void)pigeonInterfaceInfo:(InternalMultiFactorInfo *)info error:(FlutterError *_Nullable *_Nonnull)error; @end diff --git a/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart b/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart index 027892a8c8d9..3c07d4b4707a 100644 --- a/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart +++ b/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart @@ -6,7 +6,7 @@ part of '../firebase_auth.dart'; /// The entry point of the Firebase Authentication SDK. -class FirebaseAuth extends FirebasePluginPlatform { +class FirebaseAuth extends FirebasePlugin implements FirebaseService { // Cached instances of [FirebaseAuth]. static Map _firebaseAuthInstances = {}; @@ -45,10 +45,22 @@ class FirebaseAuth extends FirebasePluginPlatform { required FirebaseApp app, }) { return _firebaseAuthInstances.putIfAbsent(app.name, () { - return FirebaseAuth._(app: app); + final instance = FirebaseAuth._(app: app); + app.registerService( + instance, + dispose: (auth) => auth._dispose(), + ); + return instance; }); } + Future _dispose() async { + _firebaseAuthInstances.remove(app.name); + final delegate = _delegatePackingProperty; + _delegatePackingProperty = null; + await delegate?.dispose(); + } + /// Returns the current [User] if they are currently signed-in, or `null` if /// not. /// @@ -285,6 +297,10 @@ class FirebaseAuth extends FirebasePluginPlatform { /// To complete the password reset, call [confirmPasswordReset] with the code supplied /// in the email sent to the user, along with the new password specified by the user. /// + /// If email enumeration protection is enabled for the Firebase project, this + /// method may complete successfully even when the email does not correspond + /// to an existing user. + /// /// May throw a [FirebaseAuthException] with the following error codes: /// /// - **auth/invalid-email**\ @@ -300,7 +316,8 @@ class FirebaseAuth extends FirebasePluginPlatform { /// - **auth/unauthorized-continue-uri**\ /// The domain of the continue URL is not whitelisted. Whitelist the domain in the Firebase console. /// - **auth/user-not-found**\ - /// Thrown if there is no user corresponding to the email address. Note: This exception is no longer thrown when enabling email enumeration protection. + /// Thrown if there is no user corresponding to the email address. Note: This + /// exception is not thrown when email enumeration protection is enabled. Future sendPasswordResetEmail({ required String email, ActionCodeSettings? actionCodeSettings, @@ -798,6 +815,11 @@ class FirebaseAuth extends FirebasePluginPlatform { return _delegate.revokeTokenWithAuthorizationCode(authorizationCode); } + /// Android only. Revokes the provided accessToken. Currently supports revoking Apple-issued accessToken only. + Future revokeAccessToken(String accessToken) { + return _delegate.revokeAccessToken(accessToken); + } + /// Signs out the current user. /// /// If successful, it also updates diff --git a/packages/firebase_auth/firebase_auth/macos/firebase_auth/Package.swift b/packages/firebase_auth/firebase_auth/macos/firebase_auth/Package.swift index 96473233e110..d6451507d751 100644 --- a/packages/firebase_auth/firebase_auth/macos/firebase_auth/Package.swift +++ b/packages/firebase_auth/firebase_auth/macos/firebase_auth/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "6.2.0" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "6.5.4" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_auth", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-auth", targets: ["firebase_auth"]), + .library(name: "firebase-auth", targets: ["firebase_auth"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,14 +30,14 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ - .headerSearchPath("include/firebase_auth/Private"), - .headerSearchPath("include/firebase_auth/Public"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .headerSearchPath("include/Private"), + .headerSearchPath("include/Public"), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-auth\""), ] - ), + ) ] ) diff --git a/packages/firebase_auth/firebase_auth/pubspec.yaml b/packages/firebase_auth/firebase_auth/pubspec.yaml index e86bbd965275..ed5194bda425 100755 --- a/packages/firebase_auth/firebase_auth/pubspec.yaml +++ b/packages/firebase_auth/firebase_auth/pubspec.yaml @@ -4,7 +4,8 @@ description: Flutter plugin for Firebase Auth, enabling like Google, Facebook and Twitter. homepage: https://firebase.google.com/docs/auth repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_auth/firebase_auth -version: 6.3.0 +version: 6.5.4 +resolution: workspace topics: - firebase - authentication @@ -16,14 +17,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.16.0' dependencies: - firebase_auth_platform_interface: ^8.1.8 - firebase_auth_web: ^6.1.4 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_auth_platform_interface: ^9.0.3 + firebase_auth_web: ^6.2.3 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter meta: ^1.8.0 diff --git a/packages/firebase_auth/firebase_auth/test/firebase_auth_test.dart b/packages/firebase_auth/firebase_auth/test/firebase_auth_test.dart index d3a7bc6d837f..5e570b0b5b82 100644 --- a/packages/firebase_auth/firebase_auth/test/firebase_auth_test.dart +++ b/packages/firebase_auth/firebase_auth/test/firebase_auth_test.dart @@ -35,6 +35,7 @@ void main() { const String kMockSmsCode = '123456'; const String kMockLanguage = 'en'; const String kMockOobCode = 'oobcode'; + const String kMockAuthToken = '12460'; const String kMockURL = 'http://www.example.com'; const String kMockHost = 'www.example.com'; const String kMockValidPassword = @@ -67,8 +68,8 @@ void main() { final int kMockLastSignInTimestamp = DateTime.now().subtract(const Duration(days: 1)).millisecondsSinceEpoch; - final kMockUser = PigeonUserDetails( - userInfo: PigeonUserInfo( + final kMockUser = InternalUserDetails( + userInfo: InternalUserInfo( uid: '12345', displayName: 'displayName', creationTimestamp: kMockCreationTimestamp, @@ -97,7 +98,7 @@ void main() { MockFirebaseAuth mockAuthPlatform = MockFirebaseAuth(); group('$FirebaseAuth', () { - PigeonUserDetails user; + InternalUserDetails user; // used to generate a unique application name for each test var testCount = 0; @@ -230,6 +231,38 @@ void main() { }); }); + test('creates a fresh instance after app delete and reinitialize', + () async { + final appName = 'delete-reinit-$testCount'; + const options = FirebaseOptions( + apiKey: 'apiKey', + appId: 'appId', + messagingSenderId: 'messagingSenderId', + projectId: 'projectId', + ); + final app = await Firebase.initializeApp( + name: appName, + options: options, + ); + final auth1 = FirebaseAuth.instanceFor(app: app); + + expect(app.getService(), same(auth1)); + + await app.delete(); + + final app2 = await Firebase.initializeApp( + name: appName, + options: options, + ); + addTearDown(app2.delete); + + final auth2 = FirebaseAuth.instanceFor(app: app2); + + expect(auth2, isNot(same(auth1))); + expect(auth2.app, app2); + expect(app2.getService(), same(auth2)); + }); + group('tenantId', () { test('set tenantId should call delegate method', () async { // Each test uses a unique FirebaseApp instance to avoid sharing state @@ -778,6 +811,17 @@ void main() { }); }); + group('revokeAccessToken()', () { + test('should call delegate method', () async { + // Necessary as we otherwise get a "null is not a Future" error + when(mockAuthPlatform.revokeAccessToken(kMockAuthToken)) + .thenAnswer((i) async {}); + + await auth.revokeAccessToken(kMockAuthToken); + verify(mockAuthPlatform.revokeAccessToken(kMockAuthToken)); + }); + }); + group('passwordPolicy', () { test('passwordPolicy should be initialized with correct parameters', () async { @@ -983,7 +1027,7 @@ class MockFirebaseAuth extends Mock @override FirebaseAuthPlatform setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { return super.noSuchMethod( @@ -1150,6 +1194,15 @@ class MockFirebaseAuth extends Mock returnValueForMissingStub: neverEndingFuture(), ); } + + @override + Future revokeAccessToken(String accessToken) { + return super.noSuchMethod( + Invocation.method(#revokeAccessToken, [accessToken]), + returnValue: neverEndingFuture(), + returnValueForMissingStub: neverEndingFuture(), + ); + } } class FakeFirebaseAuthPlatform extends Fake @@ -1171,7 +1224,7 @@ class FakeFirebaseAuthPlatform extends Fake @override FirebaseAuthPlatform setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { return this; @@ -1182,7 +1235,7 @@ class MockUserPlatform extends Mock with MockPlatformInterfaceMixin implements TestUserPlatform { MockUserPlatform(FirebaseAuthPlatform auth, MultiFactorPlatform multiFactor, - PigeonUserDetails _user) { + InternalUserDetails _user) { TestUserPlatform(auth, multiFactor, _user); } } @@ -1233,7 +1286,7 @@ class TestFirebaseAuthPlatform extends FirebaseAuthPlatform { @override FirebaseAuthPlatform setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { return this; @@ -1302,7 +1355,7 @@ class TestAuthProvider extends AuthProvider { class TestUserPlatform extends UserPlatform { TestUserPlatform(FirebaseAuthPlatform auth, MultiFactorPlatform multiFactor, - PigeonUserDetails data) + InternalUserDetails data) : super(auth, multiFactor, data); } diff --git a/packages/firebase_auth/firebase_auth/test/mock.dart b/packages/firebase_auth/firebase_auth/test/mock.dart index 028d96c38558..6dc416fb78c5 100644 --- a/packages/firebase_auth/firebase_auth/test/mock.dart +++ b/packages/firebase_auth/firebase_auth/test/mock.dart @@ -12,6 +12,7 @@ void setupFirebaseAuthMocks([Callback? customHandlers]) { TestWidgetsFlutterBinding.ensureInitialized(); setupFirebaseCoreMocks(); + TestFirebaseAppHostApi.setUp(MockFirebaseAppHostApi()); } Future neverEndingFuture() async { @@ -20,3 +21,20 @@ Future neverEndingFuture() async { await Future.delayed(const Duration(minutes: 5)); } } + +class MockFirebaseAppHostApi implements TestFirebaseAppHostApi { + @override + Future delete(String appName) async {} + + @override + Future setAutomaticDataCollectionEnabled( + String appName, + bool enabled, + ) async {} + + @override + Future setAutomaticResourceManagementEnabled( + String appName, + bool enabled, + ) async {} +} diff --git a/packages/firebase_auth/firebase_auth/test/user_test.dart b/packages/firebase_auth/firebase_auth/test/user_test.dart index 0b2abfb19ce9..967be866fee2 100644 --- a/packages/firebase_auth/firebase_auth/test/user_test.dart +++ b/packages/firebase_auth/firebase_auth/test/user_test.dart @@ -31,7 +31,7 @@ void main() { late FirebaseAuth auth; - final kMockIdTokenResult = PigeonIdTokenResult( + final kMockIdTokenResult = InternalIdTokenResult( token: '12345', expirationTimestamp: 123456, authTimestamp: 1234567, @@ -47,8 +47,8 @@ void main() { final int kMockLastSignInTimestamp = DateTime.now().subtract(const Duration(days: 1)).millisecondsSinceEpoch; - final kMockUser = PigeonUserDetails( - userInfo: PigeonUserInfo( + final kMockUser = InternalUserDetails( + userInfo: InternalUserInfo( uid: '12345', displayName: 'displayName', creationTimestamp: kMockCreationTimestamp, @@ -85,7 +85,7 @@ void main() { var mockAuthPlatform = MockFirebaseAuth(); group('$User', () { - late PigeonUserDetails user; + late InternalUserDetails user; // used to generate a unique application name for each test var testCount = 0; @@ -376,7 +376,7 @@ class MockFirebaseAuth extends Mock @override FirebaseAuthPlatform setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { return super.noSuchMethod( @@ -393,7 +393,7 @@ class MockFirebaseAuth extends Mock class MockUserPlatform extends Mock with MockPlatformInterfaceMixin implements TestUserPlatform { - MockUserPlatform(FirebaseAuthPlatform auth, PigeonUserDetails _user) { + MockUserPlatform(FirebaseAuthPlatform auth, InternalUserDetails _user) { TestUserPlatform(auth, TestMultiFactorPlatform(auth), _user); } @@ -544,7 +544,7 @@ class TestFirebaseAuthPlatform extends FirebaseAuthPlatform { @override FirebaseAuthPlatform setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { return this; @@ -553,7 +553,7 @@ class TestFirebaseAuthPlatform extends FirebaseAuthPlatform { class TestUserPlatform extends UserPlatform { TestUserPlatform(FirebaseAuthPlatform auth, MultiFactorPlatform multiFactor, - PigeonUserDetails data) + InternalUserDetails data) : super(auth, multiFactor, data); } diff --git a/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.cpp b/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.cpp index 5a4f1bf27faf..a931dac2b36b 100644 --- a/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.cpp +++ b/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.cpp @@ -79,9 +79,9 @@ Auth* GetAuthFromPigeon(const AuthPigeonFirebaseApp& pigeonApp) { return auth; } -PigeonUserCredential ParseAuthResult( +InternalUserCredential ParseAuthResult( const firebase::auth::AuthResult* authResult) { - PigeonUserCredential result = PigeonUserCredential(); + InternalUserCredential result = InternalUserCredential(); result.set_user(FirebaseAuthPlugin::ParseUserDetails(authResult->user)); result.set_additional_user_info(FirebaseAuthPlugin::ParseAdditionalUserInfo( authResult->additional_user_info)); @@ -133,29 +133,29 @@ firebase_auth_windows::FirebaseAuthPlugin::ConvertToEncodableValue( } } -PigeonAdditionalUserInfo FirebaseAuthPlugin::ParseAdditionalUserInfo( +InternalAdditionalUserInfo FirebaseAuthPlugin::ParseAdditionalUserInfo( const firebase::auth::AdditionalUserInfo additionalUserInfo) { // Cannot know if the user is new or not with current API - PigeonAdditionalUserInfo result = PigeonAdditionalUserInfo(false); + InternalAdditionalUserInfo result = InternalAdditionalUserInfo(false); result.set_profile(ConvertToEncodableMap(additionalUserInfo.profile)); result.set_provider_id(additionalUserInfo.provider_id); result.set_username(additionalUserInfo.user_name); return result; } -PigeonUserDetails FirebaseAuthPlugin::ParseUserDetails( +InternalUserDetails FirebaseAuthPlugin::ParseUserDetails( const firebase::auth::User user) { - PigeonUserDetails result = - PigeonUserDetails(FirebaseAuthPlugin::ParseUserInfo(&user), - FirebaseAuthPlugin::ParseProviderData(&user)); + InternalUserDetails result = + InternalUserDetails(FirebaseAuthPlugin::ParseUserInfo(&user), + FirebaseAuthPlugin::ParseProviderData(&user)); return result; } -PigeonUserInfo FirebaseAuthPlugin::ParseUserInfo( +InternalUserInfo FirebaseAuthPlugin::ParseUserInfo( const firebase::auth::User* user) { - PigeonUserInfo result = PigeonUserInfo(user->uid(), user->is_anonymous(), - user->is_email_verified()); + InternalUserInfo result = InternalUserInfo(user->uid(), user->is_anonymous(), + user->is_email_verified()); result.set_display_name(user->display_name()); result.set_email(user->email()); result.set_phone_number(user->phone_number()); @@ -322,7 +322,8 @@ class FlutterIdTokenListener : public firebase::auth::IdTokenListener { void OnIdTokenChanged(Auth* auth) override { // Generate your ID Token firebase::auth::User user = auth->current_user(); - PigeonUserDetails userDetails = FirebaseAuthPlugin::ParseUserDetails(user); + InternalUserDetails userDetails = + FirebaseAuthPlugin::ParseUserDetails(user); using flutter::EncodableList; using flutter::EncodableMap; @@ -406,7 +407,8 @@ class FlutterAuthStateListener : public firebase::auth::AuthStateListener { void OnAuthStateChanged(Auth* auth) override { // Generate your ID Token firebase::auth::User user = auth->current_user(); - PigeonUserDetails userDetails = FirebaseAuthPlugin::ParseUserDetails(user); + InternalUserDetails userDetails = + FirebaseAuthPlugin::ParseUserDetails(user); using flutter::EncodableList; using flutter::EncodableMap; @@ -503,7 +505,7 @@ void FirebaseAuthPlugin::ApplyActionCode( void FirebaseAuthPlugin::CheckActionCode( const AuthPigeonFirebaseApp& app, const std::string& code, - std::function reply)> result) { + std::function reply)> result) { result(FlutterError("unimplemented", "CheckActionCode is not available on this platform yet.", nullptr)); @@ -521,7 +523,7 @@ void FirebaseAuthPlugin::ConfirmPasswordReset( void FirebaseAuthPlugin::CreateUserWithEmailAndPassword( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& password, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::Future createUserFuture = @@ -533,7 +535,7 @@ void FirebaseAuthPlugin::CreateUserWithEmailAndPassword( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -544,7 +546,7 @@ void FirebaseAuthPlugin::CreateUserWithEmailAndPassword( void FirebaseAuthPlugin::SignInAnonymously( const AuthPigeonFirebaseApp& app, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::Future signInFuture = @@ -555,7 +557,7 @@ void FirebaseAuthPlugin::SignInAnonymously( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -689,7 +691,7 @@ firebase::auth::Credential getCredentialFromArguments( void FirebaseAuthPlugin::SignInWithCredential( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::Future signInFuture = @@ -700,10 +702,11 @@ void FirebaseAuthPlugin::SignInWithCredential( [result](const firebase::Future& completed_future) { if (completed_future.error() == 0) { // TODO: not the right return type from C++ SDK - PigeonUserInfo credential = ParseUserInfo(completed_future.result()); - PigeonUserCredential userCredential = PigeonUserCredential(); - PigeonUserDetails user = - PigeonUserDetails(credential, flutter::EncodableList()); + InternalUserInfo credential = + ParseUserInfo(completed_future.result()); + InternalUserCredential userCredential = InternalUserCredential(); + InternalUserDetails user = + InternalUserDetails(credential, flutter::EncodableList()); userCredential.set_user(user); result(userCredential); } else { @@ -714,7 +717,7 @@ void FirebaseAuthPlugin::SignInWithCredential( void FirebaseAuthPlugin::SignInWithCustomToken( const AuthPigeonFirebaseApp& app, const std::string& token, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::Future signInFuture = @@ -725,7 +728,7 @@ void FirebaseAuthPlugin::SignInWithCustomToken( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -737,7 +740,7 @@ void FirebaseAuthPlugin::SignInWithCustomToken( void FirebaseAuthPlugin::SignInWithEmailAndPassword( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& password, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::Future signInFuture = @@ -748,7 +751,7 @@ void FirebaseAuthPlugin::SignInWithEmailAndPassword( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -760,7 +763,7 @@ void FirebaseAuthPlugin::SignInWithEmailAndPassword( void FirebaseAuthPlugin::SignInWithEmailLink( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& email_link, - std::function reply)> result) { + std::function reply)> result) { result(FlutterError( "unimplemented", "SignInWithEmailLink is not available on this platform yet.", nullptr)); @@ -795,7 +798,7 @@ std::map TransformEncodableMap( } firebase::auth::FederatedOAuthProvider getProviderFromArguments( - const PigeonSignInProvider& sign_in_provider) { + const InternalSignInProvider& sign_in_provider) { firebase::auth::FederatedOAuthProviderData federatedOAuthProviderData = firebase::auth::FederatedOAuthProviderData( sign_in_provider.provider_id().c_str(), @@ -809,8 +812,8 @@ firebase::auth::FederatedOAuthProvider getProviderFromArguments( void FirebaseAuthPlugin::SignInWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) { + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::Future signInFuture = @@ -822,7 +825,7 @@ void FirebaseAuthPlugin::SignInWithProvider( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -875,7 +878,7 @@ void FirebaseAuthPlugin::FetchSignInMethodsForEmail( void FirebaseAuthPlugin::SendPasswordResetEmail( const AuthPigeonFirebaseApp& app, const std::string& email, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); @@ -895,7 +898,7 @@ void FirebaseAuthPlugin::SendPasswordResetEmail( void FirebaseAuthPlugin::SendSignInLinkToEmail( const AuthPigeonFirebaseApp& app, const std::string& email, - const PigeonActionCodeSettings& action_code_settings, + const InternalActionCodeSettings& action_code_settings, std::function reply)> result) { result(FlutterError( "unimplemented", @@ -920,7 +923,7 @@ void FirebaseAuthPlugin::SetLanguageCode( void FirebaseAuthPlugin::SetSettings( const AuthPigeonFirebaseApp& app, - const PigeonFirebaseAuthSettings& settings, + const InternalFirebaseAuthSettings& settings, std::function reply)> result) { result(FlutterError("unimplemented", "SetSettings is not available on this platform yet.", @@ -938,7 +941,7 @@ void FirebaseAuthPlugin::VerifyPasswordResetCode( void FirebaseAuthPlugin::VerifyPhoneNumber( const AuthPigeonFirebaseApp& app, - const PigeonVerifyPhoneNumberRequest& request, + const InternalVerifyPhoneNumberRequest& request, std::function reply)> result) { result(FlutterError( "unimplemented", @@ -965,7 +968,7 @@ void FirebaseAuthPlugin::Delete( void FirebaseAuthPlugin::GetIdToken( const AuthPigeonFirebaseApp& app, bool force_refresh, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -975,7 +978,7 @@ void FirebaseAuthPlugin::GetIdToken( [result](const firebase::Future& completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonIdTokenResult token_result; + InternalIdTokenResult token_result; std::string_view sv(*completed_future.result()); token_result.set_token(sv); result(token_result); @@ -987,7 +990,7 @@ void FirebaseAuthPlugin::GetIdToken( void FirebaseAuthPlugin::LinkWithCredential( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -999,7 +1002,7 @@ void FirebaseAuthPlugin::LinkWithCredential( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -1010,8 +1013,8 @@ void FirebaseAuthPlugin::LinkWithCredential( void FirebaseAuthPlugin::LinkWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) { + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1023,7 +1026,7 @@ void FirebaseAuthPlugin::LinkWithProvider( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -1034,7 +1037,7 @@ void FirebaseAuthPlugin::LinkWithProvider( void FirebaseAuthPlugin::ReauthenticateWithCredential( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1053,8 +1056,8 @@ void FirebaseAuthPlugin::ReauthenticateWithCredential( void FirebaseAuthPlugin::ReauthenticateWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) { + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1067,7 +1070,7 @@ void FirebaseAuthPlugin::ReauthenticateWithProvider( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -1078,7 +1081,7 @@ void FirebaseAuthPlugin::ReauthenticateWithProvider( void FirebaseAuthPlugin::Reload( const AuthPigeonFirebaseApp& app, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1088,7 +1091,7 @@ void FirebaseAuthPlugin::Reload( const firebase::Future& completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserDetails user = ParseUserDetails(firebaseAuth->current_user()); + InternalUserDetails user = ParseUserDetails(firebaseAuth->current_user()); result(user); } else { result(FirebaseAuthPlugin::ParseError(completed_future)); @@ -1098,7 +1101,7 @@ void FirebaseAuthPlugin::Reload( void FirebaseAuthPlugin::SendEmailVerification( const AuthPigeonFirebaseApp& app, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1117,7 +1120,7 @@ void FirebaseAuthPlugin::SendEmailVerification( void FirebaseAuthPlugin::Unlink( const AuthPigeonFirebaseApp& app, const std::string& provider_id, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1129,7 +1132,7 @@ void FirebaseAuthPlugin::Unlink( completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserCredential credential = + InternalUserCredential credential = ParseAuthResult(completed_future.result()); result(credential); } else { @@ -1140,7 +1143,7 @@ void FirebaseAuthPlugin::Unlink( void FirebaseAuthPlugin::UpdateEmail( const AuthPigeonFirebaseApp& app, const std::string& new_email, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1151,7 +1154,7 @@ void FirebaseAuthPlugin::UpdateEmail( const firebase::Future& completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserDetails user = ParseUserDetails(firebaseAuth->current_user()); + InternalUserDetails user = ParseUserDetails(firebaseAuth->current_user()); result(user); } else { result(FirebaseAuthPlugin::ParseError(completed_future)); @@ -1161,7 +1164,7 @@ void FirebaseAuthPlugin::UpdateEmail( void FirebaseAuthPlugin::UpdatePassword( const AuthPigeonFirebaseApp& app, const std::string& new_password, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1171,7 +1174,7 @@ void FirebaseAuthPlugin::UpdatePassword( const firebase::Future& completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserDetails user = ParseUserDetails(firebaseAuth->current_user()); + InternalUserDetails user = ParseUserDetails(firebaseAuth->current_user()); result(user); } else { result(FirebaseAuthPlugin::ParseError(completed_future)); @@ -1202,7 +1205,7 @@ firebase::auth::PhoneAuthCredential getPhoneCredentialFromArguments( void FirebaseAuthPlugin::UpdatePhoneNumber( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) { + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1214,7 +1217,8 @@ void FirebaseAuthPlugin::UpdatePhoneNumber( [result](const firebase::Future& completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserDetails user = ParseUserDetails(*completed_future.result()); + InternalUserDetails user = + ParseUserDetails(*completed_future.result()); result(user); } else { result(FirebaseAuthPlugin::ParseError(completed_future)); @@ -1223,8 +1227,8 @@ void FirebaseAuthPlugin::UpdatePhoneNumber( } void FirebaseAuthPlugin::UpdateProfile( - const AuthPigeonFirebaseApp& app, const PigeonUserProfile& profile, - std::function reply)> result) { + const AuthPigeonFirebaseApp& app, const InternalUserProfile& profile, + std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1243,7 +1247,7 @@ void FirebaseAuthPlugin::UpdateProfile( const firebase::Future& completed_future) { // We are probably in a different thread right now. if (completed_future.error() == 0) { - PigeonUserDetails user = ParseUserDetails(firebaseAuth->current_user()); + InternalUserDetails user = ParseUserDetails(firebaseAuth->current_user()); result(user); } else { result(FirebaseAuthPlugin::ParseError(completed_future)); @@ -1253,7 +1257,7 @@ void FirebaseAuthPlugin::UpdateProfile( void FirebaseAuthPlugin::VerifyBeforeUpdateEmail( const AuthPigeonFirebaseApp& app, const std::string& new_email, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) { firebase::auth::Auth* firebaseAuth = GetAuthFromPigeon(app); firebase::auth::User user = firebaseAuth->current_user(); @@ -1286,6 +1290,23 @@ void FirebaseAuthPlugin::RevokeTokenWithAuthorizationCode( nullptr)); } +void FirebaseAuthPlugin::RevokeAccessToken( + const AuthPigeonFirebaseApp& app, const std::string& access_token, + std::function reply)> result) { + result(FlutterError("unimplemented", + "RevokeAccessToken is not available on this platform.", + nullptr)); +} + +void FirebaseAuthPlugin::InitializeRecaptchaConfig( + const AuthPigeonFirebaseApp& app, + std::function reply)> result) { + result(FlutterError( + "unimplemented", + "InitializeRecaptchaConfig is not available on this platform yet.", + nullptr)); +} + flutter::EncodableMap FirebaseAuthPlugin::GetPluginConstantsForFirebaseApp( const firebase::App& app) { flutter::EncodableMap constants; @@ -1294,7 +1315,7 @@ flutter::EncodableMap FirebaseAuthPlugin::GetPluginConstantsForFirebaseApp( firebase::auth::User user = auth->current_user(); if (user.is_valid()) { - PigeonUserDetails userDetails = ParseUserDetails(user); + InternalUserDetails userDetails = ParseUserDetails(user); flutter::EncodableList userDetailsList; userDetailsList.push_back( flutter::EncodableValue(userDetails.user_info().ToEncodableList())); diff --git a/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.h b/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.h index a75ff33e6cf5..575e201ff355 100644 --- a/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.h +++ b/packages/firebase_auth/firebase_auth/windows/firebase_auth_plugin.h @@ -42,14 +42,14 @@ class FirebaseAuthPlugin : public flutter::Plugin, static std::string GetAuthErrorCode(AuthError authError); static FlutterError ParseError(const firebase::FutureBase& future); - static PigeonUserDetails ParseUserDetails(const firebase::auth::User user); - static PigeonAdditionalUserInfo ParseAdditionalUserInfo( + static InternalUserDetails ParseUserDetails(const firebase::auth::User user); + static InternalAdditionalUserInfo ParseAdditionalUserInfo( const firebase::auth::AdditionalUserInfo user); static flutter::EncodableMap ConvertToEncodableMap( const std::map& originalMap); static flutter::EncodableValue ConvertToEncodableValue( const firebase::Variant& variant); - static PigeonUserInfo ParseUserInfo(const firebase::auth::User* user); + static InternalUserInfo ParseUserInfo(const firebase::auth::User* user); static flutter::EncodableList ParseProviderData( const firebase::auth::User* user); static flutter::EncodableValue ParseUserInfoToMap( @@ -70,7 +70,8 @@ class FirebaseAuthPlugin : public flutter::Plugin, std::function reply)> result) override; virtual void CheckActionCode( const AuthPigeonFirebaseApp& app, const std::string& code, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void ConfirmPasswordReset( const AuthPigeonFirebaseApp& app, const std::string& code, const std::string& new_password, @@ -78,28 +79,35 @@ class FirebaseAuthPlugin : public flutter::Plugin, virtual void CreateUserWithEmailAndPassword( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& password, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void SignInAnonymously( const AuthPigeonFirebaseApp& app, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void SignInWithCredential( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void SignInWithCustomToken( const AuthPigeonFirebaseApp& app, const std::string& token, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void SignInWithEmailAndPassword( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& password, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void SignInWithEmailLink( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& email_link, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void SignInWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) override; + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) + override; virtual void SignOut( const AuthPigeonFirebaseApp& app, std::function reply)> result) override; @@ -109,25 +117,25 @@ class FirebaseAuthPlugin : public flutter::Plugin, override; virtual void SendPasswordResetEmail( const AuthPigeonFirebaseApp& app, const std::string& email, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) override; virtual void SendSignInLinkToEmail( const AuthPigeonFirebaseApp& app, const std::string& email, - const PigeonActionCodeSettings& action_code_settings, + const InternalActionCodeSettings& action_code_settings, std::function reply)> result) override; virtual void SetLanguageCode( const AuthPigeonFirebaseApp& app, const std::string* language_code, std::function reply)> result) override; virtual void SetSettings( const AuthPigeonFirebaseApp& app, - const PigeonFirebaseAuthSettings& settings, + const InternalFirebaseAuthSettings& settings, std::function reply)> result) override; virtual void VerifyPasswordResetCode( const AuthPigeonFirebaseApp& app, const std::string& code, std::function reply)> result) override; virtual void VerifyPhoneNumber( const AuthPigeonFirebaseApp& app, - const PigeonVerifyPhoneNumberRequest& request, + const InternalVerifyPhoneNumberRequest& request, std::function reply)> result) override; // FirebaseAuthUserHostApi methods. @@ -136,52 +144,66 @@ class FirebaseAuthPlugin : public flutter::Plugin, std::function reply)> result) override; virtual void GetIdToken( const AuthPigeonFirebaseApp& app, bool force_refresh, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void LinkWithCredential( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void LinkWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) override; + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) + override; virtual void ReauthenticateWithCredential( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) override; + std::function reply)> result) + override; virtual void ReauthenticateWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) override; + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) + override; virtual void Reload( const AuthPigeonFirebaseApp& app, - std::function reply)> result) override; + std::function reply)> result) override; virtual void SendEmailVerification( const AuthPigeonFirebaseApp& app, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) override; - virtual void Unlink( - const AuthPigeonFirebaseApp& app, const std::string& provider_id, - std::function reply)> result) override; + virtual void Unlink(const AuthPigeonFirebaseApp& app, + const std::string& provider_id, + std::function reply)> + result) override; virtual void UpdateEmail( const AuthPigeonFirebaseApp& app, const std::string& new_email, - std::function reply)> result) override; + std::function reply)> result) override; virtual void UpdatePassword( const AuthPigeonFirebaseApp& app, const std::string& new_password, - std::function reply)> result) override; + std::function reply)> result) override; virtual void UpdatePhoneNumber( const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) override; + std::function reply)> result) override; virtual void UpdateProfile( - const AuthPigeonFirebaseApp& app, const PigeonUserProfile& profile, - std::function reply)> result) override; + const AuthPigeonFirebaseApp& app, const InternalUserProfile& profile, + std::function reply)> result) override; virtual void VerifyBeforeUpdateEmail( const AuthPigeonFirebaseApp& app, const std::string& new_email, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) override; virtual void RevokeTokenWithAuthorizationCode( const AuthPigeonFirebaseApp& app, const std::string& authorization_code, std::function reply)> result) override; + virtual void RevokeAccessToken( + const AuthPigeonFirebaseApp& app, const std::string& access_token, + std::function reply)> result) override; + + virtual void InitializeRecaptchaConfig( + const AuthPigeonFirebaseApp& app, + std::function reply)> result) override; + // FlutterFirebasePlugin methods. flutter::EncodableMap GetPluginConstantsForFirebaseApp( const firebase::App& app) override; diff --git a/packages/firebase_auth/firebase_auth/windows/messages.g.cpp b/packages/firebase_auth/firebase_auth/windows/messages.g.cpp index 59043185cf1b..ea2200e617b2 100644 --- a/packages/firebase_auth/firebase_auth/windows/messages.g.cpp +++ b/packages/firebase_auth/firebase_auth/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v19.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace firebase_auth_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,56 +33,283 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } -// PigeonMultiFactorSession +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); -PigeonMultiFactorSession::PigeonMultiFactorSession(const std::string& id) +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace +// InternalMultiFactorSession + +InternalMultiFactorSession::InternalMultiFactorSession(const std::string& id) : id_(id) {} -const std::string& PigeonMultiFactorSession::id() const { return id_; } +const std::string& InternalMultiFactorSession::id() const { return id_; } -void PigeonMultiFactorSession::set_id(std::string_view value_arg) { +void InternalMultiFactorSession::set_id(std::string_view value_arg) { id_ = value_arg; } -EncodableList PigeonMultiFactorSession::ToEncodableList() const { +EncodableList InternalMultiFactorSession::ToEncodableList() const { EncodableList list; list.reserve(1); list.push_back(EncodableValue(id_)); return list; } -PigeonMultiFactorSession PigeonMultiFactorSession::FromEncodableList( +InternalMultiFactorSession InternalMultiFactorSession::FromEncodableList( const EncodableList& list) { - PigeonMultiFactorSession decoded(std::get(list[0])); + InternalMultiFactorSession decoded(std::get(list[0])); return decoded; } -// PigeonPhoneMultiFactorAssertion +bool InternalMultiFactorSession::operator==( + const InternalMultiFactorSession& other) const { + return PigeonInternalDeepEquals(id_, other.id_); +} + +bool InternalMultiFactorSession::operator!=( + const InternalMultiFactorSession& other) const { + return !(*this == other); +} + +size_t InternalMultiFactorSession::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(id_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalMultiFactorSession& v) { + return v.Hash(); +} + +// InternalPhoneMultiFactorAssertion -PigeonPhoneMultiFactorAssertion::PigeonPhoneMultiFactorAssertion( +InternalPhoneMultiFactorAssertion::InternalPhoneMultiFactorAssertion( const std::string& verification_id, const std::string& verification_code) : verification_id_(verification_id), verification_code_(verification_code) {} -const std::string& PigeonPhoneMultiFactorAssertion::verification_id() const { +const std::string& InternalPhoneMultiFactorAssertion::verification_id() const { return verification_id_; } -void PigeonPhoneMultiFactorAssertion::set_verification_id( +void InternalPhoneMultiFactorAssertion::set_verification_id( std::string_view value_arg) { verification_id_ = value_arg; } -const std::string& PigeonPhoneMultiFactorAssertion::verification_code() const { +const std::string& InternalPhoneMultiFactorAssertion::verification_code() + const { return verification_code_; } -void PigeonPhoneMultiFactorAssertion::set_verification_code( +void InternalPhoneMultiFactorAssertion::set_verification_code( std::string_view value_arg) { verification_code_ = value_arg; } -EncodableList PigeonPhoneMultiFactorAssertion::ToEncodableList() const { +EncodableList InternalPhoneMultiFactorAssertion::ToEncodableList() const { EncodableList list; list.reserve(2); list.push_back(EncodableValue(verification_id_)); @@ -88,24 +317,46 @@ EncodableList PigeonPhoneMultiFactorAssertion::ToEncodableList() const { return list; } -PigeonPhoneMultiFactorAssertion -PigeonPhoneMultiFactorAssertion::FromEncodableList(const EncodableList& list) { - PigeonPhoneMultiFactorAssertion decoded(std::get(list[0]), - std::get(list[1])); +InternalPhoneMultiFactorAssertion +InternalPhoneMultiFactorAssertion::FromEncodableList( + const EncodableList& list) { + InternalPhoneMultiFactorAssertion decoded(std::get(list[0]), + std::get(list[1])); return decoded; } -// PigeonMultiFactorInfo +bool InternalPhoneMultiFactorAssertion::operator==( + const InternalPhoneMultiFactorAssertion& other) const { + return PigeonInternalDeepEquals(verification_id_, other.verification_id_) && + PigeonInternalDeepEquals(verification_code_, other.verification_code_); +} + +bool InternalPhoneMultiFactorAssertion::operator!=( + const InternalPhoneMultiFactorAssertion& other) const { + return !(*this == other); +} + +size_t InternalPhoneMultiFactorAssertion::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(verification_id_); + result = result * 31 + PigeonInternalDeepHash(verification_code_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalPhoneMultiFactorAssertion& v) { + return v.Hash(); +} + +// InternalMultiFactorInfo -PigeonMultiFactorInfo::PigeonMultiFactorInfo(double enrollment_timestamp, - const std::string& uid) +InternalMultiFactorInfo::InternalMultiFactorInfo(double enrollment_timestamp, + const std::string& uid) : enrollment_timestamp_(enrollment_timestamp), uid_(uid) {} -PigeonMultiFactorInfo::PigeonMultiFactorInfo(const std::string* display_name, - double enrollment_timestamp, - const std::string* factor_id, - const std::string& uid, - const std::string* phone_number) +InternalMultiFactorInfo::InternalMultiFactorInfo( + const std::string* display_name, double enrollment_timestamp, + const std::string* factor_id, const std::string& uid, + const std::string* phone_number) : display_name_(display_name ? std::optional(*display_name) : std::nullopt), enrollment_timestamp_(enrollment_timestamp), @@ -115,62 +366,62 @@ PigeonMultiFactorInfo::PigeonMultiFactorInfo(const std::string* display_name, phone_number_(phone_number ? std::optional(*phone_number) : std::nullopt) {} -const std::string* PigeonMultiFactorInfo::display_name() const { +const std::string* InternalMultiFactorInfo::display_name() const { return display_name_ ? &(*display_name_) : nullptr; } -void PigeonMultiFactorInfo::set_display_name( +void InternalMultiFactorInfo::set_display_name( const std::string_view* value_arg) { display_name_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonMultiFactorInfo::set_display_name(std::string_view value_arg) { +void InternalMultiFactorInfo::set_display_name(std::string_view value_arg) { display_name_ = value_arg; } -double PigeonMultiFactorInfo::enrollment_timestamp() const { +double InternalMultiFactorInfo::enrollment_timestamp() const { return enrollment_timestamp_; } -void PigeonMultiFactorInfo::set_enrollment_timestamp(double value_arg) { +void InternalMultiFactorInfo::set_enrollment_timestamp(double value_arg) { enrollment_timestamp_ = value_arg; } -const std::string* PigeonMultiFactorInfo::factor_id() const { +const std::string* InternalMultiFactorInfo::factor_id() const { return factor_id_ ? &(*factor_id_) : nullptr; } -void PigeonMultiFactorInfo::set_factor_id(const std::string_view* value_arg) { +void InternalMultiFactorInfo::set_factor_id(const std::string_view* value_arg) { factor_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonMultiFactorInfo::set_factor_id(std::string_view value_arg) { +void InternalMultiFactorInfo::set_factor_id(std::string_view value_arg) { factor_id_ = value_arg; } -const std::string& PigeonMultiFactorInfo::uid() const { return uid_; } +const std::string& InternalMultiFactorInfo::uid() const { return uid_; } -void PigeonMultiFactorInfo::set_uid(std::string_view value_arg) { +void InternalMultiFactorInfo::set_uid(std::string_view value_arg) { uid_ = value_arg; } -const std::string* PigeonMultiFactorInfo::phone_number() const { +const std::string* InternalMultiFactorInfo::phone_number() const { return phone_number_ ? &(*phone_number_) : nullptr; } -void PigeonMultiFactorInfo::set_phone_number( +void InternalMultiFactorInfo::set_phone_number( const std::string_view* value_arg) { phone_number_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonMultiFactorInfo::set_phone_number(std::string_view value_arg) { +void InternalMultiFactorInfo::set_phone_number(std::string_view value_arg) { phone_number_ = value_arg; } -EncodableList PigeonMultiFactorInfo::ToEncodableList() const { +EncodableList InternalMultiFactorInfo::ToEncodableList() const { EncodableList list; list.reserve(5); list.push_back(display_name_ ? EncodableValue(*display_name_) @@ -183,10 +434,10 @@ EncodableList PigeonMultiFactorInfo::ToEncodableList() const { return list; } -PigeonMultiFactorInfo PigeonMultiFactorInfo::FromEncodableList( +InternalMultiFactorInfo InternalMultiFactorInfo::FromEncodableList( const EncodableList& list) { - PigeonMultiFactorInfo decoded(std::get(list[1]), - std::get(list[3])); + InternalMultiFactorInfo decoded(std::get(list[1]), + std::get(list[3])); auto& encodable_display_name = list[0]; if (!encodable_display_name.IsNull()) { decoded.set_display_name(std::get(encodable_display_name)); @@ -202,6 +453,35 @@ PigeonMultiFactorInfo PigeonMultiFactorInfo::FromEncodableList( return decoded; } +bool InternalMultiFactorInfo::operator==( + const InternalMultiFactorInfo& other) const { + return PigeonInternalDeepEquals(display_name_, other.display_name_) && + PigeonInternalDeepEquals(enrollment_timestamp_, + other.enrollment_timestamp_) && + PigeonInternalDeepEquals(factor_id_, other.factor_id_) && + PigeonInternalDeepEquals(uid_, other.uid_) && + PigeonInternalDeepEquals(phone_number_, other.phone_number_); +} + +bool InternalMultiFactorInfo::operator!=( + const InternalMultiFactorInfo& other) const { + return !(*this == other); +} + +size_t InternalMultiFactorInfo::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(display_name_); + result = result * 31 + PigeonInternalDeepHash(enrollment_timestamp_); + result = result * 31 + PigeonInternalDeepHash(factor_id_); + result = result * 31 + PigeonInternalDeepHash(uid_); + result = result * 31 + PigeonInternalDeepHash(phone_number_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalMultiFactorInfo& v) { + return v.Hash(); +} + // AuthPigeonFirebaseApp AuthPigeonFirebaseApp::AuthPigeonFirebaseApp(const std::string& app_name) @@ -275,44 +555,70 @@ AuthPigeonFirebaseApp AuthPigeonFirebaseApp::FromEncodableList( return decoded; } -// PigeonActionCodeInfoData +bool AuthPigeonFirebaseApp::operator==( + const AuthPigeonFirebaseApp& other) const { + return PigeonInternalDeepEquals(app_name_, other.app_name_) && + PigeonInternalDeepEquals(tenant_id_, other.tenant_id_) && + PigeonInternalDeepEquals(custom_auth_domain_, + other.custom_auth_domain_); +} + +bool AuthPigeonFirebaseApp::operator!=( + const AuthPigeonFirebaseApp& other) const { + return !(*this == other); +} + +size_t AuthPigeonFirebaseApp::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(app_name_); + result = result * 31 + PigeonInternalDeepHash(tenant_id_); + result = result * 31 + PigeonInternalDeepHash(custom_auth_domain_); + return result; +} + +size_t PigeonInternalDeepHash(const AuthPigeonFirebaseApp& v) { + return v.Hash(); +} + +// InternalActionCodeInfoData -PigeonActionCodeInfoData::PigeonActionCodeInfoData() {} +InternalActionCodeInfoData::InternalActionCodeInfoData() {} -PigeonActionCodeInfoData::PigeonActionCodeInfoData( +InternalActionCodeInfoData::InternalActionCodeInfoData( const std::string* email, const std::string* previous_email) : email_(email ? std::optional(*email) : std::nullopt), previous_email_(previous_email ? std::optional(*previous_email) : std::nullopt) {} -const std::string* PigeonActionCodeInfoData::email() const { +const std::string* InternalActionCodeInfoData::email() const { return email_ ? &(*email_) : nullptr; } -void PigeonActionCodeInfoData::set_email(const std::string_view* value_arg) { +void InternalActionCodeInfoData::set_email(const std::string_view* value_arg) { email_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonActionCodeInfoData::set_email(std::string_view value_arg) { +void InternalActionCodeInfoData::set_email(std::string_view value_arg) { email_ = value_arg; } -const std::string* PigeonActionCodeInfoData::previous_email() const { +const std::string* InternalActionCodeInfoData::previous_email() const { return previous_email_ ? &(*previous_email_) : nullptr; } -void PigeonActionCodeInfoData::set_previous_email( +void InternalActionCodeInfoData::set_previous_email( const std::string_view* value_arg) { previous_email_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonActionCodeInfoData::set_previous_email(std::string_view value_arg) { +void InternalActionCodeInfoData::set_previous_email( + std::string_view value_arg) { previous_email_ = value_arg; } -EncodableList PigeonActionCodeInfoData::ToEncodableList() const { +EncodableList InternalActionCodeInfoData::ToEncodableList() const { EncodableList list; list.reserve(2); list.push_back(email_ ? EncodableValue(*email_) : EncodableValue()); @@ -321,9 +627,9 @@ EncodableList PigeonActionCodeInfoData::ToEncodableList() const { return list; } -PigeonActionCodeInfoData PigeonActionCodeInfoData::FromEncodableList( +InternalActionCodeInfoData InternalActionCodeInfoData::FromEncodableList( const EncodableList& list) { - PigeonActionCodeInfoData decoded; + InternalActionCodeInfoData decoded; auto& encodable_email = list[0]; if (!encodable_email.IsNull()) { decoded.set_email(std::get(encodable_email)); @@ -335,65 +641,112 @@ PigeonActionCodeInfoData PigeonActionCodeInfoData::FromEncodableList( return decoded; } -// PigeonActionCodeInfo +bool InternalActionCodeInfoData::operator==( + const InternalActionCodeInfoData& other) const { + return PigeonInternalDeepEquals(email_, other.email_) && + PigeonInternalDeepEquals(previous_email_, other.previous_email_); +} + +bool InternalActionCodeInfoData::operator!=( + const InternalActionCodeInfoData& other) const { + return !(*this == other); +} + +size_t InternalActionCodeInfoData::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(email_); + result = result * 31 + PigeonInternalDeepHash(previous_email_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalActionCodeInfoData& v) { + return v.Hash(); +} + +// InternalActionCodeInfo -PigeonActionCodeInfo::PigeonActionCodeInfo( +InternalActionCodeInfo::InternalActionCodeInfo( const ActionCodeInfoOperation& operation, - const PigeonActionCodeInfoData& data) + const InternalActionCodeInfoData& data) : operation_(operation), - data_(std::make_unique(data)) {} + data_(std::make_unique(data)) {} -PigeonActionCodeInfo::PigeonActionCodeInfo(const PigeonActionCodeInfo& other) +InternalActionCodeInfo::InternalActionCodeInfo( + const InternalActionCodeInfo& other) : operation_(other.operation_), - data_(std::make_unique(*other.data_)) {} + data_(std::make_unique(*other.data_)) {} -PigeonActionCodeInfo& PigeonActionCodeInfo::operator=( - const PigeonActionCodeInfo& other) { +InternalActionCodeInfo& InternalActionCodeInfo::operator=( + const InternalActionCodeInfo& other) { operation_ = other.operation_; - data_ = std::make_unique(*other.data_); + data_ = std::make_unique(*other.data_); return *this; } -const ActionCodeInfoOperation& PigeonActionCodeInfo::operation() const { +const ActionCodeInfoOperation& InternalActionCodeInfo::operation() const { return operation_; } -void PigeonActionCodeInfo::set_operation( +void InternalActionCodeInfo::set_operation( const ActionCodeInfoOperation& value_arg) { operation_ = value_arg; } -const PigeonActionCodeInfoData& PigeonActionCodeInfo::data() const { +const InternalActionCodeInfoData& InternalActionCodeInfo::data() const { return *data_; } -void PigeonActionCodeInfo::set_data(const PigeonActionCodeInfoData& value_arg) { - data_ = std::make_unique(value_arg); +void InternalActionCodeInfo::set_data( + const InternalActionCodeInfoData& value_arg) { + data_ = std::make_unique(value_arg); } -EncodableList PigeonActionCodeInfo::ToEncodableList() const { +EncodableList InternalActionCodeInfo::ToEncodableList() const { EncodableList list; list.reserve(2); - list.push_back(EncodableValue((int)operation_)); + list.push_back(CustomEncodableValue(operation_)); list.push_back(CustomEncodableValue(*data_)); return list; } -PigeonActionCodeInfo PigeonActionCodeInfo::FromEncodableList( +InternalActionCodeInfo InternalActionCodeInfo::FromEncodableList( const EncodableList& list) { - PigeonActionCodeInfo decoded( - (ActionCodeInfoOperation)(std::get(list[0])), - std::any_cast( + InternalActionCodeInfo decoded( + std::any_cast( + std::get(list[0])), + std::any_cast( std::get(list[1]))); return decoded; } -// PigeonAdditionalUserInfo +bool InternalActionCodeInfo::operator==( + const InternalActionCodeInfo& other) const { + return PigeonInternalDeepEquals(operation_, other.operation_) && + PigeonInternalDeepEquals(data_, other.data_); +} + +bool InternalActionCodeInfo::operator!=( + const InternalActionCodeInfo& other) const { + return !(*this == other); +} + +size_t InternalActionCodeInfo::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(operation_); + result = result * 31 + PigeonInternalDeepHash(data_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalActionCodeInfo& v) { + return v.Hash(); +} + +// InternalAdditionalUserInfo -PigeonAdditionalUserInfo::PigeonAdditionalUserInfo(bool is_new_user) +InternalAdditionalUserInfo::InternalAdditionalUserInfo(bool is_new_user) : is_new_user_(is_new_user) {} -PigeonAdditionalUserInfo::PigeonAdditionalUserInfo( +InternalAdditionalUserInfo::InternalAdditionalUserInfo( bool is_new_user, const std::string* provider_id, const std::string* username, const std::string* authorization_code, const EncodableMap* profile) @@ -408,66 +761,67 @@ PigeonAdditionalUserInfo::PigeonAdditionalUserInfo( profile_(profile ? std::optional(*profile) : std::nullopt) { } -bool PigeonAdditionalUserInfo::is_new_user() const { return is_new_user_; } +bool InternalAdditionalUserInfo::is_new_user() const { return is_new_user_; } -void PigeonAdditionalUserInfo::set_is_new_user(bool value_arg) { +void InternalAdditionalUserInfo::set_is_new_user(bool value_arg) { is_new_user_ = value_arg; } -const std::string* PigeonAdditionalUserInfo::provider_id() const { +const std::string* InternalAdditionalUserInfo::provider_id() const { return provider_id_ ? &(*provider_id_) : nullptr; } -void PigeonAdditionalUserInfo::set_provider_id( +void InternalAdditionalUserInfo::set_provider_id( const std::string_view* value_arg) { provider_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonAdditionalUserInfo::set_provider_id(std::string_view value_arg) { +void InternalAdditionalUserInfo::set_provider_id(std::string_view value_arg) { provider_id_ = value_arg; } -const std::string* PigeonAdditionalUserInfo::username() const { +const std::string* InternalAdditionalUserInfo::username() const { return username_ ? &(*username_) : nullptr; } -void PigeonAdditionalUserInfo::set_username(const std::string_view* value_arg) { +void InternalAdditionalUserInfo::set_username( + const std::string_view* value_arg) { username_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonAdditionalUserInfo::set_username(std::string_view value_arg) { +void InternalAdditionalUserInfo::set_username(std::string_view value_arg) { username_ = value_arg; } -const std::string* PigeonAdditionalUserInfo::authorization_code() const { +const std::string* InternalAdditionalUserInfo::authorization_code() const { return authorization_code_ ? &(*authorization_code_) : nullptr; } -void PigeonAdditionalUserInfo::set_authorization_code( +void InternalAdditionalUserInfo::set_authorization_code( const std::string_view* value_arg) { authorization_code_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonAdditionalUserInfo::set_authorization_code( +void InternalAdditionalUserInfo::set_authorization_code( std::string_view value_arg) { authorization_code_ = value_arg; } -const EncodableMap* PigeonAdditionalUserInfo::profile() const { +const EncodableMap* InternalAdditionalUserInfo::profile() const { return profile_ ? &(*profile_) : nullptr; } -void PigeonAdditionalUserInfo::set_profile(const EncodableMap* value_arg) { +void InternalAdditionalUserInfo::set_profile(const EncodableMap* value_arg) { profile_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonAdditionalUserInfo::set_profile(const EncodableMap& value_arg) { +void InternalAdditionalUserInfo::set_profile(const EncodableMap& value_arg) { profile_ = value_arg; } -EncodableList PigeonAdditionalUserInfo::ToEncodableList() const { +EncodableList InternalAdditionalUserInfo::ToEncodableList() const { EncodableList list; list.reserve(5); list.push_back(EncodableValue(is_new_user_)); @@ -480,9 +834,9 @@ EncodableList PigeonAdditionalUserInfo::ToEncodableList() const { return list; } -PigeonAdditionalUserInfo PigeonAdditionalUserInfo::FromEncodableList( +InternalAdditionalUserInfo InternalAdditionalUserInfo::FromEncodableList( const EncodableList& list) { - PigeonAdditionalUserInfo decoded(std::get(list[0])); + InternalAdditionalUserInfo decoded(std::get(list[0])); auto& encodable_provider_id = list[1]; if (!encodable_provider_id.IsNull()) { decoded.set_provider_id(std::get(encodable_provider_id)); @@ -503,61 +857,90 @@ PigeonAdditionalUserInfo PigeonAdditionalUserInfo::FromEncodableList( return decoded; } -// PigeonAuthCredential +bool InternalAdditionalUserInfo::operator==( + const InternalAdditionalUserInfo& other) const { + return PigeonInternalDeepEquals(is_new_user_, other.is_new_user_) && + PigeonInternalDeepEquals(provider_id_, other.provider_id_) && + PigeonInternalDeepEquals(username_, other.username_) && + PigeonInternalDeepEquals(authorization_code_, + other.authorization_code_) && + PigeonInternalDeepEquals(profile_, other.profile_); +} + +bool InternalAdditionalUserInfo::operator!=( + const InternalAdditionalUserInfo& other) const { + return !(*this == other); +} + +size_t InternalAdditionalUserInfo::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(is_new_user_); + result = result * 31 + PigeonInternalDeepHash(provider_id_); + result = result * 31 + PigeonInternalDeepHash(username_); + result = result * 31 + PigeonInternalDeepHash(authorization_code_); + result = result * 31 + PigeonInternalDeepHash(profile_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalAdditionalUserInfo& v) { + return v.Hash(); +} + +// InternalAuthCredential -PigeonAuthCredential::PigeonAuthCredential(const std::string& provider_id, - const std::string& sign_in_method, - int64_t native_id) +InternalAuthCredential::InternalAuthCredential( + const std::string& provider_id, const std::string& sign_in_method, + int64_t native_id) : provider_id_(provider_id), sign_in_method_(sign_in_method), native_id_(native_id) {} -PigeonAuthCredential::PigeonAuthCredential(const std::string& provider_id, - const std::string& sign_in_method, - int64_t native_id, - const std::string* access_token) +InternalAuthCredential::InternalAuthCredential( + const std::string& provider_id, const std::string& sign_in_method, + int64_t native_id, const std::string* access_token) : provider_id_(provider_id), sign_in_method_(sign_in_method), native_id_(native_id), access_token_(access_token ? std::optional(*access_token) : std::nullopt) {} -const std::string& PigeonAuthCredential::provider_id() const { +const std::string& InternalAuthCredential::provider_id() const { return provider_id_; } -void PigeonAuthCredential::set_provider_id(std::string_view value_arg) { +void InternalAuthCredential::set_provider_id(std::string_view value_arg) { provider_id_ = value_arg; } -const std::string& PigeonAuthCredential::sign_in_method() const { +const std::string& InternalAuthCredential::sign_in_method() const { return sign_in_method_; } -void PigeonAuthCredential::set_sign_in_method(std::string_view value_arg) { +void InternalAuthCredential::set_sign_in_method(std::string_view value_arg) { sign_in_method_ = value_arg; } -int64_t PigeonAuthCredential::native_id() const { return native_id_; } +int64_t InternalAuthCredential::native_id() const { return native_id_; } -void PigeonAuthCredential::set_native_id(int64_t value_arg) { +void InternalAuthCredential::set_native_id(int64_t value_arg) { native_id_ = value_arg; } -const std::string* PigeonAuthCredential::access_token() const { +const std::string* InternalAuthCredential::access_token() const { return access_token_ ? &(*access_token_) : nullptr; } -void PigeonAuthCredential::set_access_token(const std::string_view* value_arg) { +void InternalAuthCredential::set_access_token( + const std::string_view* value_arg) { access_token_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonAuthCredential::set_access_token(std::string_view value_arg) { +void InternalAuthCredential::set_access_token(std::string_view value_arg) { access_token_ = value_arg; } -EncodableList PigeonAuthCredential::ToEncodableList() const { +EncodableList InternalAuthCredential::ToEncodableList() const { EncodableList list; list.reserve(4); list.push_back(EncodableValue(provider_id_)); @@ -568,11 +951,11 @@ EncodableList PigeonAuthCredential::ToEncodableList() const { return list; } -PigeonAuthCredential PigeonAuthCredential::FromEncodableList( +InternalAuthCredential InternalAuthCredential::FromEncodableList( const EncodableList& list) { - PigeonAuthCredential decoded(std::get(list[0]), - std::get(list[1]), - list[2].LongValue()); + InternalAuthCredential decoded(std::get(list[0]), + std::get(list[1]), + std::get(list[2])); auto& encodable_access_token = list[3]; if (!encodable_access_token.IsNull()) { decoded.set_access_token(std::get(encodable_access_token)); @@ -580,15 +963,41 @@ PigeonAuthCredential PigeonAuthCredential::FromEncodableList( return decoded; } -// PigeonUserInfo +bool InternalAuthCredential::operator==( + const InternalAuthCredential& other) const { + return PigeonInternalDeepEquals(provider_id_, other.provider_id_) && + PigeonInternalDeepEquals(sign_in_method_, other.sign_in_method_) && + PigeonInternalDeepEquals(native_id_, other.native_id_) && + PigeonInternalDeepEquals(access_token_, other.access_token_); +} + +bool InternalAuthCredential::operator!=( + const InternalAuthCredential& other) const { + return !(*this == other); +} + +size_t InternalAuthCredential::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(provider_id_); + result = result * 31 + PigeonInternalDeepHash(sign_in_method_); + result = result * 31 + PigeonInternalDeepHash(native_id_); + result = result * 31 + PigeonInternalDeepHash(access_token_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalAuthCredential& v) { + return v.Hash(); +} + +// InternalUserInfo -PigeonUserInfo::PigeonUserInfo(const std::string& uid, bool is_anonymous, - bool is_email_verified) +InternalUserInfo::InternalUserInfo(const std::string& uid, bool is_anonymous, + bool is_email_verified) : uid_(uid), is_anonymous_(is_anonymous), is_email_verified_(is_email_verified) {} -PigeonUserInfo::PigeonUserInfo( +InternalUserInfo::InternalUserInfo( const std::string& uid, const std::string* email, const std::string* display_name, const std::string* photo_url, const std::string* phone_number, bool is_anonymous, bool is_email_verified, @@ -619,139 +1028,139 @@ PigeonUserInfo::PigeonUserInfo( ? std::optional(*last_sign_in_timestamp) : std::nullopt) {} -const std::string& PigeonUserInfo::uid() const { return uid_; } +const std::string& InternalUserInfo::uid() const { return uid_; } -void PigeonUserInfo::set_uid(std::string_view value_arg) { uid_ = value_arg; } +void InternalUserInfo::set_uid(std::string_view value_arg) { uid_ = value_arg; } -const std::string* PigeonUserInfo::email() const { +const std::string* InternalUserInfo::email() const { return email_ ? &(*email_) : nullptr; } -void PigeonUserInfo::set_email(const std::string_view* value_arg) { +void InternalUserInfo::set_email(const std::string_view* value_arg) { email_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_email(std::string_view value_arg) { +void InternalUserInfo::set_email(std::string_view value_arg) { email_ = value_arg; } -const std::string* PigeonUserInfo::display_name() const { +const std::string* InternalUserInfo::display_name() const { return display_name_ ? &(*display_name_) : nullptr; } -void PigeonUserInfo::set_display_name(const std::string_view* value_arg) { +void InternalUserInfo::set_display_name(const std::string_view* value_arg) { display_name_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_display_name(std::string_view value_arg) { +void InternalUserInfo::set_display_name(std::string_view value_arg) { display_name_ = value_arg; } -const std::string* PigeonUserInfo::photo_url() const { +const std::string* InternalUserInfo::photo_url() const { return photo_url_ ? &(*photo_url_) : nullptr; } -void PigeonUserInfo::set_photo_url(const std::string_view* value_arg) { +void InternalUserInfo::set_photo_url(const std::string_view* value_arg) { photo_url_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_photo_url(std::string_view value_arg) { +void InternalUserInfo::set_photo_url(std::string_view value_arg) { photo_url_ = value_arg; } -const std::string* PigeonUserInfo::phone_number() const { +const std::string* InternalUserInfo::phone_number() const { return phone_number_ ? &(*phone_number_) : nullptr; } -void PigeonUserInfo::set_phone_number(const std::string_view* value_arg) { +void InternalUserInfo::set_phone_number(const std::string_view* value_arg) { phone_number_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_phone_number(std::string_view value_arg) { +void InternalUserInfo::set_phone_number(std::string_view value_arg) { phone_number_ = value_arg; } -bool PigeonUserInfo::is_anonymous() const { return is_anonymous_; } +bool InternalUserInfo::is_anonymous() const { return is_anonymous_; } -void PigeonUserInfo::set_is_anonymous(bool value_arg) { +void InternalUserInfo::set_is_anonymous(bool value_arg) { is_anonymous_ = value_arg; } -bool PigeonUserInfo::is_email_verified() const { return is_email_verified_; } +bool InternalUserInfo::is_email_verified() const { return is_email_verified_; } -void PigeonUserInfo::set_is_email_verified(bool value_arg) { +void InternalUserInfo::set_is_email_verified(bool value_arg) { is_email_verified_ = value_arg; } -const std::string* PigeonUserInfo::provider_id() const { +const std::string* InternalUserInfo::provider_id() const { return provider_id_ ? &(*provider_id_) : nullptr; } -void PigeonUserInfo::set_provider_id(const std::string_view* value_arg) { +void InternalUserInfo::set_provider_id(const std::string_view* value_arg) { provider_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_provider_id(std::string_view value_arg) { +void InternalUserInfo::set_provider_id(std::string_view value_arg) { provider_id_ = value_arg; } -const std::string* PigeonUserInfo::tenant_id() const { +const std::string* InternalUserInfo::tenant_id() const { return tenant_id_ ? &(*tenant_id_) : nullptr; } -void PigeonUserInfo::set_tenant_id(const std::string_view* value_arg) { +void InternalUserInfo::set_tenant_id(const std::string_view* value_arg) { tenant_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_tenant_id(std::string_view value_arg) { +void InternalUserInfo::set_tenant_id(std::string_view value_arg) { tenant_id_ = value_arg; } -const std::string* PigeonUserInfo::refresh_token() const { +const std::string* InternalUserInfo::refresh_token() const { return refresh_token_ ? &(*refresh_token_) : nullptr; } -void PigeonUserInfo::set_refresh_token(const std::string_view* value_arg) { +void InternalUserInfo::set_refresh_token(const std::string_view* value_arg) { refresh_token_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_refresh_token(std::string_view value_arg) { +void InternalUserInfo::set_refresh_token(std::string_view value_arg) { refresh_token_ = value_arg; } -const int64_t* PigeonUserInfo::creation_timestamp() const { +const int64_t* InternalUserInfo::creation_timestamp() const { return creation_timestamp_ ? &(*creation_timestamp_) : nullptr; } -void PigeonUserInfo::set_creation_timestamp(const int64_t* value_arg) { +void InternalUserInfo::set_creation_timestamp(const int64_t* value_arg) { creation_timestamp_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_creation_timestamp(int64_t value_arg) { +void InternalUserInfo::set_creation_timestamp(int64_t value_arg) { creation_timestamp_ = value_arg; } -const int64_t* PigeonUserInfo::last_sign_in_timestamp() const { +const int64_t* InternalUserInfo::last_sign_in_timestamp() const { return last_sign_in_timestamp_ ? &(*last_sign_in_timestamp_) : nullptr; } -void PigeonUserInfo::set_last_sign_in_timestamp(const int64_t* value_arg) { +void InternalUserInfo::set_last_sign_in_timestamp(const int64_t* value_arg) { last_sign_in_timestamp_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserInfo::set_last_sign_in_timestamp(int64_t value_arg) { +void InternalUserInfo::set_last_sign_in_timestamp(int64_t value_arg) { last_sign_in_timestamp_ = value_arg; } -EncodableList PigeonUserInfo::ToEncodableList() const { +EncodableList InternalUserInfo::ToEncodableList() const { EncodableList list; list.reserve(12); list.push_back(EncodableValue(uid_)); @@ -776,9 +1185,10 @@ EncodableList PigeonUserInfo::ToEncodableList() const { return list; } -PigeonUserInfo PigeonUserInfo::FromEncodableList(const EncodableList& list) { - PigeonUserInfo decoded(std::get(list[0]), - std::get(list[5]), std::get(list[6])); +InternalUserInfo InternalUserInfo::FromEncodableList( + const EncodableList& list) { + InternalUserInfo decoded(std::get(list[0]), + std::get(list[5]), std::get(list[6])); auto& encodable_email = list[1]; if (!encodable_email.IsNull()) { decoded.set_email(std::get(encodable_email)); @@ -809,51 +1219,93 @@ PigeonUserInfo PigeonUserInfo::FromEncodableList(const EncodableList& list) { } auto& encodable_creation_timestamp = list[10]; if (!encodable_creation_timestamp.IsNull()) { - decoded.set_creation_timestamp(encodable_creation_timestamp.LongValue()); + decoded.set_creation_timestamp( + std::get(encodable_creation_timestamp)); } auto& encodable_last_sign_in_timestamp = list[11]; if (!encodable_last_sign_in_timestamp.IsNull()) { decoded.set_last_sign_in_timestamp( - encodable_last_sign_in_timestamp.LongValue()); + std::get(encodable_last_sign_in_timestamp)); } return decoded; } -// PigeonUserDetails - -PigeonUserDetails::PigeonUserDetails(const PigeonUserInfo& user_info, - const EncodableList& provider_data) - : user_info_(std::make_unique(user_info)), +bool InternalUserInfo::operator==(const InternalUserInfo& other) const { + return PigeonInternalDeepEquals(uid_, other.uid_) && + PigeonInternalDeepEquals(email_, other.email_) && + PigeonInternalDeepEquals(display_name_, other.display_name_) && + PigeonInternalDeepEquals(photo_url_, other.photo_url_) && + PigeonInternalDeepEquals(phone_number_, other.phone_number_) && + PigeonInternalDeepEquals(is_anonymous_, other.is_anonymous_) && + PigeonInternalDeepEquals(is_email_verified_, + other.is_email_verified_) && + PigeonInternalDeepEquals(provider_id_, other.provider_id_) && + PigeonInternalDeepEquals(tenant_id_, other.tenant_id_) && + PigeonInternalDeepEquals(refresh_token_, other.refresh_token_) && + PigeonInternalDeepEquals(creation_timestamp_, + other.creation_timestamp_) && + PigeonInternalDeepEquals(last_sign_in_timestamp_, + other.last_sign_in_timestamp_); +} + +bool InternalUserInfo::operator!=(const InternalUserInfo& other) const { + return !(*this == other); +} + +size_t InternalUserInfo::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(uid_); + result = result * 31 + PigeonInternalDeepHash(email_); + result = result * 31 + PigeonInternalDeepHash(display_name_); + result = result * 31 + PigeonInternalDeepHash(photo_url_); + result = result * 31 + PigeonInternalDeepHash(phone_number_); + result = result * 31 + PigeonInternalDeepHash(is_anonymous_); + result = result * 31 + PigeonInternalDeepHash(is_email_verified_); + result = result * 31 + PigeonInternalDeepHash(provider_id_); + result = result * 31 + PigeonInternalDeepHash(tenant_id_); + result = result * 31 + PigeonInternalDeepHash(refresh_token_); + result = result * 31 + PigeonInternalDeepHash(creation_timestamp_); + result = result * 31 + PigeonInternalDeepHash(last_sign_in_timestamp_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalUserInfo& v) { return v.Hash(); } + +// InternalUserDetails + +InternalUserDetails::InternalUserDetails(const InternalUserInfo& user_info, + const EncodableList& provider_data) + : user_info_(std::make_unique(user_info)), provider_data_(provider_data) {} -PigeonUserDetails::PigeonUserDetails(const PigeonUserDetails& other) - : user_info_(std::make_unique(*other.user_info_)), +InternalUserDetails::InternalUserDetails(const InternalUserDetails& other) + : user_info_(std::make_unique(*other.user_info_)), provider_data_(other.provider_data_) {} -PigeonUserDetails& PigeonUserDetails::operator=( - const PigeonUserDetails& other) { - user_info_ = std::make_unique(*other.user_info_); +InternalUserDetails& InternalUserDetails::operator=( + const InternalUserDetails& other) { + user_info_ = std::make_unique(*other.user_info_); provider_data_ = other.provider_data_; return *this; } -const PigeonUserInfo& PigeonUserDetails::user_info() const { +const InternalUserInfo& InternalUserDetails::user_info() const { return *user_info_; } -void PigeonUserDetails::set_user_info(const PigeonUserInfo& value_arg) { - user_info_ = std::make_unique(value_arg); +void InternalUserDetails::set_user_info(const InternalUserInfo& value_arg) { + user_info_ = std::make_unique(value_arg); } -const EncodableList& PigeonUserDetails::provider_data() const { +const EncodableList& InternalUserDetails::provider_data() const { return provider_data_; } -void PigeonUserDetails::set_provider_data(const EncodableList& value_arg) { +void InternalUserDetails::set_provider_data(const EncodableList& value_arg) { provider_data_ = value_arg; } -EncodableList PigeonUserDetails::ToEncodableList() const { +EncodableList InternalUserDetails::ToEncodableList() const { EncodableList list; list.reserve(2); list.push_back(CustomEncodableValue(*user_info_)); @@ -861,101 +1313,123 @@ EncodableList PigeonUserDetails::ToEncodableList() const { return list; } -PigeonUserDetails PigeonUserDetails::FromEncodableList( +InternalUserDetails InternalUserDetails::FromEncodableList( const EncodableList& list) { - PigeonUserDetails decoded(std::any_cast( - std::get(list[0])), - std::get(list[1])); + InternalUserDetails decoded(std::any_cast( + std::get(list[0])), + std::get(list[1])); return decoded; } -// PigeonUserCredential +bool InternalUserDetails::operator==(const InternalUserDetails& other) const { + return PigeonInternalDeepEquals(user_info_, other.user_info_) && + PigeonInternalDeepEquals(provider_data_, other.provider_data_); +} + +bool InternalUserDetails::operator!=(const InternalUserDetails& other) const { + return !(*this == other); +} + +size_t InternalUserDetails::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(user_info_); + result = result * 31 + PigeonInternalDeepHash(provider_data_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalUserDetails& v) { return v.Hash(); } + +// InternalUserCredential -PigeonUserCredential::PigeonUserCredential() {} +InternalUserCredential::InternalUserCredential() {} -PigeonUserCredential::PigeonUserCredential( - const PigeonUserDetails* user, - const PigeonAdditionalUserInfo* additional_user_info, - const PigeonAuthCredential* credential) - : user_(user ? std::make_unique(*user) : nullptr), +InternalUserCredential::InternalUserCredential( + const InternalUserDetails* user, + const InternalAdditionalUserInfo* additional_user_info, + const InternalAuthCredential* credential) + : user_(user ? std::make_unique(*user) : nullptr), additional_user_info_(additional_user_info - ? std::make_unique( + ? std::make_unique( *additional_user_info) : nullptr), credential_(credential - ? std::make_unique(*credential) + ? std::make_unique(*credential) : nullptr) {} -PigeonUserCredential::PigeonUserCredential(const PigeonUserCredential& other) - : user_(other.user_ ? std::make_unique(*other.user_) +InternalUserCredential::InternalUserCredential( + const InternalUserCredential& other) + : user_(other.user_ ? std::make_unique(*other.user_) : nullptr), additional_user_info_(other.additional_user_info_ - ? std::make_unique( + ? std::make_unique( *other.additional_user_info_) : nullptr), - credential_(other.credential_ ? std::make_unique( + credential_(other.credential_ ? std::make_unique( *other.credential_) : nullptr) {} -PigeonUserCredential& PigeonUserCredential::operator=( - const PigeonUserCredential& other) { - user_ = - other.user_ ? std::make_unique(*other.user_) : nullptr; +InternalUserCredential& InternalUserCredential::operator=( + const InternalUserCredential& other) { + user_ = other.user_ ? std::make_unique(*other.user_) + : nullptr; additional_user_info_ = other.additional_user_info_ - ? std::make_unique( + ? std::make_unique( *other.additional_user_info_) : nullptr; - credential_ = other.credential_ - ? std::make_unique(*other.credential_) - : nullptr; + credential_ = + other.credential_ + ? std::make_unique(*other.credential_) + : nullptr; return *this; } -const PigeonUserDetails* PigeonUserCredential::user() const { +const InternalUserDetails* InternalUserCredential::user() const { return user_.get(); } -void PigeonUserCredential::set_user(const PigeonUserDetails* value_arg) { - user_ = value_arg ? std::make_unique(*value_arg) : nullptr; +void InternalUserCredential::set_user(const InternalUserDetails* value_arg) { + user_ = + value_arg ? std::make_unique(*value_arg) : nullptr; } -void PigeonUserCredential::set_user(const PigeonUserDetails& value_arg) { - user_ = std::make_unique(value_arg); +void InternalUserCredential::set_user(const InternalUserDetails& value_arg) { + user_ = std::make_unique(value_arg); } -const PigeonAdditionalUserInfo* PigeonUserCredential::additional_user_info() +const InternalAdditionalUserInfo* InternalUserCredential::additional_user_info() const { return additional_user_info_.get(); } -void PigeonUserCredential::set_additional_user_info( - const PigeonAdditionalUserInfo* value_arg) { +void InternalUserCredential::set_additional_user_info( + const InternalAdditionalUserInfo* value_arg) { additional_user_info_ = - value_arg ? std::make_unique(*value_arg) + value_arg ? std::make_unique(*value_arg) : nullptr; } -void PigeonUserCredential::set_additional_user_info( - const PigeonAdditionalUserInfo& value_arg) { - additional_user_info_ = std::make_unique(value_arg); +void InternalUserCredential::set_additional_user_info( + const InternalAdditionalUserInfo& value_arg) { + additional_user_info_ = + std::make_unique(value_arg); } -const PigeonAuthCredential* PigeonUserCredential::credential() const { +const InternalAuthCredential* InternalUserCredential::credential() const { return credential_.get(); } -void PigeonUserCredential::set_credential( - const PigeonAuthCredential* value_arg) { - credential_ = - value_arg ? std::make_unique(*value_arg) : nullptr; +void InternalUserCredential::set_credential( + const InternalAuthCredential* value_arg) { + credential_ = value_arg ? std::make_unique(*value_arg) + : nullptr; } -void PigeonUserCredential::set_credential( - const PigeonAuthCredential& value_arg) { - credential_ = std::make_unique(value_arg); +void InternalUserCredential::set_credential( + const InternalAuthCredential& value_arg) { + credential_ = std::make_unique(value_arg); } -EncodableList PigeonUserCredential::ToEncodableList() const { +EncodableList InternalUserCredential::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(user_ ? CustomEncodableValue(*user_) : EncodableValue()); @@ -967,38 +1441,173 @@ EncodableList PigeonUserCredential::ToEncodableList() const { return list; } -PigeonUserCredential PigeonUserCredential::FromEncodableList( +InternalUserCredential InternalUserCredential::FromEncodableList( const EncodableList& list) { - PigeonUserCredential decoded; + InternalUserCredential decoded; auto& encodable_user = list[0]; if (!encodable_user.IsNull()) { - decoded.set_user(std::any_cast( + decoded.set_user(std::any_cast( std::get(encodable_user))); } auto& encodable_additional_user_info = list[1]; if (!encodable_additional_user_info.IsNull()) { decoded.set_additional_user_info( - std::any_cast( + std::any_cast( std::get(encodable_additional_user_info))); } auto& encodable_credential = list[2]; if (!encodable_credential.IsNull()) { - decoded.set_credential(std::any_cast( + decoded.set_credential(std::any_cast( std::get(encodable_credential))); } return decoded; } -// PigeonActionCodeSettings +bool InternalUserCredential::operator==( + const InternalUserCredential& other) const { + return PigeonInternalDeepEquals(user_, other.user_) && + PigeonInternalDeepEquals(additional_user_info_, + other.additional_user_info_) && + PigeonInternalDeepEquals(credential_, other.credential_); +} + +bool InternalUserCredential::operator!=( + const InternalUserCredential& other) const { + return !(*this == other); +} + +size_t InternalUserCredential::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(user_); + result = result * 31 + PigeonInternalDeepHash(additional_user_info_); + result = result * 31 + PigeonInternalDeepHash(credential_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalUserCredential& v) { + return v.Hash(); +} + +// InternalAuthCredentialInput + +InternalAuthCredentialInput::InternalAuthCredentialInput( + const std::string& provider_id, const std::string& sign_in_method) + : provider_id_(provider_id), sign_in_method_(sign_in_method) {} + +InternalAuthCredentialInput::InternalAuthCredentialInput( + const std::string& provider_id, const std::string& sign_in_method, + const std::string* token, const std::string* access_token) + : provider_id_(provider_id), + sign_in_method_(sign_in_method), + token_(token ? std::optional(*token) : std::nullopt), + access_token_(access_token ? std::optional(*access_token) + : std::nullopt) {} + +const std::string& InternalAuthCredentialInput::provider_id() const { + return provider_id_; +} + +void InternalAuthCredentialInput::set_provider_id(std::string_view value_arg) { + provider_id_ = value_arg; +} + +const std::string& InternalAuthCredentialInput::sign_in_method() const { + return sign_in_method_; +} + +void InternalAuthCredentialInput::set_sign_in_method( + std::string_view value_arg) { + sign_in_method_ = value_arg; +} + +const std::string* InternalAuthCredentialInput::token() const { + return token_ ? &(*token_) : nullptr; +} + +void InternalAuthCredentialInput::set_token(const std::string_view* value_arg) { + token_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void InternalAuthCredentialInput::set_token(std::string_view value_arg) { + token_ = value_arg; +} + +const std::string* InternalAuthCredentialInput::access_token() const { + return access_token_ ? &(*access_token_) : nullptr; +} + +void InternalAuthCredentialInput::set_access_token( + const std::string_view* value_arg) { + access_token_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void InternalAuthCredentialInput::set_access_token(std::string_view value_arg) { + access_token_ = value_arg; +} + +EncodableList InternalAuthCredentialInput::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(EncodableValue(provider_id_)); + list.push_back(EncodableValue(sign_in_method_)); + list.push_back(token_ ? EncodableValue(*token_) : EncodableValue()); + list.push_back(access_token_ ? EncodableValue(*access_token_) + : EncodableValue()); + return list; +} + +InternalAuthCredentialInput InternalAuthCredentialInput::FromEncodableList( + const EncodableList& list) { + InternalAuthCredentialInput decoded(std::get(list[0]), + std::get(list[1])); + auto& encodable_token = list[2]; + if (!encodable_token.IsNull()) { + decoded.set_token(std::get(encodable_token)); + } + auto& encodable_access_token = list[3]; + if (!encodable_access_token.IsNull()) { + decoded.set_access_token(std::get(encodable_access_token)); + } + return decoded; +} + +bool InternalAuthCredentialInput::operator==( + const InternalAuthCredentialInput& other) const { + return PigeonInternalDeepEquals(provider_id_, other.provider_id_) && + PigeonInternalDeepEquals(sign_in_method_, other.sign_in_method_) && + PigeonInternalDeepEquals(token_, other.token_) && + PigeonInternalDeepEquals(access_token_, other.access_token_); +} + +bool InternalAuthCredentialInput::operator!=( + const InternalAuthCredentialInput& other) const { + return !(*this == other); +} + +size_t InternalAuthCredentialInput::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(provider_id_); + result = result * 31 + PigeonInternalDeepHash(sign_in_method_); + result = result * 31 + PigeonInternalDeepHash(token_); + result = result * 31 + PigeonInternalDeepHash(access_token_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalAuthCredentialInput& v) { + return v.Hash(); +} + +// InternalActionCodeSettings -PigeonActionCodeSettings::PigeonActionCodeSettings(const std::string& url, - bool handle_code_in_app, - bool android_install_app) +InternalActionCodeSettings::InternalActionCodeSettings(const std::string& url, + bool handle_code_in_app, + bool android_install_app) : url_(url), handle_code_in_app_(handle_code_in_app), android_install_app_(android_install_app) {} -PigeonActionCodeSettings::PigeonActionCodeSettings( +InternalActionCodeSettings::InternalActionCodeSettings( const std::string& url, const std::string* dynamic_link_domain, bool handle_code_in_app, const std::string* i_o_s_bundle_id, const std::string* android_package_name, bool android_install_app, @@ -1022,102 +1631,103 @@ PigeonActionCodeSettings::PigeonActionCodeSettings( link_domain_(link_domain ? std::optional(*link_domain) : std::nullopt) {} -const std::string& PigeonActionCodeSettings::url() const { return url_; } +const std::string& InternalActionCodeSettings::url() const { return url_; } -void PigeonActionCodeSettings::set_url(std::string_view value_arg) { +void InternalActionCodeSettings::set_url(std::string_view value_arg) { url_ = value_arg; } -const std::string* PigeonActionCodeSettings::dynamic_link_domain() const { +const std::string* InternalActionCodeSettings::dynamic_link_domain() const { return dynamic_link_domain_ ? &(*dynamic_link_domain_) : nullptr; } -void PigeonActionCodeSettings::set_dynamic_link_domain( +void InternalActionCodeSettings::set_dynamic_link_domain( const std::string_view* value_arg) { dynamic_link_domain_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonActionCodeSettings::set_dynamic_link_domain( +void InternalActionCodeSettings::set_dynamic_link_domain( std::string_view value_arg) { dynamic_link_domain_ = value_arg; } -bool PigeonActionCodeSettings::handle_code_in_app() const { +bool InternalActionCodeSettings::handle_code_in_app() const { return handle_code_in_app_; } -void PigeonActionCodeSettings::set_handle_code_in_app(bool value_arg) { +void InternalActionCodeSettings::set_handle_code_in_app(bool value_arg) { handle_code_in_app_ = value_arg; } -const std::string* PigeonActionCodeSettings::i_o_s_bundle_id() const { +const std::string* InternalActionCodeSettings::i_o_s_bundle_id() const { return i_o_s_bundle_id_ ? &(*i_o_s_bundle_id_) : nullptr; } -void PigeonActionCodeSettings::set_i_o_s_bundle_id( +void InternalActionCodeSettings::set_i_o_s_bundle_id( const std::string_view* value_arg) { i_o_s_bundle_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonActionCodeSettings::set_i_o_s_bundle_id(std::string_view value_arg) { +void InternalActionCodeSettings::set_i_o_s_bundle_id( + std::string_view value_arg) { i_o_s_bundle_id_ = value_arg; } -const std::string* PigeonActionCodeSettings::android_package_name() const { +const std::string* InternalActionCodeSettings::android_package_name() const { return android_package_name_ ? &(*android_package_name_) : nullptr; } -void PigeonActionCodeSettings::set_android_package_name( +void InternalActionCodeSettings::set_android_package_name( const std::string_view* value_arg) { android_package_name_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonActionCodeSettings::set_android_package_name( +void InternalActionCodeSettings::set_android_package_name( std::string_view value_arg) { android_package_name_ = value_arg; } -bool PigeonActionCodeSettings::android_install_app() const { +bool InternalActionCodeSettings::android_install_app() const { return android_install_app_; } -void PigeonActionCodeSettings::set_android_install_app(bool value_arg) { +void InternalActionCodeSettings::set_android_install_app(bool value_arg) { android_install_app_ = value_arg; } -const std::string* PigeonActionCodeSettings::android_minimum_version() const { +const std::string* InternalActionCodeSettings::android_minimum_version() const { return android_minimum_version_ ? &(*android_minimum_version_) : nullptr; } -void PigeonActionCodeSettings::set_android_minimum_version( +void InternalActionCodeSettings::set_android_minimum_version( const std::string_view* value_arg) { android_minimum_version_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonActionCodeSettings::set_android_minimum_version( +void InternalActionCodeSettings::set_android_minimum_version( std::string_view value_arg) { android_minimum_version_ = value_arg; } -const std::string* PigeonActionCodeSettings::link_domain() const { +const std::string* InternalActionCodeSettings::link_domain() const { return link_domain_ ? &(*link_domain_) : nullptr; } -void PigeonActionCodeSettings::set_link_domain( +void InternalActionCodeSettings::set_link_domain( const std::string_view* value_arg) { link_domain_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonActionCodeSettings::set_link_domain(std::string_view value_arg) { +void InternalActionCodeSettings::set_link_domain(std::string_view value_arg) { link_domain_ = value_arg; } -EncodableList PigeonActionCodeSettings::ToEncodableList() const { +EncodableList InternalActionCodeSettings::ToEncodableList() const { EncodableList list; list.reserve(8); list.push_back(EncodableValue(url_)); @@ -1137,11 +1747,11 @@ EncodableList PigeonActionCodeSettings::ToEncodableList() const { return list; } -PigeonActionCodeSettings PigeonActionCodeSettings::FromEncodableList( +InternalActionCodeSettings InternalActionCodeSettings::FromEncodableList( const EncodableList& list) { - PigeonActionCodeSettings decoded(std::get(list[0]), - std::get(list[2]), - std::get(list[5])); + InternalActionCodeSettings decoded(std::get(list[0]), + std::get(list[2]), + std::get(list[5])); auto& encodable_dynamic_link_domain = list[1]; if (!encodable_dynamic_link_domain.IsNull()) { decoded.set_dynamic_link_domain( @@ -1169,14 +1779,53 @@ PigeonActionCodeSettings PigeonActionCodeSettings::FromEncodableList( return decoded; } -// PigeonFirebaseAuthSettings +bool InternalActionCodeSettings::operator==( + const InternalActionCodeSettings& other) const { + return PigeonInternalDeepEquals(url_, other.url_) && + PigeonInternalDeepEquals(dynamic_link_domain_, + other.dynamic_link_domain_) && + PigeonInternalDeepEquals(handle_code_in_app_, + other.handle_code_in_app_) && + PigeonInternalDeepEquals(i_o_s_bundle_id_, other.i_o_s_bundle_id_) && + PigeonInternalDeepEquals(android_package_name_, + other.android_package_name_) && + PigeonInternalDeepEquals(android_install_app_, + other.android_install_app_) && + PigeonInternalDeepEquals(android_minimum_version_, + other.android_minimum_version_) && + PigeonInternalDeepEquals(link_domain_, other.link_domain_); +} + +bool InternalActionCodeSettings::operator!=( + const InternalActionCodeSettings& other) const { + return !(*this == other); +} + +size_t InternalActionCodeSettings::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(url_); + result = result * 31 + PigeonInternalDeepHash(dynamic_link_domain_); + result = result * 31 + PigeonInternalDeepHash(handle_code_in_app_); + result = result * 31 + PigeonInternalDeepHash(i_o_s_bundle_id_); + result = result * 31 + PigeonInternalDeepHash(android_package_name_); + result = result * 31 + PigeonInternalDeepHash(android_install_app_); + result = result * 31 + PigeonInternalDeepHash(android_minimum_version_); + result = result * 31 + PigeonInternalDeepHash(link_domain_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalActionCodeSettings& v) { + return v.Hash(); +} + +// InternalFirebaseAuthSettings -PigeonFirebaseAuthSettings::PigeonFirebaseAuthSettings( +InternalFirebaseAuthSettings::InternalFirebaseAuthSettings( bool app_verification_disabled_for_testing) : app_verification_disabled_for_testing_( app_verification_disabled_for_testing) {} -PigeonFirebaseAuthSettings::PigeonFirebaseAuthSettings( +InternalFirebaseAuthSettings::InternalFirebaseAuthSettings( bool app_verification_disabled_for_testing, const std::string* user_access_group, const std::string* phone_number, const std::string* sms_code, const bool* force_recaptcha_flow) @@ -1193,72 +1842,74 @@ PigeonFirebaseAuthSettings::PigeonFirebaseAuthSettings( ? std::optional(*force_recaptcha_flow) : std::nullopt) {} -bool PigeonFirebaseAuthSettings::app_verification_disabled_for_testing() const { +bool InternalFirebaseAuthSettings::app_verification_disabled_for_testing() + const { return app_verification_disabled_for_testing_; } -void PigeonFirebaseAuthSettings::set_app_verification_disabled_for_testing( +void InternalFirebaseAuthSettings::set_app_verification_disabled_for_testing( bool value_arg) { app_verification_disabled_for_testing_ = value_arg; } -const std::string* PigeonFirebaseAuthSettings::user_access_group() const { +const std::string* InternalFirebaseAuthSettings::user_access_group() const { return user_access_group_ ? &(*user_access_group_) : nullptr; } -void PigeonFirebaseAuthSettings::set_user_access_group( +void InternalFirebaseAuthSettings::set_user_access_group( const std::string_view* value_arg) { user_access_group_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseAuthSettings::set_user_access_group( +void InternalFirebaseAuthSettings::set_user_access_group( std::string_view value_arg) { user_access_group_ = value_arg; } -const std::string* PigeonFirebaseAuthSettings::phone_number() const { +const std::string* InternalFirebaseAuthSettings::phone_number() const { return phone_number_ ? &(*phone_number_) : nullptr; } -void PigeonFirebaseAuthSettings::set_phone_number( +void InternalFirebaseAuthSettings::set_phone_number( const std::string_view* value_arg) { phone_number_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseAuthSettings::set_phone_number(std::string_view value_arg) { +void InternalFirebaseAuthSettings::set_phone_number( + std::string_view value_arg) { phone_number_ = value_arg; } -const std::string* PigeonFirebaseAuthSettings::sms_code() const { +const std::string* InternalFirebaseAuthSettings::sms_code() const { return sms_code_ ? &(*sms_code_) : nullptr; } -void PigeonFirebaseAuthSettings::set_sms_code( +void InternalFirebaseAuthSettings::set_sms_code( const std::string_view* value_arg) { sms_code_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseAuthSettings::set_sms_code(std::string_view value_arg) { +void InternalFirebaseAuthSettings::set_sms_code(std::string_view value_arg) { sms_code_ = value_arg; } -const bool* PigeonFirebaseAuthSettings::force_recaptcha_flow() const { +const bool* InternalFirebaseAuthSettings::force_recaptcha_flow() const { return force_recaptcha_flow_ ? &(*force_recaptcha_flow_) : nullptr; } -void PigeonFirebaseAuthSettings::set_force_recaptcha_flow( +void InternalFirebaseAuthSettings::set_force_recaptcha_flow( const bool* value_arg) { force_recaptcha_flow_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFirebaseAuthSettings::set_force_recaptcha_flow(bool value_arg) { +void InternalFirebaseAuthSettings::set_force_recaptcha_flow(bool value_arg) { force_recaptcha_flow_ = value_arg; } -EncodableList PigeonFirebaseAuthSettings::ToEncodableList() const { +EncodableList InternalFirebaseAuthSettings::ToEncodableList() const { EncodableList list; list.reserve(5); list.push_back(EncodableValue(app_verification_disabled_for_testing_)); @@ -1272,9 +1923,9 @@ EncodableList PigeonFirebaseAuthSettings::ToEncodableList() const { return list; } -PigeonFirebaseAuthSettings PigeonFirebaseAuthSettings::FromEncodableList( +InternalFirebaseAuthSettings InternalFirebaseAuthSettings::FromEncodableList( const EncodableList& list) { - PigeonFirebaseAuthSettings decoded(std::get(list[0])); + InternalFirebaseAuthSettings decoded(std::get(list[0])); auto& encodable_user_access_group = list[1]; if (!encodable_user_access_group.IsNull()) { decoded.set_user_access_group( @@ -1296,12 +1947,45 @@ PigeonFirebaseAuthSettings PigeonFirebaseAuthSettings::FromEncodableList( return decoded; } -// PigeonSignInProvider +bool InternalFirebaseAuthSettings::operator==( + const InternalFirebaseAuthSettings& other) const { + return PigeonInternalDeepEquals( + app_verification_disabled_for_testing_, + other.app_verification_disabled_for_testing_) && + PigeonInternalDeepEquals(user_access_group_, + other.user_access_group_) && + PigeonInternalDeepEquals(phone_number_, other.phone_number_) && + PigeonInternalDeepEquals(sms_code_, other.sms_code_) && + PigeonInternalDeepEquals(force_recaptcha_flow_, + other.force_recaptcha_flow_); +} + +bool InternalFirebaseAuthSettings::operator!=( + const InternalFirebaseAuthSettings& other) const { + return !(*this == other); +} + +size_t InternalFirebaseAuthSettings::Hash() const { + size_t result = 1; + result = result * 31 + + PigeonInternalDeepHash(app_verification_disabled_for_testing_); + result = result * 31 + PigeonInternalDeepHash(user_access_group_); + result = result * 31 + PigeonInternalDeepHash(phone_number_); + result = result * 31 + PigeonInternalDeepHash(sms_code_); + result = result * 31 + PigeonInternalDeepHash(force_recaptcha_flow_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalFirebaseAuthSettings& v) { + return v.Hash(); +} + +// InternalSignInProvider -PigeonSignInProvider::PigeonSignInProvider(const std::string& provider_id) +InternalSignInProvider::InternalSignInProvider(const std::string& provider_id) : provider_id_(provider_id) {} -PigeonSignInProvider::PigeonSignInProvider( +InternalSignInProvider::InternalSignInProvider( const std::string& provider_id, const EncodableList* scopes, const EncodableMap* custom_parameters) : provider_id_(provider_id), @@ -1310,42 +1994,42 @@ PigeonSignInProvider::PigeonSignInProvider( ? std::optional(*custom_parameters) : std::nullopt) {} -const std::string& PigeonSignInProvider::provider_id() const { +const std::string& InternalSignInProvider::provider_id() const { return provider_id_; } -void PigeonSignInProvider::set_provider_id(std::string_view value_arg) { +void InternalSignInProvider::set_provider_id(std::string_view value_arg) { provider_id_ = value_arg; } -const EncodableList* PigeonSignInProvider::scopes() const { +const EncodableList* InternalSignInProvider::scopes() const { return scopes_ ? &(*scopes_) : nullptr; } -void PigeonSignInProvider::set_scopes(const EncodableList* value_arg) { +void InternalSignInProvider::set_scopes(const EncodableList* value_arg) { scopes_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSignInProvider::set_scopes(const EncodableList& value_arg) { +void InternalSignInProvider::set_scopes(const EncodableList& value_arg) { scopes_ = value_arg; } -const EncodableMap* PigeonSignInProvider::custom_parameters() const { +const EncodableMap* InternalSignInProvider::custom_parameters() const { return custom_parameters_ ? &(*custom_parameters_) : nullptr; } -void PigeonSignInProvider::set_custom_parameters( +void InternalSignInProvider::set_custom_parameters( const EncodableMap* value_arg) { custom_parameters_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSignInProvider::set_custom_parameters( +void InternalSignInProvider::set_custom_parameters( const EncodableMap& value_arg) { custom_parameters_ = value_arg; } -EncodableList PigeonSignInProvider::ToEncodableList() const { +EncodableList InternalSignInProvider::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(EncodableValue(provider_id_)); @@ -1355,9 +2039,9 @@ EncodableList PigeonSignInProvider::ToEncodableList() const { return list; } -PigeonSignInProvider PigeonSignInProvider::FromEncodableList( +InternalSignInProvider InternalSignInProvider::FromEncodableList( const EncodableList& list) { - PigeonSignInProvider decoded(std::get(list[0])); + InternalSignInProvider decoded(std::get(list[0])); auto& encodable_scopes = list[1]; if (!encodable_scopes.IsNull()) { decoded.set_scopes(std::get(encodable_scopes)); @@ -1370,12 +2054,37 @@ PigeonSignInProvider PigeonSignInProvider::FromEncodableList( return decoded; } -// PigeonVerifyPhoneNumberRequest +bool InternalSignInProvider::operator==( + const InternalSignInProvider& other) const { + return PigeonInternalDeepEquals(provider_id_, other.provider_id_) && + PigeonInternalDeepEquals(scopes_, other.scopes_) && + PigeonInternalDeepEquals(custom_parameters_, other.custom_parameters_); +} + +bool InternalSignInProvider::operator!=( + const InternalSignInProvider& other) const { + return !(*this == other); +} + +size_t InternalSignInProvider::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(provider_id_); + result = result * 31 + PigeonInternalDeepHash(scopes_); + result = result * 31 + PigeonInternalDeepHash(custom_parameters_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalSignInProvider& v) { + return v.Hash(); +} + +// InternalVerifyPhoneNumberRequest -PigeonVerifyPhoneNumberRequest::PigeonVerifyPhoneNumberRequest(int64_t timeout) +InternalVerifyPhoneNumberRequest::InternalVerifyPhoneNumberRequest( + int64_t timeout) : timeout_(timeout) {} -PigeonVerifyPhoneNumberRequest::PigeonVerifyPhoneNumberRequest( +InternalVerifyPhoneNumberRequest::InternalVerifyPhoneNumberRequest( const std::string* phone_number, int64_t timeout, const int64_t* force_resending_token, const std::string* auto_retrieved_sms_code_for_testing, @@ -1399,93 +2108,93 @@ PigeonVerifyPhoneNumberRequest::PigeonVerifyPhoneNumberRequest( ? std::optional(*multi_factor_session_id) : std::nullopt) {} -const std::string* PigeonVerifyPhoneNumberRequest::phone_number() const { +const std::string* InternalVerifyPhoneNumberRequest::phone_number() const { return phone_number_ ? &(*phone_number_) : nullptr; } -void PigeonVerifyPhoneNumberRequest::set_phone_number( +void InternalVerifyPhoneNumberRequest::set_phone_number( const std::string_view* value_arg) { phone_number_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonVerifyPhoneNumberRequest::set_phone_number( +void InternalVerifyPhoneNumberRequest::set_phone_number( std::string_view value_arg) { phone_number_ = value_arg; } -int64_t PigeonVerifyPhoneNumberRequest::timeout() const { return timeout_; } +int64_t InternalVerifyPhoneNumberRequest::timeout() const { return timeout_; } -void PigeonVerifyPhoneNumberRequest::set_timeout(int64_t value_arg) { +void InternalVerifyPhoneNumberRequest::set_timeout(int64_t value_arg) { timeout_ = value_arg; } -const int64_t* PigeonVerifyPhoneNumberRequest::force_resending_token() const { +const int64_t* InternalVerifyPhoneNumberRequest::force_resending_token() const { return force_resending_token_ ? &(*force_resending_token_) : nullptr; } -void PigeonVerifyPhoneNumberRequest::set_force_resending_token( +void InternalVerifyPhoneNumberRequest::set_force_resending_token( const int64_t* value_arg) { force_resending_token_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonVerifyPhoneNumberRequest::set_force_resending_token( +void InternalVerifyPhoneNumberRequest::set_force_resending_token( int64_t value_arg) { force_resending_token_ = value_arg; } const std::string* -PigeonVerifyPhoneNumberRequest::auto_retrieved_sms_code_for_testing() const { +InternalVerifyPhoneNumberRequest::auto_retrieved_sms_code_for_testing() const { return auto_retrieved_sms_code_for_testing_ ? &(*auto_retrieved_sms_code_for_testing_) : nullptr; } -void PigeonVerifyPhoneNumberRequest::set_auto_retrieved_sms_code_for_testing( +void InternalVerifyPhoneNumberRequest::set_auto_retrieved_sms_code_for_testing( const std::string_view* value_arg) { auto_retrieved_sms_code_for_testing_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonVerifyPhoneNumberRequest::set_auto_retrieved_sms_code_for_testing( +void InternalVerifyPhoneNumberRequest::set_auto_retrieved_sms_code_for_testing( std::string_view value_arg) { auto_retrieved_sms_code_for_testing_ = value_arg; } -const std::string* PigeonVerifyPhoneNumberRequest::multi_factor_info_id() +const std::string* InternalVerifyPhoneNumberRequest::multi_factor_info_id() const { return multi_factor_info_id_ ? &(*multi_factor_info_id_) : nullptr; } -void PigeonVerifyPhoneNumberRequest::set_multi_factor_info_id( +void InternalVerifyPhoneNumberRequest::set_multi_factor_info_id( const std::string_view* value_arg) { multi_factor_info_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonVerifyPhoneNumberRequest::set_multi_factor_info_id( +void InternalVerifyPhoneNumberRequest::set_multi_factor_info_id( std::string_view value_arg) { multi_factor_info_id_ = value_arg; } -const std::string* PigeonVerifyPhoneNumberRequest::multi_factor_session_id() +const std::string* InternalVerifyPhoneNumberRequest::multi_factor_session_id() const { return multi_factor_session_id_ ? &(*multi_factor_session_id_) : nullptr; } -void PigeonVerifyPhoneNumberRequest::set_multi_factor_session_id( +void InternalVerifyPhoneNumberRequest::set_multi_factor_session_id( const std::string_view* value_arg) { multi_factor_session_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonVerifyPhoneNumberRequest::set_multi_factor_session_id( +void InternalVerifyPhoneNumberRequest::set_multi_factor_session_id( std::string_view value_arg) { multi_factor_session_id_ = value_arg; } -EncodableList PigeonVerifyPhoneNumberRequest::ToEncodableList() const { +EncodableList InternalVerifyPhoneNumberRequest::ToEncodableList() const { EncodableList list; list.reserve(6); list.push_back(phone_number_ ? EncodableValue(*phone_number_) @@ -1505,9 +2214,9 @@ EncodableList PigeonVerifyPhoneNumberRequest::ToEncodableList() const { return list; } -PigeonVerifyPhoneNumberRequest -PigeonVerifyPhoneNumberRequest::FromEncodableList(const EncodableList& list) { - PigeonVerifyPhoneNumberRequest decoded(list[1].LongValue()); +InternalVerifyPhoneNumberRequest +InternalVerifyPhoneNumberRequest::FromEncodableList(const EncodableList& list) { + InternalVerifyPhoneNumberRequest decoded(std::get(list[1])); auto& encodable_phone_number = list[0]; if (!encodable_phone_number.IsNull()) { decoded.set_phone_number(std::get(encodable_phone_number)); @@ -1515,7 +2224,7 @@ PigeonVerifyPhoneNumberRequest::FromEncodableList(const EncodableList& list) { auto& encodable_force_resending_token = list[2]; if (!encodable_force_resending_token.IsNull()) { decoded.set_force_resending_token( - encodable_force_resending_token.LongValue()); + std::get(encodable_force_resending_token)); } auto& encodable_auto_retrieved_sms_code_for_testing = list[3]; if (!encodable_auto_retrieved_sms_code_for_testing.IsNull()) { @@ -1535,11 +2244,46 @@ PigeonVerifyPhoneNumberRequest::FromEncodableList(const EncodableList& list) { return decoded; } -// PigeonIdTokenResult +bool InternalVerifyPhoneNumberRequest::operator==( + const InternalVerifyPhoneNumberRequest& other) const { + return PigeonInternalDeepEquals(phone_number_, other.phone_number_) && + PigeonInternalDeepEquals(timeout_, other.timeout_) && + PigeonInternalDeepEquals(force_resending_token_, + other.force_resending_token_) && + PigeonInternalDeepEquals(auto_retrieved_sms_code_for_testing_, + other.auto_retrieved_sms_code_for_testing_) && + PigeonInternalDeepEquals(multi_factor_info_id_, + other.multi_factor_info_id_) && + PigeonInternalDeepEquals(multi_factor_session_id_, + other.multi_factor_session_id_); +} + +bool InternalVerifyPhoneNumberRequest::operator!=( + const InternalVerifyPhoneNumberRequest& other) const { + return !(*this == other); +} + +size_t InternalVerifyPhoneNumberRequest::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(phone_number_); + result = result * 31 + PigeonInternalDeepHash(timeout_); + result = result * 31 + PigeonInternalDeepHash(force_resending_token_); + result = result * 31 + + PigeonInternalDeepHash(auto_retrieved_sms_code_for_testing_); + result = result * 31 + PigeonInternalDeepHash(multi_factor_info_id_); + result = result * 31 + PigeonInternalDeepHash(multi_factor_session_id_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalVerifyPhoneNumberRequest& v) { + return v.Hash(); +} + +// InternalIdTokenResult -PigeonIdTokenResult::PigeonIdTokenResult() {} +InternalIdTokenResult::InternalIdTokenResult() {} -PigeonIdTokenResult::PigeonIdTokenResult( +InternalIdTokenResult::InternalIdTokenResult( const std::string* token, const int64_t* expiration_timestamp, const int64_t* auth_timestamp, const int64_t* issued_at_timestamp, const std::string* sign_in_provider, const EncodableMap* claims, @@ -1561,99 +2305,99 @@ PigeonIdTokenResult::PigeonIdTokenResult( *sign_in_second_factor) : std::nullopt) {} -const std::string* PigeonIdTokenResult::token() const { +const std::string* InternalIdTokenResult::token() const { return token_ ? &(*token_) : nullptr; } -void PigeonIdTokenResult::set_token(const std::string_view* value_arg) { +void InternalIdTokenResult::set_token(const std::string_view* value_arg) { token_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonIdTokenResult::set_token(std::string_view value_arg) { +void InternalIdTokenResult::set_token(std::string_view value_arg) { token_ = value_arg; } -const int64_t* PigeonIdTokenResult::expiration_timestamp() const { +const int64_t* InternalIdTokenResult::expiration_timestamp() const { return expiration_timestamp_ ? &(*expiration_timestamp_) : nullptr; } -void PigeonIdTokenResult::set_expiration_timestamp(const int64_t* value_arg) { +void InternalIdTokenResult::set_expiration_timestamp(const int64_t* value_arg) { expiration_timestamp_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonIdTokenResult::set_expiration_timestamp(int64_t value_arg) { +void InternalIdTokenResult::set_expiration_timestamp(int64_t value_arg) { expiration_timestamp_ = value_arg; } -const int64_t* PigeonIdTokenResult::auth_timestamp() const { +const int64_t* InternalIdTokenResult::auth_timestamp() const { return auth_timestamp_ ? &(*auth_timestamp_) : nullptr; } -void PigeonIdTokenResult::set_auth_timestamp(const int64_t* value_arg) { +void InternalIdTokenResult::set_auth_timestamp(const int64_t* value_arg) { auth_timestamp_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonIdTokenResult::set_auth_timestamp(int64_t value_arg) { +void InternalIdTokenResult::set_auth_timestamp(int64_t value_arg) { auth_timestamp_ = value_arg; } -const int64_t* PigeonIdTokenResult::issued_at_timestamp() const { +const int64_t* InternalIdTokenResult::issued_at_timestamp() const { return issued_at_timestamp_ ? &(*issued_at_timestamp_) : nullptr; } -void PigeonIdTokenResult::set_issued_at_timestamp(const int64_t* value_arg) { +void InternalIdTokenResult::set_issued_at_timestamp(const int64_t* value_arg) { issued_at_timestamp_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonIdTokenResult::set_issued_at_timestamp(int64_t value_arg) { +void InternalIdTokenResult::set_issued_at_timestamp(int64_t value_arg) { issued_at_timestamp_ = value_arg; } -const std::string* PigeonIdTokenResult::sign_in_provider() const { +const std::string* InternalIdTokenResult::sign_in_provider() const { return sign_in_provider_ ? &(*sign_in_provider_) : nullptr; } -void PigeonIdTokenResult::set_sign_in_provider( +void InternalIdTokenResult::set_sign_in_provider( const std::string_view* value_arg) { sign_in_provider_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonIdTokenResult::set_sign_in_provider(std::string_view value_arg) { +void InternalIdTokenResult::set_sign_in_provider(std::string_view value_arg) { sign_in_provider_ = value_arg; } -const EncodableMap* PigeonIdTokenResult::claims() const { +const EncodableMap* InternalIdTokenResult::claims() const { return claims_ ? &(*claims_) : nullptr; } -void PigeonIdTokenResult::set_claims(const EncodableMap* value_arg) { +void InternalIdTokenResult::set_claims(const EncodableMap* value_arg) { claims_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonIdTokenResult::set_claims(const EncodableMap& value_arg) { +void InternalIdTokenResult::set_claims(const EncodableMap& value_arg) { claims_ = value_arg; } -const std::string* PigeonIdTokenResult::sign_in_second_factor() const { +const std::string* InternalIdTokenResult::sign_in_second_factor() const { return sign_in_second_factor_ ? &(*sign_in_second_factor_) : nullptr; } -void PigeonIdTokenResult::set_sign_in_second_factor( +void InternalIdTokenResult::set_sign_in_second_factor( const std::string_view* value_arg) { sign_in_second_factor_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonIdTokenResult::set_sign_in_second_factor( +void InternalIdTokenResult::set_sign_in_second_factor( std::string_view value_arg) { sign_in_second_factor_ = value_arg; } -EncodableList PigeonIdTokenResult::ToEncodableList() const { +EncodableList InternalIdTokenResult::ToEncodableList() const { EncodableList list; list.reserve(7); list.push_back(token_ ? EncodableValue(*token_) : EncodableValue()); @@ -1672,9 +2416,9 @@ EncodableList PigeonIdTokenResult::ToEncodableList() const { return list; } -PigeonIdTokenResult PigeonIdTokenResult::FromEncodableList( +InternalIdTokenResult InternalIdTokenResult::FromEncodableList( const EncodableList& list) { - PigeonIdTokenResult decoded; + InternalIdTokenResult decoded; auto& encodable_token = list[0]; if (!encodable_token.IsNull()) { decoded.set_token(std::get(encodable_token)); @@ -1682,15 +2426,16 @@ PigeonIdTokenResult PigeonIdTokenResult::FromEncodableList( auto& encodable_expiration_timestamp = list[1]; if (!encodable_expiration_timestamp.IsNull()) { decoded.set_expiration_timestamp( - encodable_expiration_timestamp.LongValue()); + std::get(encodable_expiration_timestamp)); } auto& encodable_auth_timestamp = list[2]; if (!encodable_auth_timestamp.IsNull()) { - decoded.set_auth_timestamp(encodable_auth_timestamp.LongValue()); + decoded.set_auth_timestamp(std::get(encodable_auth_timestamp)); } auto& encodable_issued_at_timestamp = list[3]; if (!encodable_issued_at_timestamp.IsNull()) { - decoded.set_issued_at_timestamp(encodable_issued_at_timestamp.LongValue()); + decoded.set_issued_at_timestamp( + std::get(encodable_issued_at_timestamp)); } auto& encodable_sign_in_provider = list[4]; if (!encodable_sign_in_provider.IsNull()) { @@ -1709,65 +2454,102 @@ PigeonIdTokenResult PigeonIdTokenResult::FromEncodableList( return decoded; } -// PigeonUserProfile +bool InternalIdTokenResult::operator==( + const InternalIdTokenResult& other) const { + return PigeonInternalDeepEquals(token_, other.token_) && + PigeonInternalDeepEquals(expiration_timestamp_, + other.expiration_timestamp_) && + PigeonInternalDeepEquals(auth_timestamp_, other.auth_timestamp_) && + PigeonInternalDeepEquals(issued_at_timestamp_, + other.issued_at_timestamp_) && + PigeonInternalDeepEquals(sign_in_provider_, other.sign_in_provider_) && + PigeonInternalDeepEquals(claims_, other.claims_) && + PigeonInternalDeepEquals(sign_in_second_factor_, + other.sign_in_second_factor_); +} -PigeonUserProfile::PigeonUserProfile(bool display_name_changed, - bool photo_url_changed) - : display_name_changed_(display_name_changed), - photo_url_changed_(photo_url_changed) {} +bool InternalIdTokenResult::operator!=( + const InternalIdTokenResult& other) const { + return !(*this == other); +} -PigeonUserProfile::PigeonUserProfile(const std::string* display_name, - const std::string* photo_url, - bool display_name_changed, - bool photo_url_changed) - : display_name_(display_name ? std::optional(*display_name) - : std::nullopt), - photo_url_(photo_url ? std::optional(*photo_url) +size_t InternalIdTokenResult::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(token_); + result = result * 31 + PigeonInternalDeepHash(expiration_timestamp_); + result = result * 31 + PigeonInternalDeepHash(auth_timestamp_); + result = result * 31 + PigeonInternalDeepHash(issued_at_timestamp_); + result = result * 31 + PigeonInternalDeepHash(sign_in_provider_); + result = result * 31 + PigeonInternalDeepHash(claims_); + result = result * 31 + PigeonInternalDeepHash(sign_in_second_factor_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalIdTokenResult& v) { + return v.Hash(); +} + +// InternalUserProfile + +InternalUserProfile::InternalUserProfile(bool display_name_changed, + bool photo_url_changed) + : display_name_changed_(display_name_changed), + photo_url_changed_(photo_url_changed) {} + +InternalUserProfile::InternalUserProfile(const std::string* display_name, + const std::string* photo_url, + bool display_name_changed, + bool photo_url_changed) + : display_name_(display_name ? std::optional(*display_name) + : std::nullopt), + photo_url_(photo_url ? std::optional(*photo_url) : std::nullopt), display_name_changed_(display_name_changed), photo_url_changed_(photo_url_changed) {} -const std::string* PigeonUserProfile::display_name() const { +const std::string* InternalUserProfile::display_name() const { return display_name_ ? &(*display_name_) : nullptr; } -void PigeonUserProfile::set_display_name(const std::string_view* value_arg) { +void InternalUserProfile::set_display_name(const std::string_view* value_arg) { display_name_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserProfile::set_display_name(std::string_view value_arg) { +void InternalUserProfile::set_display_name(std::string_view value_arg) { display_name_ = value_arg; } -const std::string* PigeonUserProfile::photo_url() const { +const std::string* InternalUserProfile::photo_url() const { return photo_url_ ? &(*photo_url_) : nullptr; } -void PigeonUserProfile::set_photo_url(const std::string_view* value_arg) { +void InternalUserProfile::set_photo_url(const std::string_view* value_arg) { photo_url_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonUserProfile::set_photo_url(std::string_view value_arg) { +void InternalUserProfile::set_photo_url(std::string_view value_arg) { photo_url_ = value_arg; } -bool PigeonUserProfile::display_name_changed() const { +bool InternalUserProfile::display_name_changed() const { return display_name_changed_; } -void PigeonUserProfile::set_display_name_changed(bool value_arg) { +void InternalUserProfile::set_display_name_changed(bool value_arg) { display_name_changed_ = value_arg; } -bool PigeonUserProfile::photo_url_changed() const { return photo_url_changed_; } +bool InternalUserProfile::photo_url_changed() const { + return photo_url_changed_; +} -void PigeonUserProfile::set_photo_url_changed(bool value_arg) { +void InternalUserProfile::set_photo_url_changed(bool value_arg) { photo_url_changed_ = value_arg; } -EncodableList PigeonUserProfile::ToEncodableList() const { +EncodableList InternalUserProfile::ToEncodableList() const { EncodableList list; list.reserve(4); list.push_back(display_name_ ? EncodableValue(*display_name_) @@ -1778,9 +2560,9 @@ EncodableList PigeonUserProfile::ToEncodableList() const { return list; } -PigeonUserProfile PigeonUserProfile::FromEncodableList( +InternalUserProfile InternalUserProfile::FromEncodableList( const EncodableList& list) { - PigeonUserProfile decoded(std::get(list[2]), std::get(list[3])); + InternalUserProfile decoded(std::get(list[2]), std::get(list[3])); auto& encodable_display_name = list[0]; if (!encodable_display_name.IsNull()) { decoded.set_display_name(std::get(encodable_display_name)); @@ -1792,12 +2574,35 @@ PigeonUserProfile PigeonUserProfile::FromEncodableList( return decoded; } -// PigeonTotpSecret +bool InternalUserProfile::operator==(const InternalUserProfile& other) const { + return PigeonInternalDeepEquals(display_name_, other.display_name_) && + PigeonInternalDeepEquals(photo_url_, other.photo_url_) && + PigeonInternalDeepEquals(display_name_changed_, + other.display_name_changed_) && + PigeonInternalDeepEquals(photo_url_changed_, other.photo_url_changed_); +} + +bool InternalUserProfile::operator!=(const InternalUserProfile& other) const { + return !(*this == other); +} + +size_t InternalUserProfile::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(display_name_); + result = result * 31 + PigeonInternalDeepHash(photo_url_); + result = result * 31 + PigeonInternalDeepHash(display_name_changed_); + result = result * 31 + PigeonInternalDeepHash(photo_url_changed_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalUserProfile& v) { return v.Hash(); } -PigeonTotpSecret::PigeonTotpSecret(const std::string& secret_key) +// InternalTotpSecret + +InternalTotpSecret::InternalTotpSecret(const std::string& secret_key) : secret_key_(secret_key) {} -PigeonTotpSecret::PigeonTotpSecret( +InternalTotpSecret::InternalTotpSecret( const int64_t* code_interval_seconds, const int64_t* code_length, const int64_t* enrollment_completion_deadline, const std::string* hashing_algorithm, const std::string& secret_key) @@ -1815,67 +2620,69 @@ PigeonTotpSecret::PigeonTotpSecret( : std::nullopt), secret_key_(secret_key) {} -const int64_t* PigeonTotpSecret::code_interval_seconds() const { +const int64_t* InternalTotpSecret::code_interval_seconds() const { return code_interval_seconds_ ? &(*code_interval_seconds_) : nullptr; } -void PigeonTotpSecret::set_code_interval_seconds(const int64_t* value_arg) { +void InternalTotpSecret::set_code_interval_seconds(const int64_t* value_arg) { code_interval_seconds_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonTotpSecret::set_code_interval_seconds(int64_t value_arg) { +void InternalTotpSecret::set_code_interval_seconds(int64_t value_arg) { code_interval_seconds_ = value_arg; } -const int64_t* PigeonTotpSecret::code_length() const { +const int64_t* InternalTotpSecret::code_length() const { return code_length_ ? &(*code_length_) : nullptr; } -void PigeonTotpSecret::set_code_length(const int64_t* value_arg) { +void InternalTotpSecret::set_code_length(const int64_t* value_arg) { code_length_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonTotpSecret::set_code_length(int64_t value_arg) { +void InternalTotpSecret::set_code_length(int64_t value_arg) { code_length_ = value_arg; } -const int64_t* PigeonTotpSecret::enrollment_completion_deadline() const { +const int64_t* InternalTotpSecret::enrollment_completion_deadline() const { return enrollment_completion_deadline_ ? &(*enrollment_completion_deadline_) : nullptr; } -void PigeonTotpSecret::set_enrollment_completion_deadline( +void InternalTotpSecret::set_enrollment_completion_deadline( const int64_t* value_arg) { enrollment_completion_deadline_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonTotpSecret::set_enrollment_completion_deadline(int64_t value_arg) { +void InternalTotpSecret::set_enrollment_completion_deadline(int64_t value_arg) { enrollment_completion_deadline_ = value_arg; } -const std::string* PigeonTotpSecret::hashing_algorithm() const { +const std::string* InternalTotpSecret::hashing_algorithm() const { return hashing_algorithm_ ? &(*hashing_algorithm_) : nullptr; } -void PigeonTotpSecret::set_hashing_algorithm( +void InternalTotpSecret::set_hashing_algorithm( const std::string_view* value_arg) { hashing_algorithm_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonTotpSecret::set_hashing_algorithm(std::string_view value_arg) { +void InternalTotpSecret::set_hashing_algorithm(std::string_view value_arg) { hashing_algorithm_ = value_arg; } -const std::string& PigeonTotpSecret::secret_key() const { return secret_key_; } +const std::string& InternalTotpSecret::secret_key() const { + return secret_key_; +} -void PigeonTotpSecret::set_secret_key(std::string_view value_arg) { +void InternalTotpSecret::set_secret_key(std::string_view value_arg) { secret_key_ = value_arg; } -EncodableList PigeonTotpSecret::ToEncodableList() const { +EncodableList InternalTotpSecret::ToEncodableList() const { EncodableList list; list.reserve(5); list.push_back(code_interval_seconds_ @@ -1892,22 +2699,22 @@ EncodableList PigeonTotpSecret::ToEncodableList() const { return list; } -PigeonTotpSecret PigeonTotpSecret::FromEncodableList( +InternalTotpSecret InternalTotpSecret::FromEncodableList( const EncodableList& list) { - PigeonTotpSecret decoded(std::get(list[4])); + InternalTotpSecret decoded(std::get(list[4])); auto& encodable_code_interval_seconds = list[0]; if (!encodable_code_interval_seconds.IsNull()) { decoded.set_code_interval_seconds( - encodable_code_interval_seconds.LongValue()); + std::get(encodable_code_interval_seconds)); } auto& encodable_code_length = list[1]; if (!encodable_code_length.IsNull()) { - decoded.set_code_length(encodable_code_length.LongValue()); + decoded.set_code_length(std::get(encodable_code_length)); } auto& encodable_enrollment_completion_deadline = list[2]; if (!encodable_enrollment_completion_deadline.IsNull()) { decoded.set_enrollment_completion_deadline( - encodable_enrollment_completion_deadline.LongValue()); + std::get(encodable_enrollment_completion_deadline)); } auto& encodable_hashing_algorithm = list[3]; if (!encodable_hashing_algorithm.IsNull()) { @@ -1917,236 +2724,314 @@ PigeonTotpSecret PigeonTotpSecret::FromEncodableList( return decoded; } -FirebaseAuthHostApiCodecSerializer::FirebaseAuthHostApiCodecSerializer() {} +bool InternalTotpSecret::operator==(const InternalTotpSecret& other) const { + return PigeonInternalDeepEquals(code_interval_seconds_, + other.code_interval_seconds_) && + PigeonInternalDeepEquals(code_length_, other.code_length_) && + PigeonInternalDeepEquals(enrollment_completion_deadline_, + other.enrollment_completion_deadline_) && + PigeonInternalDeepEquals(hashing_algorithm_, + other.hashing_algorithm_) && + PigeonInternalDeepEquals(secret_key_, other.secret_key_); +} + +bool InternalTotpSecret::operator!=(const InternalTotpSecret& other) const { + return !(*this == other); +} + +size_t InternalTotpSecret::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(code_interval_seconds_); + result = result * 31 + PigeonInternalDeepHash(code_length_); + result = + result * 31 + PigeonInternalDeepHash(enrollment_completion_deadline_); + result = result * 31 + PigeonInternalDeepHash(hashing_algorithm_); + result = result * 31 + PigeonInternalDeepHash(secret_key_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalTotpSecret& v) { return v.Hash(); } -EncodableValue FirebaseAuthHostApiCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { +PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} + +EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { - case 128: - return CustomEncodableValue(AuthPigeonFirebaseApp::FromEncodableList( + case 129: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 130: { + return CustomEncodableValue(InternalMultiFactorSession::FromEncodableList( std::get(ReadValue(stream)))); - case 129: - return CustomEncodableValue(PigeonActionCodeInfo::FromEncodableList( + } + case 131: { + return CustomEncodableValue( + InternalPhoneMultiFactorAssertion::FromEncodableList( + std::get(ReadValue(stream)))); + } + case 132: { + return CustomEncodableValue(InternalMultiFactorInfo::FromEncodableList( std::get(ReadValue(stream)))); - case 130: - return CustomEncodableValue(PigeonActionCodeInfoData::FromEncodableList( + } + case 133: { + return CustomEncodableValue(AuthPigeonFirebaseApp::FromEncodableList( std::get(ReadValue(stream)))); - case 131: - return CustomEncodableValue(PigeonActionCodeSettings::FromEncodableList( + } + case 134: { + return CustomEncodableValue(InternalActionCodeInfoData::FromEncodableList( std::get(ReadValue(stream)))); - case 132: - return CustomEncodableValue(PigeonAdditionalUserInfo::FromEncodableList( + } + case 135: { + return CustomEncodableValue(InternalActionCodeInfo::FromEncodableList( std::get(ReadValue(stream)))); - case 133: - return CustomEncodableValue(PigeonAuthCredential::FromEncodableList( + } + case 136: { + return CustomEncodableValue(InternalAdditionalUserInfo::FromEncodableList( std::get(ReadValue(stream)))); - case 134: - return CustomEncodableValue(PigeonFirebaseAuthSettings::FromEncodableList( + } + case 137: { + return CustomEncodableValue(InternalAuthCredential::FromEncodableList( std::get(ReadValue(stream)))); - case 135: - return CustomEncodableValue(PigeonIdTokenResult::FromEncodableList( + } + case 138: { + return CustomEncodableValue(InternalUserInfo::FromEncodableList( std::get(ReadValue(stream)))); - case 136: - return CustomEncodableValue(PigeonMultiFactorInfo::FromEncodableList( + } + case 139: { + return CustomEncodableValue(InternalUserDetails::FromEncodableList( std::get(ReadValue(stream)))); - case 137: - return CustomEncodableValue(PigeonMultiFactorSession::FromEncodableList( + } + case 140: { + return CustomEncodableValue(InternalUserCredential::FromEncodableList( std::get(ReadValue(stream)))); - case 138: + } + case 141: { return CustomEncodableValue( - PigeonPhoneMultiFactorAssertion::FromEncodableList( + InternalAuthCredentialInput::FromEncodableList( std::get(ReadValue(stream)))); - case 139: - return CustomEncodableValue(PigeonSignInProvider::FromEncodableList( - std::get(ReadValue(stream)))); - case 140: - return CustomEncodableValue(PigeonTotpSecret::FromEncodableList( + } + case 142: { + return CustomEncodableValue(InternalActionCodeSettings::FromEncodableList( std::get(ReadValue(stream)))); - case 141: - return CustomEncodableValue(PigeonUserCredential::FromEncodableList( + } + case 143: { + return CustomEncodableValue( + InternalFirebaseAuthSettings::FromEncodableList( + std::get(ReadValue(stream)))); + } + case 144: { + return CustomEncodableValue(InternalSignInProvider::FromEncodableList( std::get(ReadValue(stream)))); - case 142: - return CustomEncodableValue(PigeonUserDetails::FromEncodableList( + } + case 145: { + return CustomEncodableValue( + InternalVerifyPhoneNumberRequest::FromEncodableList( + std::get(ReadValue(stream)))); + } + case 146: { + return CustomEncodableValue(InternalIdTokenResult::FromEncodableList( std::get(ReadValue(stream)))); - case 143: - return CustomEncodableValue(PigeonUserInfo::FromEncodableList( + } + case 147: { + return CustomEncodableValue(InternalUserProfile::FromEncodableList( std::get(ReadValue(stream)))); - case 144: - return CustomEncodableValue(PigeonUserProfile::FromEncodableList( + } + case 148: { + return CustomEncodableValue(InternalTotpSecret::FromEncodableList( std::get(ReadValue(stream)))); - case 145: - return CustomEncodableValue( - PigeonVerifyPhoneNumberRequest::FromEncodableList( - std::get(ReadValue(stream)))); + } default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } -void FirebaseAuthHostApiCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { +void PigeonInternalCodecSerializer::WriteValue( + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { - if (custom_value->type() == typeid(AuthPigeonFirebaseApp)) { - stream->WriteByte(128); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonActionCodeInfo)) { + if (custom_value->type() == typeid(ActionCodeInfoOperation)) { stream->WriteByte(129); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); return; } - if (custom_value->type() == typeid(PigeonActionCodeInfoData)) { + if (custom_value->type() == typeid(InternalMultiFactorSession)) { stream->WriteByte(130); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); return; } - if (custom_value->type() == typeid(PigeonActionCodeSettings)) { + if (custom_value->type() == typeid(InternalPhoneMultiFactorAssertion)) { stream->WriteByte(131); WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), + EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonAdditionalUserInfo)) { + if (custom_value->type() == typeid(InternalMultiFactorInfo)) { stream->WriteByte(132); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonAuthCredential)) { + if (custom_value->type() == typeid(AuthPigeonFirebaseApp)) { stream->WriteByte(133); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonFirebaseAuthSettings)) { + if (custom_value->type() == typeid(InternalActionCodeInfoData)) { stream->WriteByte(134); WriteValue(EncodableValue( - std::any_cast(*custom_value) + std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonIdTokenResult)) { + if (custom_value->type() == typeid(InternalActionCodeInfo)) { stream->WriteByte(135); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonMultiFactorInfo)) { + if (custom_value->type() == typeid(InternalAdditionalUserInfo)) { stream->WriteByte(136); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); return; } - if (custom_value->type() == typeid(PigeonMultiFactorSession)) { + if (custom_value->type() == typeid(InternalAuthCredential)) { stream->WriteByte(137); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonPhoneMultiFactorAssertion)) { + if (custom_value->type() == typeid(InternalUserInfo)) { stream->WriteByte(138); - WriteValue(EncodableValue(std::any_cast( - *custom_value) - .ToEncodableList()), - stream); + WriteValue( + EncodableValue( + std::any_cast(*custom_value).ToEncodableList()), + stream); return; } - if (custom_value->type() == typeid(PigeonSignInProvider)) { + if (custom_value->type() == typeid(InternalUserDetails)) { stream->WriteByte(139); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonTotpSecret)) { + if (custom_value->type() == typeid(InternalUserCredential)) { stream->WriteByte(140); WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonUserCredential)) { + if (custom_value->type() == typeid(InternalAuthCredentialInput)) { stream->WriteByte(141); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); return; } - if (custom_value->type() == typeid(PigeonUserDetails)) { + if (custom_value->type() == typeid(InternalActionCodeSettings)) { stream->WriteByte(142); - WriteValue(EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonUserInfo)) { + if (custom_value->type() == typeid(InternalFirebaseAuthSettings)) { stream->WriteByte(143); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(InternalSignInProvider)) { + stream->WriteByte(144); WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonUserProfile)) { - stream->WriteByte(144); - WriteValue(EncodableValue(std::any_cast(*custom_value) + if (custom_value->type() == typeid(InternalVerifyPhoneNumberRequest)) { + stream->WriteByte(145); + WriteValue(EncodableValue(std::any_cast( + *custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonVerifyPhoneNumberRequest)) { - stream->WriteByte(145); - WriteValue(EncodableValue(std::any_cast( - *custom_value) + if (custom_value->type() == typeid(InternalIdTokenResult)) { + stream->WriteByte(146); + WriteValue( + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(InternalUserProfile)) { + stream->WriteByte(147); + WriteValue( + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(InternalTotpSecret)) { + stream->WriteByte(148); + WriteValue(EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } } - flutter::StandardCodecSerializer::WriteValue(value, stream); + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebaseAuthHostApi. -const flutter::StandardMessageCodec& FirebaseAuthHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &FirebaseAuthHostApiCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& FirebaseAuthHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseAuthHostApi` to handle messages through the // `binary_messenger`. -void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void FirebaseAuthHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAuthHostApi* api) { FirebaseAuthHostApi::SetUp(binary_messenger, api, ""); } -void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void FirebaseAuthHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAuthHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -2163,7 +3048,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2202,7 +3087,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2240,7 +3125,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2290,7 +3175,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2335,7 +3220,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2353,7 +3238,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& code_arg = std::get(encodable_code_arg); api->CheckActionCode( app_arg, code_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2381,7 +3266,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2433,7 +3318,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2459,7 +3344,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_password_arg); api->CreateUserWithEmailAndPassword( app_arg, email_arg, password_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2487,7 +3372,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2498,7 +3383,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& app_arg = std::any_cast( std::get(encodable_app_arg)); api->SignInAnonymously( - app_arg, [reply](ErrorOr&& output) { + app_arg, [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2526,7 +3411,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2545,7 +3430,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_input_arg); api->SignInWithCredential( app_arg, input_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2573,7 +3458,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2592,7 +3477,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_token_arg); api->SignInWithCustomToken( app_arg, token_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2620,7 +3505,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2646,7 +3531,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_password_arg); api->SignInWithEmailAndPassword( app_arg, email_arg, password_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2674,7 +3559,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2700,7 +3585,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_email_link_arg); api->SignInWithEmailLink( app_arg, email_arg, email_link_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2728,7 +3613,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2744,12 +3629,12 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& sign_in_provider_arg = - std::any_cast( + std::any_cast( std::get( encodable_sign_in_provider_arg)); api->SignInWithProvider( app_arg, sign_in_provider_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -2776,7 +3661,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2814,7 +3699,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2860,7 +3745,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2878,16 +3763,12 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& email_arg = std::get(encodable_email_arg); const auto& encodable_action_code_settings_arg = args.at(2); - // IF CODE REGENERATED, PLEASE REINSERT THIS. IF ARG IS NULL, APP - // CRASHES - const PigeonActionCodeSettings* action_code_settings_arg = - nullptr; - if (!encodable_action_code_settings_arg.IsNull()) { - action_code_settings_arg = - &(std::any_cast( - std::get( - encodable_action_code_settings_arg))); - } + const auto* action_code_settings_arg = + encodable_action_code_settings_arg.IsNull() + ? nullptr + : &(std::any_cast( + std::get( + encodable_action_code_settings_arg))); api->SendPasswordResetEmail( app_arg, email_arg, action_code_settings_arg, [reply](std::optional&& output) { @@ -2917,7 +3798,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -2940,7 +3821,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& action_code_settings_arg = - std::any_cast( + std::any_cast( std::get( encodable_action_code_settings_arg)); api->SendSignInLinkToEmail( @@ -2972,7 +3853,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3013,7 +3894,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3029,7 +3910,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& settings_arg = - std::any_cast( + std::any_cast( std::get(encodable_settings_arg)); api->SetSettings(app_arg, settings_arg, [reply](std::optional&& output) { @@ -3059,7 +3940,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3104,7 +3985,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3120,7 +4001,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& request_arg = - std::any_cast( + std::any_cast( std::get(encodable_request_arg)); api->VerifyPhoneNumber( app_arg, request_arg, [reply](ErrorOr&& output) { @@ -3151,7 +4032,7 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3187,6 +4068,90 @@ void FirebaseAuthHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_auth_platform_interface." + "FirebaseAuthHostApi.revokeAccessToken" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_arg = args.at(0); + if (encodable_app_arg.IsNull()) { + reply(WrapError("app_arg unexpectedly null.")); + return; + } + const auto& app_arg = std::any_cast( + std::get(encodable_app_arg)); + const auto& encodable_access_token_arg = args.at(1); + if (encodable_access_token_arg.IsNull()) { + reply(WrapError("access_token_arg unexpectedly null.")); + return; + } + const auto& access_token_arg = + std::get(encodable_access_token_arg); + api->RevokeAccessToken( + app_arg, access_token_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_auth_platform_interface." + "FirebaseAuthHostApi.initializeRecaptchaConfig" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_arg = args.at(0); + if (encodable_app_arg.IsNull()) { + reply(WrapError("app_arg unexpectedly null.")); + return; + } + const auto& app_arg = std::any_cast( + std::get(encodable_app_arg)); + api->InitializeRecaptchaConfig( + app_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } } EncodableValue FirebaseAuthHostApi::WrapError(std::string_view error_message) { @@ -3201,239 +4166,23 @@ EncodableValue FirebaseAuthHostApi::WrapError(const FlutterError& error) { error.details()}); } -FirebaseAuthUserHostApiCodecSerializer:: - FirebaseAuthUserHostApiCodecSerializer() {} - -EncodableValue FirebaseAuthUserHostApiCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { - switch (type) { - case 128: - return CustomEncodableValue(AuthPigeonFirebaseApp::FromEncodableList( - std::get(ReadValue(stream)))); - case 129: - return CustomEncodableValue(PigeonActionCodeInfo::FromEncodableList( - std::get(ReadValue(stream)))); - case 130: - return CustomEncodableValue(PigeonActionCodeInfoData::FromEncodableList( - std::get(ReadValue(stream)))); - case 131: - return CustomEncodableValue(PigeonActionCodeSettings::FromEncodableList( - std::get(ReadValue(stream)))); - case 132: - return CustomEncodableValue(PigeonAdditionalUserInfo::FromEncodableList( - std::get(ReadValue(stream)))); - case 133: - return CustomEncodableValue(PigeonAuthCredential::FromEncodableList( - std::get(ReadValue(stream)))); - case 134: - return CustomEncodableValue(PigeonFirebaseAuthSettings::FromEncodableList( - std::get(ReadValue(stream)))); - case 135: - return CustomEncodableValue(PigeonIdTokenResult::FromEncodableList( - std::get(ReadValue(stream)))); - case 136: - return CustomEncodableValue(PigeonMultiFactorInfo::FromEncodableList( - std::get(ReadValue(stream)))); - case 137: - return CustomEncodableValue(PigeonMultiFactorSession::FromEncodableList( - std::get(ReadValue(stream)))); - case 138: - return CustomEncodableValue( - PigeonPhoneMultiFactorAssertion::FromEncodableList( - std::get(ReadValue(stream)))); - case 139: - return CustomEncodableValue(PigeonSignInProvider::FromEncodableList( - std::get(ReadValue(stream)))); - case 140: - return CustomEncodableValue(PigeonTotpSecret::FromEncodableList( - std::get(ReadValue(stream)))); - case 141: - return CustomEncodableValue(PigeonUserCredential::FromEncodableList( - std::get(ReadValue(stream)))); - case 142: - return CustomEncodableValue(PigeonUserDetails::FromEncodableList( - std::get(ReadValue(stream)))); - case 143: - return CustomEncodableValue(PigeonUserInfo::FromEncodableList( - std::get(ReadValue(stream)))); - case 144: - return CustomEncodableValue(PigeonUserProfile::FromEncodableList( - std::get(ReadValue(stream)))); - case 145: - return CustomEncodableValue( - PigeonVerifyPhoneNumberRequest::FromEncodableList( - std::get(ReadValue(stream)))); - default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } -} - -void FirebaseAuthUserHostApiCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = - std::get_if(&value)) { - if (custom_value->type() == typeid(AuthPigeonFirebaseApp)) { - stream->WriteByte(128); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonActionCodeInfo)) { - stream->WriteByte(129); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonActionCodeInfoData)) { - stream->WriteByte(130); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonActionCodeSettings)) { - stream->WriteByte(131); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonAdditionalUserInfo)) { - stream->WriteByte(132); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonAuthCredential)) { - stream->WriteByte(133); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonFirebaseAuthSettings)) { - stream->WriteByte(134); - WriteValue(EncodableValue( - std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonIdTokenResult)) { - stream->WriteByte(135); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonMultiFactorInfo)) { - stream->WriteByte(136); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonMultiFactorSession)) { - stream->WriteByte(137); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonPhoneMultiFactorAssertion)) { - stream->WriteByte(138); - WriteValue(EncodableValue(std::any_cast( - *custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonSignInProvider)) { - stream->WriteByte(139); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonTotpSecret)) { - stream->WriteByte(140); - WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonUserCredential)) { - stream->WriteByte(141); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonUserDetails)) { - stream->WriteByte(142); - WriteValue(EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonUserInfo)) { - stream->WriteByte(143); - WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonUserProfile)) { - stream->WriteByte(144); - WriteValue(EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonVerifyPhoneNumberRequest)) { - stream->WriteByte(145); - WriteValue(EncodableValue(std::any_cast( - *custom_value) - .ToEncodableList()), - stream); - return; - } - } - flutter::StandardCodecSerializer::WriteValue(value, stream); -} - /// The codec used by FirebaseAuthUserHostApi. -const flutter::StandardMessageCodec& FirebaseAuthUserHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &FirebaseAuthUserHostApiCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& FirebaseAuthUserHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseAuthUserHostApi` to handle messages through // the `binary_messenger`. -void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseAuthUserHostApi* api) { +void FirebaseAuthUserHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseAuthUserHostApi* api) { FirebaseAuthUserHostApi::SetUp(binary_messenger, api, ""); } -void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseAuthUserHostApi* api, - const std::string& message_channel_suffix) { +void FirebaseAuthUserHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, FirebaseAuthUserHostApi* api, + const std::string& message_channel_suffix) { const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix @@ -3447,7 +4196,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3485,7 +4234,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3503,7 +4252,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& force_refresh_arg = std::get(encodable_force_refresh_arg); api->GetIdToken(app_arg, force_refresh_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3531,7 +4280,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3550,7 +4299,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_input_arg); api->LinkWithCredential( app_arg, input_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3578,7 +4327,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3594,12 +4343,12 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& sign_in_provider_arg = - std::any_cast( + std::any_cast( std::get( encodable_sign_in_provider_arg)); api->LinkWithProvider( app_arg, sign_in_provider_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3627,7 +4376,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3646,7 +4395,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_input_arg); api->ReauthenticateWithCredential( app_arg, input_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3674,7 +4423,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3690,12 +4439,12 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& sign_in_provider_arg = - std::any_cast( + std::any_cast( std::get( encodable_sign_in_provider_arg)); api->ReauthenticateWithProvider( app_arg, sign_in_provider_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3722,7 +4471,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3733,7 +4482,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& app_arg = std::any_cast( std::get(encodable_app_arg)); api->Reload( - app_arg, [reply](ErrorOr&& output) { + app_arg, [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3761,7 +4510,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3772,16 +4521,12 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& app_arg = std::any_cast( std::get(encodable_app_arg)); const auto& encodable_action_code_settings_arg = args.at(1); - // IF CODE REGENERATED, PLEASE REINSERT THIS. IF ARG IS NULL, APP - // CRASHES - const PigeonActionCodeSettings* action_code_settings_arg = - nullptr; - if (!encodable_action_code_settings_arg.IsNull()) { - action_code_settings_arg = - &(std::any_cast( - std::get( - encodable_action_code_settings_arg))); - } + const auto* action_code_settings_arg = + encodable_action_code_settings_arg.IsNull() + ? nullptr + : &(std::any_cast( + std::get( + encodable_action_code_settings_arg))); api->SendEmailVerification( app_arg, action_code_settings_arg, [reply](std::optional&& output) { @@ -3810,7 +4555,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3828,7 +4573,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& provider_id_arg = std::get(encodable_provider_id_arg); api->Unlink(app_arg, provider_id_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3856,7 +4601,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3874,7 +4619,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& new_email_arg = std::get(encodable_new_email_arg); api->UpdateEmail(app_arg, new_email_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3902,7 +4647,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3919,17 +4664,18 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } const auto& new_password_arg = std::get(encodable_new_password_arg); - api->UpdatePassword(app_arg, new_password_arg, - [reply](ErrorOr&& output) { - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - EncodableList wrapped; - wrapped.push_back(CustomEncodableValue( - std::move(output).TakeValue())); - reply(EncodableValue(std::move(wrapped))); - }); + api->UpdatePassword( + app_arg, new_password_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); } catch (const std::exception& exception) { reply(WrapError(exception.what())); } @@ -3948,7 +4694,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -3967,7 +4713,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_input_arg); api->UpdatePhoneNumber( app_arg, input_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -3995,7 +4741,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -4010,19 +4756,21 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, reply(WrapError("profile_arg unexpectedly null.")); return; } - const auto& profile_arg = std::any_cast( - std::get(encodable_profile_arg)); - api->UpdateProfile(app_arg, profile_arg, - [reply](ErrorOr&& output) { - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - EncodableList wrapped; - wrapped.push_back(CustomEncodableValue( - std::move(output).TakeValue())); - reply(EncodableValue(std::move(wrapped))); - }); + const auto& profile_arg = + std::any_cast( + std::get(encodable_profile_arg)); + api->UpdateProfile( + app_arg, profile_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); } catch (const std::exception& exception) { reply(WrapError(exception.what())); } @@ -4041,7 +4789,7 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -4059,16 +4807,12 @@ void FirebaseAuthUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& new_email_arg = std::get(encodable_new_email_arg); const auto& encodable_action_code_settings_arg = args.at(2); - // IF CODE REGENERATED, PLEASE REINSERT THIS. IF ARG IS NULL, APP - // CRASHES - const PigeonActionCodeSettings* action_code_settings_arg = - nullptr; - if (!encodable_action_code_settings_arg.IsNull()) { - action_code_settings_arg = - &(std::any_cast( - std::get( - encodable_action_code_settings_arg))); - } + const auto* action_code_settings_arg = + encodable_action_code_settings_arg.IsNull() + ? nullptr + : &(std::any_cast( + std::get( + encodable_action_code_settings_arg))); api->VerifyBeforeUpdateEmail( app_arg, new_email_arg, action_code_settings_arg, [reply](std::optional&& output) { @@ -4103,84 +4847,20 @@ EncodableValue FirebaseAuthUserHostApi::WrapError(const FlutterError& error) { error.details()}); } -MultiFactorUserHostApiCodecSerializer::MultiFactorUserHostApiCodecSerializer() { -} - -EncodableValue MultiFactorUserHostApiCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { - switch (type) { - case 128: - return CustomEncodableValue(AuthPigeonFirebaseApp::FromEncodableList( - std::get(ReadValue(stream)))); - case 129: - return CustomEncodableValue(PigeonMultiFactorInfo::FromEncodableList( - std::get(ReadValue(stream)))); - case 130: - return CustomEncodableValue(PigeonMultiFactorSession::FromEncodableList( - std::get(ReadValue(stream)))); - case 131: - return CustomEncodableValue( - PigeonPhoneMultiFactorAssertion::FromEncodableList( - std::get(ReadValue(stream)))); - default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } -} - -void MultiFactorUserHostApiCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = - std::get_if(&value)) { - if (custom_value->type() == typeid(AuthPigeonFirebaseApp)) { - stream->WriteByte(128); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonMultiFactorInfo)) { - stream->WriteByte(129); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonMultiFactorSession)) { - stream->WriteByte(130); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonPhoneMultiFactorAssertion)) { - stream->WriteByte(131); - WriteValue(EncodableValue(std::any_cast( - *custom_value) - .ToEncodableList()), - stream); - return; - } - } - flutter::StandardCodecSerializer::WriteValue(value, stream); -} - /// The codec used by MultiFactorUserHostApi. -const flutter::StandardMessageCodec& MultiFactorUserHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &MultiFactorUserHostApiCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& MultiFactorUserHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `MultiFactorUserHostApi` to handle messages through // the `binary_messenger`. -void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void MultiFactorUserHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorUserHostApi* api) { MultiFactorUserHostApi::SetUp(binary_messenger, api, ""); } -void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void MultiFactorUserHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorUserHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -4197,7 +4877,7 @@ void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -4213,7 +4893,7 @@ void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& assertion_arg = - std::any_cast( + std::any_cast( std::get(encodable_assertion_arg)); const auto& encodable_display_name_arg = args.at(2); const auto* display_name_arg = @@ -4246,7 +4926,7 @@ void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -4294,7 +4974,7 @@ void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -4305,7 +4985,8 @@ void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& app_arg = std::any_cast( std::get(encodable_app_arg)); api->GetSession( - app_arg, [reply](ErrorOr&& output) { + app_arg, + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -4332,7 +5013,7 @@ void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -4377,7 +5058,7 @@ void MultiFactorUserHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -4421,108 +5102,23 @@ EncodableValue MultiFactorUserHostApi::WrapError(const FlutterError& error) { error.details()}); } -MultiFactoResolverHostApiCodecSerializer:: - MultiFactoResolverHostApiCodecSerializer() {} - -EncodableValue MultiFactoResolverHostApiCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { - switch (type) { - case 128: - return CustomEncodableValue(PigeonAdditionalUserInfo::FromEncodableList( - std::get(ReadValue(stream)))); - case 129: - return CustomEncodableValue(PigeonAuthCredential::FromEncodableList( - std::get(ReadValue(stream)))); - case 130: - return CustomEncodableValue( - PigeonPhoneMultiFactorAssertion::FromEncodableList( - std::get(ReadValue(stream)))); - case 131: - return CustomEncodableValue(PigeonUserCredential::FromEncodableList( - std::get(ReadValue(stream)))); - case 132: - return CustomEncodableValue(PigeonUserDetails::FromEncodableList( - std::get(ReadValue(stream)))); - case 133: - return CustomEncodableValue(PigeonUserInfo::FromEncodableList( - std::get(ReadValue(stream)))); - default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } -} - -void MultiFactoResolverHostApiCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = - std::get_if(&value)) { - if (custom_value->type() == typeid(PigeonAdditionalUserInfo)) { - stream->WriteByte(128); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonAuthCredential)) { - stream->WriteByte(129); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonPhoneMultiFactorAssertion)) { - stream->WriteByte(130); - WriteValue(EncodableValue(std::any_cast( - *custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonUserCredential)) { - stream->WriteByte(131); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonUserDetails)) { - stream->WriteByte(132); - WriteValue(EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonUserInfo)) { - stream->WriteByte(133); - WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), - stream); - return; - } - } - flutter::StandardCodecSerializer::WriteValue(value, stream); -} - /// The codec used by MultiFactoResolverHostApi. -const flutter::StandardMessageCodec& MultiFactoResolverHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &MultiFactoResolverHostApiCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& MultiFactoResolverHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `MultiFactoResolverHostApi` to handle messages through // the `binary_messenger`. void MultiFactoResolverHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, + ::flutter::BinaryMessenger* binary_messenger, MultiFactoResolverHostApi* api) { MultiFactoResolverHostApi::SetUp(binary_messenger, api, ""); } void MultiFactoResolverHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, MultiFactoResolverHostApi* api, - const std::string& message_channel_suffix) { + ::flutter::BinaryMessenger* binary_messenger, + MultiFactoResolverHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix @@ -4535,41 +5131,44 @@ void MultiFactoResolverHostApi::SetUp( prepended_suffix, &GetCodec()); if (api != nullptr) { - channel.SetMessageHandler( - [api](const EncodableValue& message, - const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_resolver_id_arg = args.at(0); - if (encodable_resolver_id_arg.IsNull()) { - reply(WrapError("resolver_id_arg unexpectedly null.")); - return; - } - const auto& resolver_id_arg = - std::get(encodable_resolver_id_arg); - const auto& encodable_assertion_arg = args.at(1); - const auto* assertion_arg = - &(std::any_cast( - std::get(encodable_assertion_arg))); - const auto& encodable_totp_assertion_id_arg = args.at(2); - const auto* totp_assertion_id_arg = - std::get_if(&encodable_totp_assertion_id_arg); - api->ResolveSignIn( - resolver_id_arg, assertion_arg, totp_assertion_id_arg, - [reply](ErrorOr&& output) { - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - EncodableList wrapped; - wrapped.push_back( - CustomEncodableValue(std::move(output).TakeValue())); - reply(EncodableValue(std::move(wrapped))); - }); - } catch (const std::exception& exception) { - reply(WrapError(exception.what())); - } - }); + channel.SetMessageHandler([api](const EncodableValue& message, + const ::flutter::MessageReply< + EncodableValue>& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_resolver_id_arg = args.at(0); + if (encodable_resolver_id_arg.IsNull()) { + reply(WrapError("resolver_id_arg unexpectedly null.")); + return; + } + const auto& resolver_id_arg = + std::get(encodable_resolver_id_arg); + const auto& encodable_assertion_arg = args.at(1); + const auto* assertion_arg = + encodable_assertion_arg.IsNull() + ? nullptr + : &(std::any_cast( + std::get( + encodable_assertion_arg))); + const auto& encodable_totp_assertion_id_arg = args.at(2); + const auto* totp_assertion_id_arg = + std::get_if(&encodable_totp_assertion_id_arg); + api->ResolveSignIn( + resolver_id_arg, assertion_arg, totp_assertion_id_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); } else { channel.SetMessageHandler(nullptr); } @@ -4589,50 +5188,20 @@ EncodableValue MultiFactoResolverHostApi::WrapError(const FlutterError& error) { error.details()}); } -MultiFactorTotpHostApiCodecSerializer::MultiFactorTotpHostApiCodecSerializer() { -} - -EncodableValue MultiFactorTotpHostApiCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { - switch (type) { - case 128: - return CustomEncodableValue(PigeonTotpSecret::FromEncodableList( - std::get(ReadValue(stream)))); - default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } -} - -void MultiFactorTotpHostApiCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = - std::get_if(&value)) { - if (custom_value->type() == typeid(PigeonTotpSecret)) { - stream->WriteByte(128); - WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), - stream); - return; - } - } - flutter::StandardCodecSerializer::WriteValue(value, stream); -} - /// The codec used by MultiFactorTotpHostApi. -const flutter::StandardMessageCodec& MultiFactorTotpHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &MultiFactorTotpHostApiCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& MultiFactorTotpHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `MultiFactorTotpHostApi` to handle messages through // the `binary_messenger`. -void MultiFactorTotpHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void MultiFactorTotpHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpHostApi* api) { MultiFactorTotpHostApi::SetUp(binary_messenger, api, ""); } -void MultiFactorTotpHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void MultiFactorTotpHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -4649,7 +5218,7 @@ void MultiFactorTotpHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_session_id_arg = args.at(0); @@ -4660,7 +5229,8 @@ void MultiFactorTotpHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& session_id_arg = std::get(encodable_session_id_arg); api->GenerateSecret( - session_id_arg, [reply](ErrorOr&& output) { + session_id_arg, + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -4688,7 +5258,7 @@ void MultiFactorTotpHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_secret_key_arg = args.at(0); @@ -4735,7 +5305,7 @@ void MultiFactorTotpHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_enrollment_id_arg = args.at(0); @@ -4788,21 +5358,22 @@ EncodableValue MultiFactorTotpHostApi::WrapError(const FlutterError& error) { } /// The codec used by MultiFactorTotpSecretHostApi. -const flutter::StandardMessageCodec& MultiFactorTotpSecretHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &flutter::StandardCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& +MultiFactorTotpSecretHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `MultiFactorTotpSecretHostApi` to handle messages // through the `binary_messenger`. void MultiFactorTotpSecretHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, + ::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpSecretHostApi* api) { MultiFactorTotpSecretHostApi::SetUp(binary_messenger, api, ""); } void MultiFactorTotpSecretHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, + ::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpSecretHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -4819,7 +5390,7 @@ void MultiFactorTotpSecretHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_secret_key_arg = args.at(0); @@ -4865,7 +5436,7 @@ void MultiFactorTotpSecretHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_secret_key_arg = args.at(0); @@ -4916,49 +5487,20 @@ EncodableValue MultiFactorTotpSecretHostApi::WrapError( error.details()}); } -GenerateInterfacesCodecSerializer::GenerateInterfacesCodecSerializer() {} - -EncodableValue GenerateInterfacesCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { - switch (type) { - case 128: - return CustomEncodableValue(PigeonMultiFactorInfo::FromEncodableList( - std::get(ReadValue(stream)))); - default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } -} - -void GenerateInterfacesCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = - std::get_if(&value)) { - if (custom_value->type() == typeid(PigeonMultiFactorInfo)) { - stream->WriteByte(128); - WriteValue( - EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - } - flutter::StandardCodecSerializer::WriteValue(value, stream); -} - /// The codec used by GenerateInterfaces. -const flutter::StandardMessageCodec& GenerateInterfaces::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &GenerateInterfacesCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& GenerateInterfaces::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `GenerateInterfaces` to handle messages through the // `binary_messenger`. -void GenerateInterfaces::SetUp(flutter::BinaryMessenger* binary_messenger, +void GenerateInterfaces::SetUp(::flutter::BinaryMessenger* binary_messenger, GenerateInterfaces* api) { GenerateInterfaces::SetUp(binary_messenger, api, ""); } -void GenerateInterfaces::SetUp(flutter::BinaryMessenger* binary_messenger, +void GenerateInterfaces::SetUp(::flutter::BinaryMessenger* binary_messenger, GenerateInterfaces* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -4975,7 +5517,7 @@ void GenerateInterfaces::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_info_arg = args.at(0); @@ -4984,7 +5526,7 @@ void GenerateInterfaces::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& info_arg = - std::any_cast( + std::any_cast( std::get(encodable_info_arg)); std::optional output = api->PigeonInterface(info_arg); diff --git a/packages/firebase_auth/firebase_auth/windows/messages.g.h b/packages/firebase_auth/firebase_auth/windows/messages.g.h index 91fe62cbaa74..e1e1af028fbc 100644 --- a/packages/firebase_auth/firebase_auth/windows/messages.g.h +++ b/packages/firebase_auth/firebase_auth/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v19.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -68,56 +68,60 @@ class ErrorOr { // [checkActionCode]. enum class ActionCodeInfoOperation { // Unknown operation. - unknown = 0, + kUnknown = 0, // Password reset code generated via [sendPasswordResetEmail]. - passwordReset = 1, + kPasswordReset = 1, // Email verification code generated via [User.sendEmailVerification]. - verifyEmail = 2, + kVerifyEmail = 2, // Email change revocation code generated via [User.updateEmail]. - recoverEmail = 3, + kRecoverEmail = 3, // Email sign in code generated via [sendSignInLinkToEmail]. - emailSignIn = 4, + kEmailSignIn = 4, // Verify and change email code generated via [User.verifyBeforeUpdateEmail]. - verifyAndChangeEmail = 5, + kVerifyAndChangeEmail = 5, // Action code for reverting second factor addition. - revertSecondFactorAddition = 6 + kRevertSecondFactorAddition = 6 }; // Generated class from Pigeon that represents data sent in messages. -class PigeonMultiFactorSession { +class InternalMultiFactorSession { public: // Constructs an object setting all fields. - explicit PigeonMultiFactorSession(const std::string& id); + explicit InternalMultiFactorSession(const std::string& id); const std::string& id() const; void set_id(std::string_view value_arg); + bool operator==(const InternalMultiFactorSession& other) const; + bool operator!=(const InternalMultiFactorSession& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalMultiFactorSession FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonMultiFactorSession FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string id_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonPhoneMultiFactorAssertion { +class InternalPhoneMultiFactorAssertion { public: // Constructs an object setting all fields. - explicit PigeonPhoneMultiFactorAssertion( + explicit InternalPhoneMultiFactorAssertion( const std::string& verification_id, const std::string& verification_code); const std::string& verification_id() const; @@ -126,41 +130,45 @@ class PigeonPhoneMultiFactorAssertion { const std::string& verification_code() const; void set_verification_code(std::string_view value_arg); + bool operator==(const InternalPhoneMultiFactorAssertion& other) const; + bool operator!=(const InternalPhoneMultiFactorAssertion& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalPhoneMultiFactorAssertion FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonPhoneMultiFactorAssertion FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string verification_id_; std::string verification_code_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonMultiFactorInfo { +class InternalMultiFactorInfo { public: // Constructs an object setting all non-nullable fields. - explicit PigeonMultiFactorInfo(double enrollment_timestamp, - const std::string& uid); + explicit InternalMultiFactorInfo(double enrollment_timestamp, + const std::string& uid); // Constructs an object setting all fields. - explicit PigeonMultiFactorInfo(const std::string* display_name, - double enrollment_timestamp, - const std::string* factor_id, - const std::string& uid, - const std::string* phone_number); + explicit InternalMultiFactorInfo(const std::string* display_name, + double enrollment_timestamp, + const std::string* factor_id, + const std::string& uid, + const std::string* phone_number); const std::string* display_name() const; void set_display_name(const std::string_view* value_arg); @@ -180,24 +188,28 @@ class PigeonMultiFactorInfo { void set_phone_number(const std::string_view* value_arg); void set_phone_number(std::string_view value_arg); + bool operator==(const InternalMultiFactorInfo& other) const; + bool operator!=(const InternalMultiFactorInfo& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalMultiFactorInfo FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonMultiFactorInfo FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::optional display_name_; double enrollment_timestamp_; std::optional factor_id_; @@ -227,38 +239,42 @@ class AuthPigeonFirebaseApp { void set_custom_auth_domain(const std::string_view* value_arg); void set_custom_auth_domain(std::string_view value_arg); + bool operator==(const AuthPigeonFirebaseApp& other) const; + bool operator!=(const AuthPigeonFirebaseApp& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static AuthPigeonFirebaseApp FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + + private: friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string app_name_; std::optional tenant_id_; std::optional custom_auth_domain_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonActionCodeInfoData { +class InternalActionCodeInfoData { public: // Constructs an object setting all non-nullable fields. - PigeonActionCodeInfoData(); + InternalActionCodeInfoData(); // Constructs an object setting all fields. - explicit PigeonActionCodeInfoData(const std::string* email, - const std::string* previous_email); + explicit InternalActionCodeInfoData(const std::string* email, + const std::string* previous_email); const std::string* email() const; void set_email(const std::string_view* value_arg); @@ -268,82 +284,90 @@ class PigeonActionCodeInfoData { void set_previous_email(const std::string_view* value_arg); void set_previous_email(std::string_view value_arg); + bool operator==(const InternalActionCodeInfoData& other) const; + bool operator!=(const InternalActionCodeInfoData& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: - static PigeonActionCodeInfoData FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; - friend class PigeonActionCodeInfo; + static InternalActionCodeInfoData FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + friend class InternalActionCodeInfo; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::optional email_; std::optional previous_email_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonActionCodeInfo { +class InternalActionCodeInfo { public: // Constructs an object setting all fields. - explicit PigeonActionCodeInfo(const ActionCodeInfoOperation& operation, - const PigeonActionCodeInfoData& data); - - ~PigeonActionCodeInfo() = default; - PigeonActionCodeInfo(const PigeonActionCodeInfo& other); - PigeonActionCodeInfo& operator=(const PigeonActionCodeInfo& other); - PigeonActionCodeInfo(PigeonActionCodeInfo&& other) = default; - PigeonActionCodeInfo& operator=(PigeonActionCodeInfo&& other) noexcept = + explicit InternalActionCodeInfo(const ActionCodeInfoOperation& operation, + const InternalActionCodeInfoData& data); + + ~InternalActionCodeInfo() = default; + InternalActionCodeInfo(const InternalActionCodeInfo& other); + InternalActionCodeInfo& operator=(const InternalActionCodeInfo& other); + InternalActionCodeInfo(InternalActionCodeInfo&& other) = default; + InternalActionCodeInfo& operator=(InternalActionCodeInfo&& other) noexcept = default; const ActionCodeInfoOperation& operation() const; void set_operation(const ActionCodeInfoOperation& value_arg); - const PigeonActionCodeInfoData& data() const; - void set_data(const PigeonActionCodeInfoData& value_arg); + const InternalActionCodeInfoData& data() const; + void set_data(const InternalActionCodeInfoData& value_arg); + + bool operator==(const InternalActionCodeInfo& other) const; + bool operator!=(const InternalActionCodeInfo& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalActionCodeInfo FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; private: - static PigeonActionCodeInfo FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; ActionCodeInfoOperation operation_; - std::unique_ptr data_; + std::unique_ptr data_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonAdditionalUserInfo { +class InternalAdditionalUserInfo { public: // Constructs an object setting all non-nullable fields. - explicit PigeonAdditionalUserInfo(bool is_new_user); + explicit InternalAdditionalUserInfo(bool is_new_user); // Constructs an object setting all fields. - explicit PigeonAdditionalUserInfo(bool is_new_user, - const std::string* provider_id, - const std::string* username, - const std::string* authorization_code, - const flutter::EncodableMap* profile); + explicit InternalAdditionalUserInfo(bool is_new_user, + const std::string* provider_id, + const std::string* username, + const std::string* authorization_code, + const ::flutter::EncodableMap* profile); bool is_new_user() const; void set_is_new_user(bool value_arg); @@ -360,49 +384,53 @@ class PigeonAdditionalUserInfo { void set_authorization_code(const std::string_view* value_arg); void set_authorization_code(std::string_view value_arg); - const flutter::EncodableMap* profile() const; - void set_profile(const flutter::EncodableMap* value_arg); - void set_profile(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* profile() const; + void set_profile(const ::flutter::EncodableMap* value_arg); + void set_profile(const ::flutter::EncodableMap& value_arg); + + bool operator==(const InternalAdditionalUserInfo& other) const; + bool operator!=(const InternalAdditionalUserInfo& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; private: - static PigeonAdditionalUserInfo FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; - friend class PigeonUserCredential; + static InternalAdditionalUserInfo FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + friend class InternalUserCredential; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; bool is_new_user_; std::optional provider_id_; std::optional username_; std::optional authorization_code_; - std::optional profile_; + std::optional<::flutter::EncodableMap> profile_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonAuthCredential { +class InternalAuthCredential { public: // Constructs an object setting all non-nullable fields. - explicit PigeonAuthCredential(const std::string& provider_id, - const std::string& sign_in_method, - int64_t native_id); + explicit InternalAuthCredential(const std::string& provider_id, + const std::string& sign_in_method, + int64_t native_id); // Constructs an object setting all fields. - explicit PigeonAuthCredential(const std::string& provider_id, - const std::string& sign_in_method, - int64_t native_id, - const std::string* access_token); + explicit InternalAuthCredential(const std::string& provider_id, + const std::string& sign_in_method, + int64_t native_id, + const std::string* access_token); const std::string& provider_id() const; void set_provider_id(std::string_view value_arg); @@ -417,25 +445,29 @@ class PigeonAuthCredential { void set_access_token(const std::string_view* value_arg); void set_access_token(std::string_view value_arg); + bool operator==(const InternalAuthCredential& other) const; + bool operator!=(const InternalAuthCredential& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalAuthCredential FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonAuthCredential FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; - friend class PigeonUserCredential; + friend class InternalUserCredential; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string provider_id_; std::string sign_in_method_; int64_t native_id_; @@ -443,14 +475,14 @@ class PigeonAuthCredential { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonUserInfo { +class InternalUserInfo { public: // Constructs an object setting all non-nullable fields. - explicit PigeonUserInfo(const std::string& uid, bool is_anonymous, - bool is_email_verified); + explicit InternalUserInfo(const std::string& uid, bool is_anonymous, + bool is_email_verified); // Constructs an object setting all fields. - explicit PigeonUserInfo( + explicit InternalUserInfo( const std::string& uid, const std::string* email, const std::string* display_name, const std::string* photo_url, const std::string* phone_number, bool is_anonymous, @@ -503,25 +535,29 @@ class PigeonUserInfo { void set_last_sign_in_timestamp(const int64_t* value_arg); void set_last_sign_in_timestamp(int64_t value_arg); - flutter::EncodableList ToEncodableList() const; + bool operator==(const InternalUserInfo& other) const; + bool operator!=(const InternalUserInfo& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; private: - static PigeonUserInfo FromEncodableList(const flutter::EncodableList& list); - friend class PigeonUserDetails; + static InternalUserInfo FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + friend class InternalUserDetails; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string uid_; std::optional email_; std::optional display_name_; @@ -537,117 +573,178 @@ class PigeonUserInfo { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonUserDetails { +class InternalUserDetails { public: // Constructs an object setting all fields. - explicit PigeonUserDetails(const PigeonUserInfo& user_info, - const flutter::EncodableList& provider_data); + explicit InternalUserDetails(const InternalUserInfo& user_info, + const ::flutter::EncodableList& provider_data); + + ~InternalUserDetails() = default; + InternalUserDetails(const InternalUserDetails& other); + InternalUserDetails& operator=(const InternalUserDetails& other); + InternalUserDetails(InternalUserDetails&& other) = default; + InternalUserDetails& operator=(InternalUserDetails&& other) noexcept = + default; + const InternalUserInfo& user_info() const; + void set_user_info(const InternalUserInfo& value_arg); + + const ::flutter::EncodableList& provider_data() const; + void set_provider_data(const ::flutter::EncodableList& value_arg); - ~PigeonUserDetails() = default; - PigeonUserDetails(const PigeonUserDetails& other); - PigeonUserDetails& operator=(const PigeonUserDetails& other); - PigeonUserDetails(PigeonUserDetails&& other) = default; - PigeonUserDetails& operator=(PigeonUserDetails&& other) noexcept = default; - const PigeonUserInfo& user_info() const; - void set_user_info(const PigeonUserInfo& value_arg); + bool operator==(const InternalUserDetails& other) const; + bool operator!=(const InternalUserDetails& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; - const flutter::EncodableList& provider_data() const; - void set_provider_data(const flutter::EncodableList& value_arg); + private: + static InternalUserDetails FromEncodableList( + const ::flutter::EncodableList& list); - static PigeonUserDetails FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + public: + ::flutter::EncodableList ToEncodableList() const; private: - friend class PigeonUserCredential; + friend class InternalUserCredential; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; - std::unique_ptr user_info_; - flutter::EncodableList provider_data_; + friend class PigeonInternalCodecSerializer; + std::unique_ptr user_info_; + ::flutter::EncodableList provider_data_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonUserCredential { +class InternalUserCredential { public: // Constructs an object setting all non-nullable fields. - PigeonUserCredential(); + InternalUserCredential(); // Constructs an object setting all fields. - explicit PigeonUserCredential( - const PigeonUserDetails* user, - const PigeonAdditionalUserInfo* additional_user_info, - const PigeonAuthCredential* credential); - - ~PigeonUserCredential() = default; - PigeonUserCredential(const PigeonUserCredential& other); - PigeonUserCredential& operator=(const PigeonUserCredential& other); - PigeonUserCredential(PigeonUserCredential&& other) = default; - PigeonUserCredential& operator=(PigeonUserCredential&& other) noexcept = + explicit InternalUserCredential( + const InternalUserDetails* user, + const InternalAdditionalUserInfo* additional_user_info, + const InternalAuthCredential* credential); + + ~InternalUserCredential() = default; + InternalUserCredential(const InternalUserCredential& other); + InternalUserCredential& operator=(const InternalUserCredential& other); + InternalUserCredential(InternalUserCredential&& other) = default; + InternalUserCredential& operator=(InternalUserCredential&& other) noexcept = default; - const PigeonUserDetails* user() const; - void set_user(const PigeonUserDetails* value_arg); - void set_user(const PigeonUserDetails& value_arg); + const InternalUserDetails* user() const; + void set_user(const InternalUserDetails* value_arg); + void set_user(const InternalUserDetails& value_arg); + + const InternalAdditionalUserInfo* additional_user_info() const; + void set_additional_user_info(const InternalAdditionalUserInfo* value_arg); + void set_additional_user_info(const InternalAdditionalUserInfo& value_arg); + + const InternalAuthCredential* credential() const; + void set_credential(const InternalAuthCredential* value_arg); + void set_credential(const InternalAuthCredential& value_arg); + + bool operator==(const InternalUserCredential& other) const; + bool operator!=(const InternalUserCredential& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalUserCredential FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + friend class FirebaseAuthHostApi; + friend class FirebaseAuthUserHostApi; + friend class MultiFactorUserHostApi; + friend class MultiFactoResolverHostApi; + friend class MultiFactorTotpHostApi; + friend class MultiFactorTotpSecretHostApi; + friend class GenerateInterfaces; + friend class PigeonInternalCodecSerializer; + std::unique_ptr user_; + std::unique_ptr additional_user_info_; + std::unique_ptr credential_; +}; + +// Generated class from Pigeon that represents data sent in messages. +class InternalAuthCredentialInput { + public: + // Constructs an object setting all non-nullable fields. + explicit InternalAuthCredentialInput(const std::string& provider_id, + const std::string& sign_in_method); + + // Constructs an object setting all fields. + explicit InternalAuthCredentialInput(const std::string& provider_id, + const std::string& sign_in_method, + const std::string* token, + const std::string* access_token); + + const std::string& provider_id() const; + void set_provider_id(std::string_view value_arg); + + const std::string& sign_in_method() const; + void set_sign_in_method(std::string_view value_arg); + + const std::string* token() const; + void set_token(const std::string_view* value_arg); + void set_token(std::string_view value_arg); + + const std::string* access_token() const; + void set_access_token(const std::string_view* value_arg); + void set_access_token(std::string_view value_arg); - const PigeonAdditionalUserInfo* additional_user_info() const; - void set_additional_user_info(const PigeonAdditionalUserInfo* value_arg); - void set_additional_user_info(const PigeonAdditionalUserInfo& value_arg); + bool operator==(const InternalAuthCredentialInput& other) const; + bool operator!=(const InternalAuthCredentialInput& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; - const PigeonAuthCredential* credential() const; - void set_credential(const PigeonAuthCredential* value_arg); - void set_credential(const PigeonAuthCredential& value_arg); + private: + static InternalAuthCredentialInput FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; private: - static PigeonUserCredential FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; - std::unique_ptr user_; - std::unique_ptr additional_user_info_; - std::unique_ptr credential_; + friend class PigeonInternalCodecSerializer; + std::string provider_id_; + std::string sign_in_method_; + std::optional token_; + std::optional access_token_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonActionCodeSettings { +class InternalActionCodeSettings { public: // Constructs an object setting all non-nullable fields. - explicit PigeonActionCodeSettings(const std::string& url, - bool handle_code_in_app, - bool android_install_app); + explicit InternalActionCodeSettings(const std::string& url, + bool handle_code_in_app, + bool android_install_app); // Constructs an object setting all fields. - explicit PigeonActionCodeSettings(const std::string& url, - const std::string* dynamic_link_domain, - bool handle_code_in_app, - const std::string* i_o_s_bundle_id, - const std::string* android_package_name, - bool android_install_app, - const std::string* android_minimum_version, - const std::string* link_domain); + explicit InternalActionCodeSettings( + const std::string& url, const std::string* dynamic_link_domain, + bool handle_code_in_app, const std::string* i_o_s_bundle_id, + const std::string* android_package_name, bool android_install_app, + const std::string* android_minimum_version, + const std::string* link_domain); const std::string& url() const; void set_url(std::string_view value_arg); @@ -678,24 +775,28 @@ class PigeonActionCodeSettings { void set_link_domain(const std::string_view* value_arg); void set_link_domain(std::string_view value_arg); + bool operator==(const InternalActionCodeSettings& other) const; + bool operator!=(const InternalActionCodeSettings& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalActionCodeSettings FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonActionCodeSettings FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string url_; std::optional dynamic_link_domain_; bool handle_code_in_app_; @@ -707,14 +808,14 @@ class PigeonActionCodeSettings { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonFirebaseAuthSettings { +class InternalFirebaseAuthSettings { public: // Constructs an object setting all non-nullable fields. - explicit PigeonFirebaseAuthSettings( + explicit InternalFirebaseAuthSettings( bool app_verification_disabled_for_testing); // Constructs an object setting all fields. - explicit PigeonFirebaseAuthSettings( + explicit InternalFirebaseAuthSettings( bool app_verification_disabled_for_testing, const std::string* user_access_group, const std::string* phone_number, const std::string* sms_code, const bool* force_recaptcha_flow); @@ -738,24 +839,28 @@ class PigeonFirebaseAuthSettings { void set_force_recaptcha_flow(const bool* value_arg); void set_force_recaptcha_flow(bool value_arg); + bool operator==(const InternalFirebaseAuthSettings& other) const; + bool operator!=(const InternalFirebaseAuthSettings& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalFirebaseAuthSettings FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonFirebaseAuthSettings FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; bool app_verification_disabled_for_testing_; std::optional user_access_group_; std::optional phone_number_; @@ -764,58 +869,62 @@ class PigeonFirebaseAuthSettings { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonSignInProvider { +class InternalSignInProvider { public: // Constructs an object setting all non-nullable fields. - explicit PigeonSignInProvider(const std::string& provider_id); + explicit InternalSignInProvider(const std::string& provider_id); // Constructs an object setting all fields. - explicit PigeonSignInProvider(const std::string& provider_id, - const flutter::EncodableList* scopes, - const flutter::EncodableMap* custom_parameters); + explicit InternalSignInProvider( + const std::string& provider_id, const ::flutter::EncodableList* scopes, + const ::flutter::EncodableMap* custom_parameters); const std::string& provider_id() const; void set_provider_id(std::string_view value_arg); - const flutter::EncodableList* scopes() const; - void set_scopes(const flutter::EncodableList* value_arg); - void set_scopes(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList* scopes() const; + void set_scopes(const ::flutter::EncodableList* value_arg); + void set_scopes(const ::flutter::EncodableList& value_arg); - const flutter::EncodableMap* custom_parameters() const; - void set_custom_parameters(const flutter::EncodableMap* value_arg); - void set_custom_parameters(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* custom_parameters() const; + void set_custom_parameters(const ::flutter::EncodableMap* value_arg); + void set_custom_parameters(const ::flutter::EncodableMap& value_arg); + + bool operator==(const InternalSignInProvider& other) const; + bool operator!=(const InternalSignInProvider& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalSignInProvider FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; private: - static PigeonSignInProvider FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string provider_id_; - std::optional scopes_; - std::optional custom_parameters_; + std::optional<::flutter::EncodableList> scopes_; + std::optional<::flutter::EncodableMap> custom_parameters_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonVerifyPhoneNumberRequest { +class InternalVerifyPhoneNumberRequest { public: // Constructs an object setting all non-nullable fields. - explicit PigeonVerifyPhoneNumberRequest(int64_t timeout); + explicit InternalVerifyPhoneNumberRequest(int64_t timeout); // Constructs an object setting all fields. - explicit PigeonVerifyPhoneNumberRequest( + explicit InternalVerifyPhoneNumberRequest( const std::string* phone_number, int64_t timeout, const int64_t* force_resending_token, const std::string* auto_retrieved_sms_code_for_testing, @@ -846,24 +955,28 @@ class PigeonVerifyPhoneNumberRequest { void set_multi_factor_session_id(const std::string_view* value_arg); void set_multi_factor_session_id(std::string_view value_arg); + bool operator==(const InternalVerifyPhoneNumberRequest& other) const; + bool operator!=(const InternalVerifyPhoneNumberRequest& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalVerifyPhoneNumberRequest FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonVerifyPhoneNumberRequest FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::optional phone_number_; int64_t timeout_; std::optional force_resending_token_; @@ -873,19 +986,19 @@ class PigeonVerifyPhoneNumberRequest { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonIdTokenResult { +class InternalIdTokenResult { public: // Constructs an object setting all non-nullable fields. - PigeonIdTokenResult(); + InternalIdTokenResult(); // Constructs an object setting all fields. - explicit PigeonIdTokenResult(const std::string* token, - const int64_t* expiration_timestamp, - const int64_t* auth_timestamp, - const int64_t* issued_at_timestamp, - const std::string* sign_in_provider, - const flutter::EncodableMap* claims, - const std::string* sign_in_second_factor); + explicit InternalIdTokenResult(const std::string* token, + const int64_t* expiration_timestamp, + const int64_t* auth_timestamp, + const int64_t* issued_at_timestamp, + const std::string* sign_in_provider, + const ::flutter::EncodableMap* claims, + const std::string* sign_in_second_factor); const std::string* token() const; void set_token(const std::string_view* value_arg); @@ -907,51 +1020,57 @@ class PigeonIdTokenResult { void set_sign_in_provider(const std::string_view* value_arg); void set_sign_in_provider(std::string_view value_arg); - const flutter::EncodableMap* claims() const; - void set_claims(const flutter::EncodableMap* value_arg); - void set_claims(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* claims() const; + void set_claims(const ::flutter::EncodableMap* value_arg); + void set_claims(const ::flutter::EncodableMap& value_arg); const std::string* sign_in_second_factor() const; void set_sign_in_second_factor(const std::string_view* value_arg); void set_sign_in_second_factor(std::string_view value_arg); + bool operator==(const InternalIdTokenResult& other) const; + bool operator!=(const InternalIdTokenResult& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalIdTokenResult FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonIdTokenResult FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::optional token_; std::optional expiration_timestamp_; std::optional auth_timestamp_; std::optional issued_at_timestamp_; std::optional sign_in_provider_; - std::optional claims_; + std::optional<::flutter::EncodableMap> claims_; std::optional sign_in_second_factor_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonUserProfile { +class InternalUserProfile { public: // Constructs an object setting all non-nullable fields. - explicit PigeonUserProfile(bool display_name_changed, bool photo_url_changed); + explicit InternalUserProfile(bool display_name_changed, + bool photo_url_changed); // Constructs an object setting all fields. - explicit PigeonUserProfile(const std::string* display_name, - const std::string* photo_url, - bool display_name_changed, bool photo_url_changed); + explicit InternalUserProfile(const std::string* display_name, + const std::string* photo_url, + bool display_name_changed, + bool photo_url_changed); const std::string* display_name() const; void set_display_name(const std::string_view* value_arg); @@ -967,24 +1086,28 @@ class PigeonUserProfile { bool photo_url_changed() const; void set_photo_url_changed(bool value_arg); + bool operator==(const InternalUserProfile& other) const; + bool operator!=(const InternalUserProfile& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalUserProfile FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonUserProfile FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::optional display_name_; std::optional photo_url_; bool display_name_changed_; @@ -992,17 +1115,17 @@ class PigeonUserProfile { }; // Generated class from Pigeon that represents data sent in messages. -class PigeonTotpSecret { +class InternalTotpSecret { public: // Constructs an object setting all non-nullable fields. - explicit PigeonTotpSecret(const std::string& secret_key); + explicit InternalTotpSecret(const std::string& secret_key); // Constructs an object setting all fields. - explicit PigeonTotpSecret(const int64_t* code_interval_seconds, - const int64_t* code_length, - const int64_t* enrollment_completion_deadline, - const std::string* hashing_algorithm, - const std::string& secret_key); + explicit InternalTotpSecret(const int64_t* code_interval_seconds, + const int64_t* code_length, + const int64_t* enrollment_completion_deadline, + const std::string* hashing_algorithm, + const std::string& secret_key); const int64_t* code_interval_seconds() const; void set_code_interval_seconds(const int64_t* value_arg); @@ -1023,23 +1146,28 @@ class PigeonTotpSecret { const std::string& secret_key() const; void set_secret_key(std::string_view value_arg); + bool operator==(const InternalTotpSecret& other) const; + bool operator!=(const InternalTotpSecret& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalTotpSecret FromEncodableList( + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + private: - static PigeonTotpSecret FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseAuthHostApi; - friend class FirebaseAuthHostApiCodecSerializer; friend class FirebaseAuthUserHostApi; - friend class FirebaseAuthUserHostApiCodecSerializer; friend class MultiFactorUserHostApi; - friend class MultiFactorUserHostApiCodecSerializer; friend class MultiFactoResolverHostApi; - friend class MultiFactoResolverHostApiCodecSerializer; friend class MultiFactorTotpHostApi; - friend class MultiFactorTotpHostApiCodecSerializer; friend class MultiFactorTotpSecretHostApi; - friend class MultiFactorTotpSecretHostApiCodecSerializer; friend class GenerateInterfaces; - friend class GenerateInterfacesCodecSerializer; + friend class PigeonInternalCodecSerializer; std::optional code_interval_seconds_; std::optional code_length_; std::optional enrollment_completion_deadline_; @@ -1047,21 +1175,21 @@ class PigeonTotpSecret { std::string secret_key_; }; -class FirebaseAuthHostApiCodecSerializer - : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: - FirebaseAuthHostApiCodecSerializer(); - inline static FirebaseAuthHostApiCodecSerializer& GetInstance() { - static FirebaseAuthHostApiCodecSerializer sInstance; + PigeonInternalCodecSerializer(); + inline static PigeonInternalCodecSerializer& GetInstance() { + static PigeonInternalCodecSerializer sInstance; return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -1085,7 +1213,7 @@ class FirebaseAuthHostApi { std::function reply)> result) = 0; virtual void CheckActionCode( const AuthPigeonFirebaseApp& app, const std::string& code, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void ConfirmPasswordReset( const AuthPigeonFirebaseApp& app, const std::string& code, const std::string& new_password, @@ -1093,92 +1221,81 @@ class FirebaseAuthHostApi { virtual void CreateUserWithEmailAndPassword( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& password, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SignInAnonymously( const AuthPigeonFirebaseApp& app, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SignInWithCredential( - const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) = 0; + const AuthPigeonFirebaseApp& app, const ::flutter::EncodableMap& input, + std::function reply)> result) = 0; virtual void SignInWithCustomToken( const AuthPigeonFirebaseApp& app, const std::string& token, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SignInWithEmailAndPassword( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& password, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SignInWithEmailLink( const AuthPigeonFirebaseApp& app, const std::string& email, const std::string& email_link, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SignInWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) = 0; + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) = 0; virtual void SignOut( const AuthPigeonFirebaseApp& app, std::function reply)> result) = 0; virtual void FetchSignInMethodsForEmail( const AuthPigeonFirebaseApp& app, const std::string& email, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SendPasswordResetEmail( const AuthPigeonFirebaseApp& app, const std::string& email, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) = 0; virtual void SendSignInLinkToEmail( const AuthPigeonFirebaseApp& app, const std::string& email, - const PigeonActionCodeSettings& action_code_settings, + const InternalActionCodeSettings& action_code_settings, std::function reply)> result) = 0; virtual void SetLanguageCode( const AuthPigeonFirebaseApp& app, const std::string* language_code, std::function reply)> result) = 0; virtual void SetSettings( const AuthPigeonFirebaseApp& app, - const PigeonFirebaseAuthSettings& settings, + const InternalFirebaseAuthSettings& settings, std::function reply)> result) = 0; virtual void VerifyPasswordResetCode( const AuthPigeonFirebaseApp& app, const std::string& code, std::function reply)> result) = 0; virtual void VerifyPhoneNumber( const AuthPigeonFirebaseApp& app, - const PigeonVerifyPhoneNumberRequest& request, + const InternalVerifyPhoneNumberRequest& request, std::function reply)> result) = 0; virtual void RevokeTokenWithAuthorizationCode( const AuthPigeonFirebaseApp& app, const std::string& authorization_code, std::function reply)> result) = 0; + virtual void RevokeAccessToken( + const AuthPigeonFirebaseApp& app, const std::string& access_token, + std::function reply)> result) = 0; + virtual void InitializeRecaptchaConfig( + const AuthPigeonFirebaseApp& app, + std::function reply)> result) = 0; // The codec used by FirebaseAuthHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseAuthHostApi` to handle messages through the // `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAuthHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAuthHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseAuthHostApi() = default; }; -class FirebaseAuthUserHostApiCodecSerializer - : public flutter::StandardCodecSerializer { - public: - FirebaseAuthUserHostApiCodecSerializer(); - inline static FirebaseAuthUserHostApiCodecSerializer& GetInstance() { - static FirebaseAuthUserHostApiCodecSerializer sInstance; - return sInstance; - } - - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; - - protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; -}; - // Generated interface from Pigeon that represents a handler of messages from // Flutter. class FirebaseAuthUserHostApi { @@ -1191,80 +1308,63 @@ class FirebaseAuthUserHostApi { std::function reply)> result) = 0; virtual void GetIdToken( const AuthPigeonFirebaseApp& app, bool force_refresh, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void LinkWithCredential( - const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) = 0; + const AuthPigeonFirebaseApp& app, const ::flutter::EncodableMap& input, + std::function reply)> result) = 0; virtual void LinkWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) = 0; + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) = 0; virtual void ReauthenticateWithCredential( - const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) = 0; + const AuthPigeonFirebaseApp& app, const ::flutter::EncodableMap& input, + std::function reply)> result) = 0; virtual void ReauthenticateWithProvider( const AuthPigeonFirebaseApp& app, - const PigeonSignInProvider& sign_in_provider, - std::function reply)> result) = 0; + const InternalSignInProvider& sign_in_provider, + std::function reply)> result) = 0; virtual void Reload( const AuthPigeonFirebaseApp& app, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SendEmailVerification( const AuthPigeonFirebaseApp& app, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) = 0; virtual void Unlink( const AuthPigeonFirebaseApp& app, const std::string& provider_id, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void UpdateEmail( const AuthPigeonFirebaseApp& app, const std::string& new_email, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void UpdatePassword( const AuthPigeonFirebaseApp& app, const std::string& new_password, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void UpdatePhoneNumber( - const AuthPigeonFirebaseApp& app, const flutter::EncodableMap& input, - std::function reply)> result) = 0; + const AuthPigeonFirebaseApp& app, const ::flutter::EncodableMap& input, + std::function reply)> result) = 0; virtual void UpdateProfile( - const AuthPigeonFirebaseApp& app, const PigeonUserProfile& profile, - std::function reply)> result) = 0; + const AuthPigeonFirebaseApp& app, const InternalUserProfile& profile, + std::function reply)> result) = 0; virtual void VerifyBeforeUpdateEmail( const AuthPigeonFirebaseApp& app, const std::string& new_email, - const PigeonActionCodeSettings* action_code_settings, + const InternalActionCodeSettings* action_code_settings, std::function reply)> result) = 0; // The codec used by FirebaseAuthUserHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseAuthUserHostApi` to handle messages through // the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAuthUserHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAuthUserHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseAuthUserHostApi() = default; }; -class MultiFactorUserHostApiCodecSerializer - : public flutter::StandardCodecSerializer { - public: - MultiFactorUserHostApiCodecSerializer(); - inline static MultiFactorUserHostApiCodecSerializer& GetInstance() { - static MultiFactorUserHostApiCodecSerializer sInstance; - return sInstance; - } - - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; - - protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; -}; - // Generated interface from Pigeon that represents a handler of messages from // Flutter. class MultiFactorUserHostApi { @@ -1274,7 +1374,7 @@ class MultiFactorUserHostApi { virtual ~MultiFactorUserHostApi() {} virtual void EnrollPhone( const AuthPigeonFirebaseApp& app, - const PigeonPhoneMultiFactorAssertion& assertion, + const InternalPhoneMultiFactorAssertion& assertion, const std::string* display_name, std::function reply)> result) = 0; virtual void EnrollTotp( @@ -1283,46 +1383,30 @@ class MultiFactorUserHostApi { std::function reply)> result) = 0; virtual void GetSession( const AuthPigeonFirebaseApp& app, - std::function reply)> result) = 0; + std::function reply)> + result) = 0; virtual void Unenroll( const AuthPigeonFirebaseApp& app, const std::string& factor_uid, std::function reply)> result) = 0; virtual void GetEnrolledFactors( const AuthPigeonFirebaseApp& app, - std::function reply)> result) = 0; + std::function reply)> result) = 0; // The codec used by MultiFactorUserHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `MultiFactorUserHostApi` to handle messages through // the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorUserHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorUserHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: MultiFactorUserHostApi() = default; }; -class MultiFactoResolverHostApiCodecSerializer - : public flutter::StandardCodecSerializer { - public: - MultiFactoResolverHostApiCodecSerializer(); - inline static MultiFactoResolverHostApiCodecSerializer& GetInstance() { - static MultiFactoResolverHostApiCodecSerializer sInstance; - return sInstance; - } - - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; - - protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; -}; - // Generated interface from Pigeon that represents a handler of messages from // Flutter. class MultiFactoResolverHostApi { @@ -1333,42 +1417,25 @@ class MultiFactoResolverHostApi { virtual ~MultiFactoResolverHostApi() {} virtual void ResolveSignIn( const std::string& resolver_id, - const PigeonPhoneMultiFactorAssertion* assertion, + const InternalPhoneMultiFactorAssertion* assertion, const std::string* totp_assertion_id, - std::function reply)> result) = 0; + std::function reply)> result) = 0; // The codec used by MultiFactoResolverHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `MultiFactoResolverHostApi` to handle messages // through the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactoResolverHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactoResolverHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: MultiFactoResolverHostApi() = default; }; -class MultiFactorTotpHostApiCodecSerializer - : public flutter::StandardCodecSerializer { - public: - MultiFactorTotpHostApiCodecSerializer(); - inline static MultiFactorTotpHostApiCodecSerializer& GetInstance() { - static MultiFactorTotpHostApiCodecSerializer sInstance; - return sInstance; - } - - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; - - protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; -}; - // Generated interface from Pigeon that represents a handler of messages from // Flutter. class MultiFactorTotpHostApi { @@ -1378,7 +1445,7 @@ class MultiFactorTotpHostApi { virtual ~MultiFactorTotpHostApi() {} virtual void GenerateSecret( const std::string& session_id, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void GetAssertionForEnrollment( const std::string& secret_key, const std::string& one_time_password, std::function reply)> result) = 0; @@ -1387,16 +1454,16 @@ class MultiFactorTotpHostApi { std::function reply)> result) = 0; // The codec used by MultiFactorTotpHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `MultiFactorTotpHostApi` to handle messages through // the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: MultiFactorTotpHostApi() = default; @@ -1418,37 +1485,20 @@ class MultiFactorTotpSecretHostApi { std::function reply)> result) = 0; // The codec used by MultiFactorTotpSecretHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `MultiFactorTotpSecretHostApi` to handle messages // through the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpSecretHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, MultiFactorTotpSecretHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: MultiFactorTotpSecretHostApi() = default; }; -class GenerateInterfacesCodecSerializer - : public flutter::StandardCodecSerializer { - public: - GenerateInterfacesCodecSerializer(); - inline static GenerateInterfacesCodecSerializer& GetInstance() { - static GenerateInterfacesCodecSerializer sInstance; - return sInstance; - } - - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; - - protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; -}; - // Only used to generate the object interface that are use outside of the Pigeon // interface // @@ -1460,19 +1510,19 @@ class GenerateInterfaces { GenerateInterfaces& operator=(const GenerateInterfaces&) = delete; virtual ~GenerateInterfaces() {} virtual std::optional PigeonInterface( - const PigeonMultiFactorInfo& info) = 0; + const InternalMultiFactorInfo& info) = 0; // The codec used by GenerateInterfaces. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `GenerateInterfaces` to handle messages through the // `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, GenerateInterfaces* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, GenerateInterfaces* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: GenerateInterfaces() = default; diff --git a/packages/firebase_auth/firebase_auth_platform_interface/CHANGELOG.md b/packages/firebase_auth/firebase_auth_platform_interface/CHANGELOG.md index 95374b3a8bc2..bff12ebd7d1b 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/CHANGELOG.md +++ b/packages/firebase_auth/firebase_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,28 @@ +## 9.0.3 + + - Update a dependency to the latest release. + +## 9.0.2 + + - **DOCS**(auth): clarify behavior of password reset email with email enumeration protection enabled ([#18296](https://github.com/firebase/flutterfire/issues/18296)). ([0bcce87a](https://github.com/firebase/flutterfire/commit/0bcce87a17830797dae6ff3c1a8ba4ce210c2c0d)) + +## 9.0.1 + + - Update a dependency to the latest release. + +## 9.0.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + - **FEAT**(auth,android): add revokeAccessToken support for Android ([#18206](https://github.com/firebase/flutterfire/issues/18206)) ([#18207](https://github.com/firebase/flutterfire/issues/18207)). ([7e0a2227](https://github.com/firebase/flutterfire/commit/7e0a222700178a57d064c27b4ef62cefdda1e253)) + +## 8.1.9 + + - Update a dependency to the latest release. + ## 8.1.8 - **REFACTOR**: fix formatting and analysis issues across the repo ([#18124](https://github.com/firebase/flutterfire/issues/18124)). ([ab79fd93](https://github.com/firebase/flutterfire/commit/ab79fd93ee4ccfeb478687623134b1cf8ab71c74)) diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/firebase_auth_platform_interface.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/firebase_auth_platform_interface.dart index e373d6935fbf..783afa3774a6 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/firebase_auth_platform_interface.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/firebase_auth_platform_interface.dart @@ -13,10 +13,10 @@ export 'src/firebase_auth_multi_factor_exception.dart'; export 'src/id_token_result.dart'; export 'src/pigeon/messages.pigeon.dart' show - PigeonUserDetails, - PigeonUserInfo, + InternalUserDetails, + InternalUserInfo, ActionCodeInfoOperation, - PigeonIdTokenResult; + InternalIdTokenResult; export 'src/platform_interface/platform_interface_confirmation_result.dart'; export 'src/platform_interface/platform_interface_firebase_auth.dart'; export 'src/platform_interface/platform_interface_multi_factor.dart'; diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/id_token_result.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/id_token_result.dart index d58bc4f09b3d..e323a6f6c194 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/id_token_result.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/id_token_result.dart @@ -19,7 +19,7 @@ class IdTokenResult { @protected IdTokenResult(this._data); - final PigeonIdTokenResult _data; + final InternalIdTokenResult _data; /// The authentication time formatted as UTC string. This is the time the user /// authenticated (signed in) and not the time the token was refreshed. diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_firebase_auth.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_firebase_auth.dart index 187b01c1943d..c4ee96da1389 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_firebase_auth.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_firebase_auth.dart @@ -47,6 +47,11 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { _userChangesListeners = >>{}; + final List> _listenerRegistrations = >[]; + final List> _subscriptions = + >[]; + bool _isDisposed = false; + StreamController _createBroadcastStream() { return StreamController.broadcast(); } @@ -74,28 +79,6 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { /// Creates a new instance with a given [FirebaseApp]. MethodChannelFirebaseAuth({required FirebaseApp app}) : super(appInstance: app) { - _api.registerIdTokenListener(pigeonDefault).then((channelName) { - final events = EventChannel(channelName, channel.codec); - events - .receiveGuardedBroadcastStream(onError: convertPlatformException) - .listen( - (arguments) { - _handleIdTokenChangesListener(app.name, arguments); - }, - ); - }); - - _api.registerAuthStateListener(pigeonDefault).then((channelName) { - final events = EventChannel(channelName, channel.codec); - events - .receiveGuardedBroadcastStream(onError: convertPlatformException) - .listen( - (arguments) { - _handleAuthStateChangesListener(app.name, arguments); - }, - ); - }); - // Create a app instance broadcast stream for native listener events _authStateChangesListeners[app.name] = _createBroadcastStream<_ValueWrapper>(); @@ -103,6 +86,57 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { _createBroadcastStream<_ValueWrapper>(); _userChangesListeners[app.name] = _createBroadcastStream<_ValueWrapper>(); + + _listenerRegistrations.add(_registerIdTokenListener(app)); + _listenerRegistrations.add(_registerAuthStateListener(app)); + } + + Future _registerIdTokenListener(FirebaseApp app) async { + try { + final channelName = await _api.registerIdTokenListener(pigeonDefault); + if (_isDisposed) { + return; + } + + final events = EventChannel(channelName, channel.codec); + _subscriptions.add( + events + .receiveGuardedBroadcastStream(onError: convertPlatformException) + .listen((arguments) { + if (!_isDisposed) { + _handleIdTokenChangesListener(app.name, arguments); + } + }), + ); + // ignore: avoid_catches_without_on_clauses + } catch (_) { + // Silently ignore errors during listener registration. This can happen + // in test environments where the host API is not set up. + } + } + + Future _registerAuthStateListener(FirebaseApp app) async { + try { + final channelName = await _api.registerAuthStateListener(pigeonDefault); + if (_isDisposed) { + return; + } + + final events = EventChannel(channelName, channel.codec); + _subscriptions.add( + events + .receiveGuardedBroadcastStream(onError: convertPlatformException) + .listen((arguments) { + if (!_isDisposed) { + _handleAuthStateChangesListener(app.name, arguments); + } + }), + ); + // ignore: avoid_catches_without_on_clauses + } catch (_) { + // Silently ignore errors during listener registration. This can happen + // in test environments where the host API is not set up. + } } @override @@ -124,9 +158,13 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { Future _handleAuthStateChangesListener( String appName, Map arguments) async { // ignore: close_sinks - final streamController = _authStateChangesListeners[appName]!; - MethodChannelFirebaseAuth instance = - methodChannelFirebaseAuthInstances[appName]!; + final streamController = _authStateChangesListeners[appName]; + MethodChannelFirebaseAuth? instance = + methodChannelFirebaseAuthInstances[appName]; + + if (streamController == null || instance == null) { + return; + } MethodChannelMultiFactor? multiFactorInstance = _multiFactorInstances[appName]; @@ -143,8 +181,8 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { final MethodChannelUser user = MethodChannelUser( instance, multiFactorInstance, - PigeonUserDetails.decode( - [PigeonUserInfo.decode(userList[0]!), userList[1]], + InternalUserDetails.decode( + [InternalUserInfo.decode(userList[0]!), userList[1]], ), ); @@ -159,14 +197,18 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { /// to any [userChanges] stream subscribers. Future _handleIdTokenChangesListener( String appName, Map arguments) async { - final StreamController<_ValueWrapper> - // ignore: close_sinks - idTokenStreamController = _idTokenChangesListeners[appName]!; - final StreamController<_ValueWrapper> - // ignore: close_sinks - userChangesStreamController = _userChangesListeners[appName]!; - MethodChannelFirebaseAuth instance = - methodChannelFirebaseAuthInstances[appName]!; + // ignore: close_sinks + final idTokenStreamController = _idTokenChangesListeners[appName]; + // ignore: close_sinks + final userChangesStreamController = _userChangesListeners[appName]; + MethodChannelFirebaseAuth? instance = + methodChannelFirebaseAuthInstances[appName]; + + if (idTokenStreamController == null || + userChangesStreamController == null || + instance == null) { + return; + } MethodChannelMultiFactor? multiFactorInstance = _multiFactorInstances[appName]; if (multiFactorInstance == null) { @@ -183,8 +225,8 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { final MethodChannelUser user = MethodChannelUser( instance, multiFactorInstance, - PigeonUserDetails.decode( - [PigeonUserInfo.decode(userList[0]!), userList[1]], + InternalUserDetails.decode( + [InternalUserInfo.decode(userList[0]!), userList[1]], ), ); @@ -205,9 +247,32 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { }); } + @override + Future dispose() async { + _isDisposed = true; + + await Future.wait( + _listenerRegistrations.map((registration) { + return registration.catchError((_) {}); + }), + ); + + await Future.wait( + _subscriptions.map((subscription) => subscription.cancel()), + ); + _subscriptions.clear(); + + await _authStateChangesListeners.remove(app.name)?.close(); + await _idTokenChangesListeners.remove(app.name)?.close(); + await _userChangesListeners.remove(app.name)?.close(); + _multiFactorInstances.remove(app.name); + methodChannelFirebaseAuthInstances.remove(app.name); + currentUser = null; + } + @override MethodChannelFirebaseAuth setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { if (currentUser != null) { @@ -385,7 +450,7 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { final result = await _api.signInWithProvider( pigeonDefault, - PigeonSignInProvider( + InternalSignInProvider( providerId: convertedProvider.providerId, scopes: convertedProvider is OAuthProvider ? convertedProvider.scopes @@ -475,7 +540,7 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { email, actionCodeSettings == null ? null - : PigeonActionCodeSettings( + : InternalActionCodeSettings( url: actionCodeSettings.url, handleCodeInApp: actionCodeSettings.handleCodeInApp, iOSBundleId: actionCodeSettings.iOSBundleId, @@ -499,7 +564,7 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { await _api.sendSignInLinkToEmail( pigeonDefault, email, - PigeonActionCodeSettings( + InternalActionCodeSettings( url: actionCodeSettings.url, handleCodeInApp: actionCodeSettings.handleCodeInApp, iOSBundleId: actionCodeSettings.iOSBundleId, @@ -544,7 +609,7 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { try { await _api.setSettings( pigeonDefault, - PigeonFirebaseAuthSettings( + InternalFirebaseAuthSettings( appVerificationDisabledForTesting: appVerificationDisabledForTesting, userAccessGroup: userAccessGroup, @@ -597,7 +662,7 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { try { final eventChannelName = await _api.verifyPhoneNumber( pigeonDefault, - PigeonVerifyPhoneNumberRequest( + InternalVerifyPhoneNumberRequest( phoneNumber: phoneNumber, multiFactorInfoId: multiFactorInfo?.uid, timeout: timeout.inMilliseconds, @@ -664,6 +729,20 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform { } } + @override + Future revokeAccessToken(String accessToken) async { + if (defaultTargetPlatform != TargetPlatform.android) { + throw UnimplementedError( + 'revokeAccessToken() is only available on the Android platform.'); + } + + try { + await _api.revokeAccessToken(pigeonDefault, accessToken); + } catch (e, stack) { + convertPlatformException(e, stack); + } + } + @override Future initializeRecaptchaConfig() async { try { diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_multi_factor.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_multi_factor.dart index 988ef141ce2c..f0a438aeb0b7 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_multi_factor.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_multi_factor.dart @@ -55,7 +55,7 @@ class MethodChannelMultiFactor extends MultiFactorPlatform { try { await _api.enrollPhone( pigeonDefault, - PigeonPhoneMultiFactorAssertion( + InternalPhoneMultiFactorAssertion( verificationId: verificationId, verificationCode: verificationCode, ), @@ -150,7 +150,7 @@ class MethodChannelMultiFactorResolver extends MultiFactorResolverPlatform { try { final result = await _api.resolveSignIn( _resolverId, - PigeonPhoneMultiFactorAssertion( + InternalPhoneMultiFactorAssertion( verificationId: verificationId, verificationCode: verificationCode, ), diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user.dart index 977d1736898e..e6a627f8017b 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user.dart @@ -16,7 +16,7 @@ import 'utils/exception.dart'; class MethodChannelUser extends UserPlatform { /// Constructs a new [MethodChannelUser] instance. MethodChannelUser(FirebaseAuthPlatform auth, MultiFactorPlatform multiFactor, - PigeonUserDetails data) + InternalUserDetails data) : super(auth, multiFactor, data); final _api = FirebaseAuthUserHostApi(); @@ -96,7 +96,7 @@ class MethodChannelUser extends UserPlatform { final result = await _api.linkWithProvider( pigeonDefault, - PigeonSignInProvider( + InternalSignInProvider( providerId: convertedProvider.providerId, scopes: convertedProvider is OAuthProvider ? convertedProvider.scopes @@ -147,7 +147,7 @@ class MethodChannelUser extends UserPlatform { final result = await _api.reauthenticateWithProvider( pigeonDefault, - PigeonSignInProvider( + InternalSignInProvider( providerId: convertedProvider.providerId, scopes: convertedProvider is OAuthProvider ? convertedProvider.scopes @@ -191,7 +191,7 @@ class MethodChannelUser extends UserPlatform { pigeonDefault, actionCodeSettings == null ? null - : PigeonActionCodeSettings( + : InternalActionCodeSettings( url: actionCodeSettings.url, handleCodeInApp: actionCodeSettings.handleCodeInApp, iOSBundleId: actionCodeSettings.iOSBundleId, @@ -274,7 +274,7 @@ class MethodChannelUser extends UserPlatform { try { final result = await _api.updateProfile( pigeonDefault, - PigeonUserProfile( + InternalUserProfile( displayName: profile['displayName'], photoUrl: profile['photoURL'], displayNameChanged: profile.containsKey('displayName'), @@ -301,7 +301,7 @@ class MethodChannelUser extends UserPlatform { newEmail, actionCodeSettings == null ? null - : PigeonActionCodeSettings( + : InternalActionCodeSettings( url: actionCodeSettings.url, handleCodeInApp: actionCodeSettings.handleCodeInApp, iOSBundleId: actionCodeSettings.iOSBundleId, diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user_credential.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user_credential.dart index 35482d624798..7b65930754a9 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user_credential.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/method_channel_user_credential.dart @@ -12,7 +12,7 @@ import 'package:firebase_auth_platform_interface/src/pigeon/messages.pigeon.dart class MethodChannelUserCredential extends UserCredentialPlatform { // ignore: public_member_api_docs MethodChannelUserCredential( - FirebaseAuthPlatform auth, PigeonUserCredential data) + FirebaseAuthPlatform auth, InternalUserCredential data) : super( auth: auth, additionalUserInfo: data.additionalUserInfo == null diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart index f78a4e99669d..d01fedccb150 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/exception.dart @@ -42,9 +42,12 @@ FirebaseException platformExceptionToFirebaseAuthException( .replaceAll('ERROR_', '') .toLowerCase() .replaceAll('_', '-'); + final details = platformException.details is Map + ? platformException.details as Map + : null; final customCode = _getCustomCode( - platformException.details, + details, platformException.message, ); if (customCode != null) { @@ -60,11 +63,10 @@ FirebaseException platformExceptionToFirebaseAuthException( AuthCredential? credential; String? email; - if (platformException.details != null) { - if (platformException.details['authCredential'] != null && - platformException.details['authCredential'] is PigeonAuthCredential) { - PigeonAuthCredential pigeonAuthCredential = - platformException.details['authCredential']; + if (details != null) { + if (details['authCredential'] != null && + details['authCredential'] is InternalAuthCredential) { + InternalAuthCredential pigeonAuthCredential = details['authCredential']; credential = AuthCredential( providerId: pigeonAuthCredential.providerId, @@ -74,8 +76,8 @@ FirebaseException platformExceptionToFirebaseAuthException( ); } - if (platformException.details['email'] != null) { - email = platformException.details['email']; + if (details['email'] != null) { + email = details['email']; } } @@ -180,7 +182,7 @@ FirebaseAuthMultiFactorExceptionPlatform parseMultiFactorError( (additionalData['multiFactorHints'] as List? ?? []) .nonNulls .map( - PigeonMultiFactorInfo.decode, + InternalMultiFactorInfo.decode, ) .toList(); diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/pigeon_helper.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/pigeon_helper.dart index 7b3377c3bf69..66ed6e508946 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/pigeon_helper.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/method_channel/utils/pigeon_helper.dart @@ -6,7 +6,7 @@ import 'package:firebase_auth_platform_interface/firebase_auth_platform_interfac import 'package:firebase_auth_platform_interface/src/pigeon/messages.pigeon.dart'; List multiFactorInfoPigeonToObject( - List pigeonMultiFactorInfo, + List pigeonMultiFactorInfo, ) { return pigeonMultiFactorInfo.nonNulls.map((e) { if (e.phoneNumber != null) { diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/pigeon/messages.pigeon.dart index e66a71b7bc5a..60844c34575c 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,21 +1,40 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v19.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.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, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -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".', - ); +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } List wrapResponse( @@ -29,6 +48,68 @@ List wrapResponse( return [error.code, error.message, error.details]; } +bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + return a == b; +} + +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + /// The type of operation that generated the action code from calling /// [checkActionCode]. enum ActionCodeInfoOperation { @@ -54,29 +135,50 @@ enum ActionCodeInfoOperation { revertSecondFactorAddition, } -class PigeonMultiFactorSession { - PigeonMultiFactorSession({ +class InternalMultiFactorSession { + InternalMultiFactorSession({ required this.id, }); String id; - Object encode() { + List _toList() { return [ id, ]; } - static PigeonMultiFactorSession decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalMultiFactorSession decode(Object result) { result as List; - return PigeonMultiFactorSession( + return InternalMultiFactorSession( id: result[0]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalMultiFactorSession || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(id, other.id); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonPhoneMultiFactorAssertion { - PigeonPhoneMultiFactorAssertion({ +class InternalPhoneMultiFactorAssertion { + InternalPhoneMultiFactorAssertion({ required this.verificationId, required this.verificationCode, }); @@ -85,24 +187,46 @@ class PigeonPhoneMultiFactorAssertion { String verificationCode; - Object encode() { + List _toList() { return [ verificationId, verificationCode, ]; } - static PigeonPhoneMultiFactorAssertion decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalPhoneMultiFactorAssertion decode(Object result) { result as List; - return PigeonPhoneMultiFactorAssertion( + return InternalPhoneMultiFactorAssertion( verificationId: result[0]! as String, verificationCode: result[1]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalPhoneMultiFactorAssertion || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(verificationId, other.verificationId) && + _deepEquals(verificationCode, other.verificationCode); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonMultiFactorInfo { - PigeonMultiFactorInfo({ +class InternalMultiFactorInfo { + InternalMultiFactorInfo({ this.displayName, required this.enrollmentTimestamp, this.factorId, @@ -120,7 +244,7 @@ class PigeonMultiFactorInfo { String? phoneNumber; - Object encode() { + List _toList() { return [ displayName, enrollmentTimestamp, @@ -130,9 +254,13 @@ class PigeonMultiFactorInfo { ]; } - static PigeonMultiFactorInfo decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalMultiFactorInfo decode(Object result) { result as List; - return PigeonMultiFactorInfo( + return InternalMultiFactorInfo( displayName: result[0] as String?, enrollmentTimestamp: result[1]! as double, factorId: result[2] as String?, @@ -140,6 +268,26 @@ class PigeonMultiFactorInfo { phoneNumber: result[4] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalMultiFactorInfo || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(displayName, other.displayName) && + _deepEquals(enrollmentTimestamp, other.enrollmentTimestamp) && + _deepEquals(factorId, other.factorId) && + _deepEquals(uid, other.uid) && + _deepEquals(phoneNumber, other.phoneNumber); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class AuthPigeonFirebaseApp { @@ -155,7 +303,7 @@ class AuthPigeonFirebaseApp { String? customAuthDomain; - Object encode() { + List _toList() { return [ appName, tenantId, @@ -163,6 +311,10 @@ class AuthPigeonFirebaseApp { ]; } + Object encode() { + return _toList(); + } + static AuthPigeonFirebaseApp decode(Object result) { result as List; return AuthPigeonFirebaseApp( @@ -171,10 +323,28 @@ class AuthPigeonFirebaseApp { customAuthDomain: result[2] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! AuthPigeonFirebaseApp || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(appName, other.appName) && + _deepEquals(tenantId, other.tenantId) && + _deepEquals(customAuthDomain, other.customAuthDomain); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonActionCodeInfoData { - PigeonActionCodeInfoData({ +class InternalActionCodeInfoData { + InternalActionCodeInfoData({ this.email, this.previousEmail, }); @@ -183,50 +353,93 @@ class PigeonActionCodeInfoData { String? previousEmail; - Object encode() { + List _toList() { return [ email, previousEmail, ]; } - static PigeonActionCodeInfoData decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalActionCodeInfoData decode(Object result) { result as List; - return PigeonActionCodeInfoData( + return InternalActionCodeInfoData( email: result[0] as String?, previousEmail: result[1] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalActionCodeInfoData || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(email, other.email) && + _deepEquals(previousEmail, other.previousEmail); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonActionCodeInfo { - PigeonActionCodeInfo({ +class InternalActionCodeInfo { + InternalActionCodeInfo({ required this.operation, required this.data, }); ActionCodeInfoOperation operation; - PigeonActionCodeInfoData data; + InternalActionCodeInfoData data; - Object encode() { + List _toList() { return [ - operation.index, + operation, data, ]; } - static PigeonActionCodeInfo decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalActionCodeInfo decode(Object result) { result as List; - return PigeonActionCodeInfo( - operation: ActionCodeInfoOperation.values[result[0]! as int], - data: result[1]! as PigeonActionCodeInfoData, + return InternalActionCodeInfo( + operation: result[0]! as ActionCodeInfoOperation, + data: result[1]! as InternalActionCodeInfoData, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalActionCodeInfo || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(operation, other.operation) && + _deepEquals(data, other.data); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonAdditionalUserInfo { - PigeonAdditionalUserInfo({ +class InternalAdditionalUserInfo { + InternalAdditionalUserInfo({ required this.isNewUser, this.providerId, this.username, @@ -244,7 +457,7 @@ class PigeonAdditionalUserInfo { Map? profile; - Object encode() { + List _toList() { return [ isNewUser, providerId, @@ -254,9 +467,13 @@ class PigeonAdditionalUserInfo { ]; } - static PigeonAdditionalUserInfo decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalAdditionalUserInfo decode(Object result) { result as List; - return PigeonAdditionalUserInfo( + return InternalAdditionalUserInfo( isNewUser: result[0]! as bool, providerId: result[1] as String?, username: result[2] as String?, @@ -264,10 +481,31 @@ class PigeonAdditionalUserInfo { profile: (result[4] as Map?)?.cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalAdditionalUserInfo || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(isNewUser, other.isNewUser) && + _deepEquals(providerId, other.providerId) && + _deepEquals(username, other.username) && + _deepEquals(authorizationCode, other.authorizationCode) && + _deepEquals(profile, other.profile); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonAuthCredential { - PigeonAuthCredential({ +class InternalAuthCredential { + InternalAuthCredential({ required this.providerId, required this.signInMethod, required this.nativeId, @@ -282,7 +520,7 @@ class PigeonAuthCredential { String? accessToken; - Object encode() { + List _toList() { return [ providerId, signInMethod, @@ -291,19 +529,42 @@ class PigeonAuthCredential { ]; } - static PigeonAuthCredential decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalAuthCredential decode(Object result) { result as List; - return PigeonAuthCredential( + return InternalAuthCredential( providerId: result[0]! as String, signInMethod: result[1]! as String, nativeId: result[2]! as int, accessToken: result[3] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalAuthCredential || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(providerId, other.providerId) && + _deepEquals(signInMethod, other.signInMethod) && + _deepEquals(nativeId, other.nativeId) && + _deepEquals(accessToken, other.accessToken); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonUserInfo { - PigeonUserInfo({ +class InternalUserInfo { + InternalUserInfo({ required this.uid, this.email, this.displayName, @@ -342,7 +603,7 @@ class PigeonUserInfo { int? lastSignInTimestamp; - Object encode() { + List _toList() { return [ uid, email, @@ -359,9 +620,13 @@ class PigeonUserInfo { ]; } - static PigeonUserInfo decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalUserInfo decode(Object result) { result as List; - return PigeonUserInfo( + return InternalUserInfo( uid: result[0]! as String, email: result[1] as String?, displayName: result[2] as String?, @@ -376,49 +641,97 @@ class PigeonUserInfo { lastSignInTimestamp: result[11] as int?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalUserInfo || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(uid, other.uid) && + _deepEquals(email, other.email) && + _deepEquals(displayName, other.displayName) && + _deepEquals(photoUrl, other.photoUrl) && + _deepEquals(phoneNumber, other.phoneNumber) && + _deepEquals(isAnonymous, other.isAnonymous) && + _deepEquals(isEmailVerified, other.isEmailVerified) && + _deepEquals(providerId, other.providerId) && + _deepEquals(tenantId, other.tenantId) && + _deepEquals(refreshToken, other.refreshToken) && + _deepEquals(creationTimestamp, other.creationTimestamp) && + _deepEquals(lastSignInTimestamp, other.lastSignInTimestamp); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonUserDetails { - PigeonUserDetails({ +class InternalUserDetails { + InternalUserDetails({ required this.userInfo, required this.providerData, }); - PigeonUserInfo userInfo; + InternalUserInfo userInfo; List?> providerData; - Object encode() { + List _toList() { return [ userInfo, providerData, ]; } - static PigeonUserDetails decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalUserDetails decode(Object result) { result as List; - return PigeonUserDetails( - userInfo: result[0]! as PigeonUserInfo, + return InternalUserDetails( + userInfo: result[0]! as InternalUserInfo, providerData: - (result[1] as List?)!.cast?>(), + (result[1]! as List).cast?>(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalUserDetails || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(userInfo, other.userInfo) && + _deepEquals(providerData, other.providerData); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonUserCredential { - PigeonUserCredential({ +class InternalUserCredential { + InternalUserCredential({ this.user, this.additionalUserInfo, this.credential, }); - PigeonUserDetails? user; + InternalUserDetails? user; - PigeonAdditionalUserInfo? additionalUserInfo; + InternalAdditionalUserInfo? additionalUserInfo; - PigeonAuthCredential? credential; + InternalAuthCredential? credential; - Object encode() { + List _toList() { return [ user, additionalUserInfo, @@ -426,18 +739,100 @@ class PigeonUserCredential { ]; } - static PigeonUserCredential decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalUserCredential decode(Object result) { + result as List; + return InternalUserCredential( + user: result[0] as InternalUserDetails?, + additionalUserInfo: result[1] as InternalAdditionalUserInfo?, + credential: result[2] as InternalAuthCredential?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalUserCredential || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(user, other.user) && + _deepEquals(additionalUserInfo, other.additionalUserInfo) && + _deepEquals(credential, other.credential); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); +} + +class InternalAuthCredentialInput { + InternalAuthCredentialInput({ + required this.providerId, + required this.signInMethod, + this.token, + this.accessToken, + }); + + String providerId; + + String signInMethod; + + String? token; + + String? accessToken; + + List _toList() { + return [ + providerId, + signInMethod, + token, + accessToken, + ]; + } + + Object encode() { + return _toList(); + } + + static InternalAuthCredentialInput decode(Object result) { result as List; - return PigeonUserCredential( - user: result[0] as PigeonUserDetails?, - additionalUserInfo: result[1] as PigeonAdditionalUserInfo?, - credential: result[2] as PigeonAuthCredential?, + return InternalAuthCredentialInput( + providerId: result[0]! as String, + signInMethod: result[1]! as String, + token: result[2] as String?, + accessToken: result[3] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalAuthCredentialInput || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(providerId, other.providerId) && + _deepEquals(signInMethod, other.signInMethod) && + _deepEquals(token, other.token) && + _deepEquals(accessToken, other.accessToken); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonActionCodeSettings { - PigeonActionCodeSettings({ +class InternalActionCodeSettings { + InternalActionCodeSettings({ required this.url, this.dynamicLinkDomain, required this.handleCodeInApp, @@ -464,7 +859,7 @@ class PigeonActionCodeSettings { String? linkDomain; - Object encode() { + List _toList() { return [ url, dynamicLinkDomain, @@ -477,9 +872,13 @@ class PigeonActionCodeSettings { ]; } - static PigeonActionCodeSettings decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalActionCodeSettings decode(Object result) { result as List; - return PigeonActionCodeSettings( + return InternalActionCodeSettings( url: result[0]! as String, dynamicLinkDomain: result[1] as String?, handleCodeInApp: result[2]! as bool, @@ -490,10 +889,34 @@ class PigeonActionCodeSettings { linkDomain: result[7] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalActionCodeSettings || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(url, other.url) && + _deepEquals(dynamicLinkDomain, other.dynamicLinkDomain) && + _deepEquals(handleCodeInApp, other.handleCodeInApp) && + _deepEquals(iOSBundleId, other.iOSBundleId) && + _deepEquals(androidPackageName, other.androidPackageName) && + _deepEquals(androidInstallApp, other.androidInstallApp) && + _deepEquals(androidMinimumVersion, other.androidMinimumVersion) && + _deepEquals(linkDomain, other.linkDomain); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonFirebaseAuthSettings { - PigeonFirebaseAuthSettings({ +class InternalFirebaseAuthSettings { + InternalFirebaseAuthSettings({ required this.appVerificationDisabledForTesting, this.userAccessGroup, this.phoneNumber, @@ -511,7 +934,7 @@ class PigeonFirebaseAuthSettings { bool? forceRecaptchaFlow; - Object encode() { + List _toList() { return [ appVerificationDisabledForTesting, userAccessGroup, @@ -521,9 +944,13 @@ class PigeonFirebaseAuthSettings { ]; } - static PigeonFirebaseAuthSettings decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalFirebaseAuthSettings decode(Object result) { result as List; - return PigeonFirebaseAuthSettings( + return InternalFirebaseAuthSettings( appVerificationDisabledForTesting: result[0]! as bool, userAccessGroup: result[1] as String?, phoneNumber: result[2] as String?, @@ -531,10 +958,32 @@ class PigeonFirebaseAuthSettings { forceRecaptchaFlow: result[4] as bool?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalFirebaseAuthSettings || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(appVerificationDisabledForTesting, + other.appVerificationDisabledForTesting) && + _deepEquals(userAccessGroup, other.userAccessGroup) && + _deepEquals(phoneNumber, other.phoneNumber) && + _deepEquals(smsCode, other.smsCode) && + _deepEquals(forceRecaptchaFlow, other.forceRecaptchaFlow); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonSignInProvider { - PigeonSignInProvider({ +class InternalSignInProvider { + InternalSignInProvider({ required this.providerId, this.scopes, this.customParameters, @@ -546,7 +995,7 @@ class PigeonSignInProvider { Map? customParameters; - Object encode() { + List _toList() { return [ providerId, scopes, @@ -554,19 +1003,41 @@ class PigeonSignInProvider { ]; } - static PigeonSignInProvider decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalSignInProvider decode(Object result) { result as List; - return PigeonSignInProvider( + return InternalSignInProvider( providerId: result[0]! as String, scopes: (result[1] as List?)?.cast(), customParameters: (result[2] as Map?)?.cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalSignInProvider || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(providerId, other.providerId) && + _deepEquals(scopes, other.scopes) && + _deepEquals(customParameters, other.customParameters); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonVerifyPhoneNumberRequest { - PigeonVerifyPhoneNumberRequest({ +class InternalVerifyPhoneNumberRequest { + InternalVerifyPhoneNumberRequest({ this.phoneNumber, required this.timeout, this.forceResendingToken, @@ -587,7 +1058,7 @@ class PigeonVerifyPhoneNumberRequest { String? multiFactorSessionId; - Object encode() { + List _toList() { return [ phoneNumber, timeout, @@ -598,9 +1069,13 @@ class PigeonVerifyPhoneNumberRequest { ]; } - static PigeonVerifyPhoneNumberRequest decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalVerifyPhoneNumberRequest decode(Object result) { result as List; - return PigeonVerifyPhoneNumberRequest( + return InternalVerifyPhoneNumberRequest( phoneNumber: result[0] as String?, timeout: result[1]! as int, forceResendingToken: result[2] as int?, @@ -609,10 +1084,33 @@ class PigeonVerifyPhoneNumberRequest { multiFactorSessionId: result[5] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalVerifyPhoneNumberRequest || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(phoneNumber, other.phoneNumber) && + _deepEquals(timeout, other.timeout) && + _deepEquals(forceResendingToken, other.forceResendingToken) && + _deepEquals(autoRetrievedSmsCodeForTesting, + other.autoRetrievedSmsCodeForTesting) && + _deepEquals(multiFactorInfoId, other.multiFactorInfoId) && + _deepEquals(multiFactorSessionId, other.multiFactorSessionId); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonIdTokenResult { - PigeonIdTokenResult({ +class InternalIdTokenResult { + InternalIdTokenResult({ this.token, this.expirationTimestamp, this.authTimestamp, @@ -636,7 +1134,7 @@ class PigeonIdTokenResult { String? signInSecondFactor; - Object encode() { + List _toList() { return [ token, expirationTimestamp, @@ -648,9 +1146,13 @@ class PigeonIdTokenResult { ]; } - static PigeonIdTokenResult decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalIdTokenResult decode(Object result) { result as List; - return PigeonIdTokenResult( + return InternalIdTokenResult( token: result[0] as String?, expirationTimestamp: result[1] as int?, authTimestamp: result[2] as int?, @@ -660,10 +1162,32 @@ class PigeonIdTokenResult { signInSecondFactor: result[6] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalIdTokenResult || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(token, other.token) && + _deepEquals(expirationTimestamp, other.expirationTimestamp) && + _deepEquals(authTimestamp, other.authTimestamp) && + _deepEquals(issuedAtTimestamp, other.issuedAtTimestamp) && + _deepEquals(signInProvider, other.signInProvider) && + _deepEquals(claims, other.claims) && + _deepEquals(signInSecondFactor, other.signInSecondFactor); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonUserProfile { - PigeonUserProfile({ +class InternalUserProfile { + InternalUserProfile({ this.displayName, this.photoUrl, required this.displayNameChanged, @@ -678,7 +1202,7 @@ class PigeonUserProfile { bool photoUrlChanged; - Object encode() { + List _toList() { return [ displayName, photoUrl, @@ -687,19 +1211,42 @@ class PigeonUserProfile { ]; } - static PigeonUserProfile decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalUserProfile decode(Object result) { result as List; - return PigeonUserProfile( + return InternalUserProfile( displayName: result[0] as String?, photoUrl: result[1] as String?, displayNameChanged: result[2]! as bool, photoUrlChanged: result[3]! as bool, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalUserProfile || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(displayName, other.displayName) && + _deepEquals(photoUrl, other.photoUrl) && + _deepEquals(displayNameChanged, other.displayNameChanged) && + _deepEquals(photoUrlChanged, other.photoUrlChanged); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonTotpSecret { - PigeonTotpSecret({ +class InternalTotpSecret { + InternalTotpSecret({ this.codeIntervalSeconds, this.codeLength, this.enrollmentCompletionDeadline, @@ -717,7 +1264,7 @@ class PigeonTotpSecret { String secretKey; - Object encode() { + List _toList() { return [ codeIntervalSeconds, codeLength, @@ -727,9 +1274,13 @@ class PigeonTotpSecret { ]; } - static PigeonTotpSecret decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalTotpSecret decode(Object result) { result as List; - return PigeonTotpSecret( + return InternalTotpSecret( codeIntervalSeconds: result[0] as int?, codeLength: result[1] as int?, enrollmentCompletionDeadline: result[2] as int?, @@ -737,66 +1288,96 @@ class PigeonTotpSecret { secretKey: result[4]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalTotpSecret || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(codeIntervalSeconds, other.codeIntervalSeconds) && + _deepEquals(codeLength, other.codeLength) && + _deepEquals( + enrollmentCompletionDeadline, other.enrollmentCompletionDeadline) && + _deepEquals(hashingAlgorithm, other.hashingAlgorithm) && + _deepEquals(secretKey, other.secretKey); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class _FirebaseAuthHostApiCodec extends StandardMessageCodec { - const _FirebaseAuthHostApiCodec(); +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is AuthPigeonFirebaseApp) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfo) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is ActionCodeInfoOperation) { buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfoData) { + writeValue(buffer, value.index); + } else if (value is InternalMultiFactorSession) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeSettings) { + } else if (value is InternalPhoneMultiFactorAssertion) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is PigeonAdditionalUserInfo) { + } else if (value is InternalMultiFactorInfo) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PigeonAuthCredential) { + } else if (value is AuthPigeonFirebaseApp) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is PigeonFirebaseAuthSettings) { + } else if (value is InternalActionCodeInfoData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is PigeonIdTokenResult) { + } else if (value is InternalActionCodeInfo) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorInfo) { + } else if (value is InternalAdditionalUserInfo) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorSession) { + } else if (value is InternalAuthCredential) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { + } else if (value is InternalUserInfo) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PigeonSignInProvider) { + } else if (value is InternalUserDetails) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PigeonTotpSecret) { + } else if (value is InternalUserCredential) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is PigeonUserCredential) { + } else if (value is InternalAuthCredentialInput) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is PigeonUserDetails) { + } else if (value is InternalActionCodeSettings) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is PigeonUserInfo) { + } else if (value is InternalFirebaseAuthSettings) { buffer.putUint8(143); writeValue(buffer, value.encode()); - } else if (value is PigeonUserProfile) { + } else if (value is InternalSignInProvider) { buffer.putUint8(144); writeValue(buffer, value.encode()); - } else if (value is PigeonVerifyPhoneNumberRequest) { + } else if (value is InternalVerifyPhoneNumberRequest) { buffer.putUint8(145); writeValue(buffer, value.encode()); + } else if (value is InternalIdTokenResult) { + buffer.putUint8(146); + writeValue(buffer, value.encode()); + } else if (value is InternalUserProfile) { + buffer.putUint8(147); + writeValue(buffer, value.encode()); + } else if (value is InternalTotpSecret) { + buffer.putUint8(148); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -805,42 +1386,47 @@ class _FirebaseAuthHostApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: - return AuthPigeonFirebaseApp.decode(readValue(buffer)!); case 129: - return PigeonActionCodeInfo.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : ActionCodeInfoOperation.values[value]; case 130: - return PigeonActionCodeInfoData.decode(readValue(buffer)!); + return InternalMultiFactorSession.decode(readValue(buffer)!); case 131: - return PigeonActionCodeSettings.decode(readValue(buffer)!); + return InternalPhoneMultiFactorAssertion.decode(readValue(buffer)!); case 132: - return PigeonAdditionalUserInfo.decode(readValue(buffer)!); + return InternalMultiFactorInfo.decode(readValue(buffer)!); case 133: - return PigeonAuthCredential.decode(readValue(buffer)!); + return AuthPigeonFirebaseApp.decode(readValue(buffer)!); case 134: - return PigeonFirebaseAuthSettings.decode(readValue(buffer)!); + return InternalActionCodeInfoData.decode(readValue(buffer)!); case 135: - return PigeonIdTokenResult.decode(readValue(buffer)!); + return InternalActionCodeInfo.decode(readValue(buffer)!); case 136: - return PigeonMultiFactorInfo.decode(readValue(buffer)!); + return InternalAdditionalUserInfo.decode(readValue(buffer)!); case 137: - return PigeonMultiFactorSession.decode(readValue(buffer)!); + return InternalAuthCredential.decode(readValue(buffer)!); case 138: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); + return InternalUserInfo.decode(readValue(buffer)!); case 139: - return PigeonSignInProvider.decode(readValue(buffer)!); + return InternalUserDetails.decode(readValue(buffer)!); case 140: - return PigeonTotpSecret.decode(readValue(buffer)!); + return InternalUserCredential.decode(readValue(buffer)!); case 141: - return PigeonUserCredential.decode(readValue(buffer)!); + return InternalAuthCredentialInput.decode(readValue(buffer)!); case 142: - return PigeonUserDetails.decode(readValue(buffer)!); + return InternalActionCodeSettings.decode(readValue(buffer)!); case 143: - return PigeonUserInfo.decode(readValue(buffer)!); + return InternalFirebaseAuthSettings.decode(readValue(buffer)!); case 144: - return PigeonUserProfile.decode(readValue(buffer)!); + return InternalSignInProvider.decode(readValue(buffer)!); case 145: - return PigeonVerifyPhoneNumberRequest.decode(readValue(buffer)!); + return InternalVerifyPhoneNumberRequest.decode(readValue(buffer)!); + case 146: + return InternalIdTokenResult.decode(readValue(buffer)!); + case 147: + return InternalUserProfile.decode(readValue(buffer)!); + case 148: + return InternalTotpSecret.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -853,762 +1439,502 @@ class FirebaseAuthHostApi { /// BinaryMessenger will be used which routes to the host platform. FirebaseAuthHostApi( {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? __pigeon_binaryMessenger; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _FirebaseAuthHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - final String __pigeon_messageChannelSuffix; + final String pigeonVar_messageChannelSuffix; Future registerIdTokenListener(AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerIdTokenListener$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerIdTokenListener$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future registerAuthStateListener(AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerAuthStateListener$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerAuthStateListener$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future useEmulator( AuthPigeonFirebaseApp app, String host, int port) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.useEmulator$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.useEmulator$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, host, port]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, host, port]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future applyActionCode(AuthPigeonFirebaseApp app, String code) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.applyActionCode$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.applyActionCode$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, code]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, code]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future checkActionCode( + Future checkActionCode( AuthPigeonFirebaseApp app, String code) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.checkActionCode$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.checkActionCode$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, code]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonActionCodeInfo?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, code]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalActionCodeInfo; } Future confirmPasswordReset( AuthPigeonFirebaseApp app, String code, String newPassword) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.confirmPasswordReset$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.confirmPasswordReset$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, code, newPassword]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, code, newPassword]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future createUserWithEmailAndPassword( + Future createUserWithEmailAndPassword( AuthPigeonFirebaseApp app, String email, String password) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.createUserWithEmailAndPassword$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.createUserWithEmailAndPassword$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, email, password]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, email, password]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future signInAnonymously( + Future signInAnonymously( AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInAnonymously$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInAnonymously$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future signInWithCredential( + Future signInWithCredential( AuthPigeonFirebaseApp app, Map input) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCredential$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCredential$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, input]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, input]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future signInWithCustomToken( + Future signInWithCustomToken( AuthPigeonFirebaseApp app, String token) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCustomToken$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCustomToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, token]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, token]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future signInWithEmailAndPassword( + Future signInWithEmailAndPassword( AuthPigeonFirebaseApp app, String email, String password) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailAndPassword$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailAndPassword$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, email, password]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, email, password]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future signInWithEmailLink( + Future signInWithEmailLink( AuthPigeonFirebaseApp app, String email, String emailLink) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailLink$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailLink$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, email, emailLink]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, email, emailLink]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future signInWithProvider( - AuthPigeonFirebaseApp app, PigeonSignInProvider signInProvider) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithProvider$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future signInWithProvider( + AuthPigeonFirebaseApp app, InternalSignInProvider signInProvider) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithProvider$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, signInProvider]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, signInProvider]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } Future signOut(AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signOut$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signOut$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future> fetchSignInMethodsForEmail( + Future> fetchSignInMethodsForEmail( AuthPigeonFirebaseApp app, String email) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.fetchSignInMethodsForEmail$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.fetchSignInMethodsForEmail$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, email]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as List?)!.cast(); - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, email]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as List).cast(); } Future sendPasswordResetEmail(AuthPigeonFirebaseApp app, String email, - PigeonActionCodeSettings? actionCodeSettings) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendPasswordResetEmail$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + InternalActionCodeSettings? actionCodeSettings) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendPasswordResetEmail$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, email, actionCodeSettings]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, email, actionCodeSettings]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future sendSignInLinkToEmail(AuthPigeonFirebaseApp app, String email, - PigeonActionCodeSettings actionCodeSettings) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendSignInLinkToEmail$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + InternalActionCodeSettings actionCodeSettings) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendSignInLinkToEmail$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, email, actionCodeSettings]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, email, actionCodeSettings]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setLanguageCode( AuthPigeonFirebaseApp app, String? languageCode) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setLanguageCode$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setLanguageCode$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, languageCode]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, languageCode]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future setSettings( - AuthPigeonFirebaseApp app, PigeonFirebaseAuthSettings settings) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setSettings$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + AuthPigeonFirebaseApp app, InternalFirebaseAuthSettings settings) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setSettings$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, settings]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, settings]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future verifyPasswordResetCode( AuthPigeonFirebaseApp app, String code) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPasswordResetCode$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPasswordResetCode$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, code]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, code]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } - Future verifyPhoneNumber( - AuthPigeonFirebaseApp app, PigeonVerifyPhoneNumberRequest request) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPhoneNumber$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future verifyPhoneNumber(AuthPigeonFirebaseApp app, + InternalVerifyPhoneNumberRequest request) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPhoneNumber$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, request]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future revokeTokenWithAuthorizationCode( AuthPigeonFirebaseApp app, String authorizationCode) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeTokenWithAuthorizationCode$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeTokenWithAuthorizationCode$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, authorizationCode]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, authorizationCode]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future initializeRecaptchaConfig(AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future revokeAccessToken( + AuthPigeonFirebaseApp app, String accessToken) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeAccessToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } - } -} + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, accessToken]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; -class _FirebaseAuthUserHostApiCodec extends StandardMessageCodec { - const _FirebaseAuthUserHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is AuthPigeonFirebaseApp) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfo) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfoData) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeSettings) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is PigeonAdditionalUserInfo) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is PigeonAuthCredential) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else if (value is PigeonFirebaseAuthSettings) { - buffer.putUint8(134); - writeValue(buffer, value.encode()); - } else if (value is PigeonIdTokenResult) { - buffer.putUint8(135); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorInfo) { - buffer.putUint8(136); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorSession) { - buffer.putUint8(137); - writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { - buffer.putUint8(138); - writeValue(buffer, value.encode()); - } else if (value is PigeonSignInProvider) { - buffer.putUint8(139); - writeValue(buffer, value.encode()); - } else if (value is PigeonTotpSecret) { - buffer.putUint8(140); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserCredential) { - buffer.putUint8(141); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserDetails) { - buffer.putUint8(142); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserInfo) { - buffer.putUint8(143); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserProfile) { - buffer.putUint8(144); - writeValue(buffer, value.encode()); - } else if (value is PigeonVerifyPhoneNumberRequest) { - buffer.putUint8(145); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return AuthPigeonFirebaseApp.decode(readValue(buffer)!); - case 129: - return PigeonActionCodeInfo.decode(readValue(buffer)!); - case 130: - return PigeonActionCodeInfoData.decode(readValue(buffer)!); - case 131: - return PigeonActionCodeSettings.decode(readValue(buffer)!); - case 132: - return PigeonAdditionalUserInfo.decode(readValue(buffer)!); - case 133: - return PigeonAuthCredential.decode(readValue(buffer)!); - case 134: - return PigeonFirebaseAuthSettings.decode(readValue(buffer)!); - case 135: - return PigeonIdTokenResult.decode(readValue(buffer)!); - case 136: - return PigeonMultiFactorInfo.decode(readValue(buffer)!); - case 137: - return PigeonMultiFactorSession.decode(readValue(buffer)!); - case 138: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); - case 139: - return PigeonSignInProvider.decode(readValue(buffer)!); - case 140: - return PigeonTotpSecret.decode(readValue(buffer)!); - case 141: - return PigeonUserCredential.decode(readValue(buffer)!); - case 142: - return PigeonUserDetails.decode(readValue(buffer)!); - case 143: - return PigeonUserInfo.decode(readValue(buffer)!); - case 144: - return PigeonUserProfile.decode(readValue(buffer)!); - case 145: - return PigeonVerifyPhoneNumberRequest.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } + Future initializeRecaptchaConfig(AuthPigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } @@ -1618,455 +1944,302 @@ class FirebaseAuthUserHostApi { /// BinaryMessenger will be used which routes to the host platform. FirebaseAuthUserHostApi( {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? __pigeon_binaryMessenger; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _FirebaseAuthUserHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - final String __pigeon_messageChannelSuffix; + final String pigeonVar_messageChannelSuffix; Future delete(AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.delete$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.delete$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future getIdToken( + Future getIdToken( AuthPigeonFirebaseApp app, bool forceRefresh) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.getIdToken$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.getIdToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, forceRefresh]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonIdTokenResult?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, forceRefresh]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalIdTokenResult; } - Future linkWithCredential( + Future linkWithCredential( AuthPigeonFirebaseApp app, Map input) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithCredential$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithCredential$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, input]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, input]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future linkWithProvider( - AuthPigeonFirebaseApp app, PigeonSignInProvider signInProvider) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithProvider$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future linkWithProvider( + AuthPigeonFirebaseApp app, InternalSignInProvider signInProvider) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithProvider$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, signInProvider]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, signInProvider]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future reauthenticateWithCredential( + Future reauthenticateWithCredential( AuthPigeonFirebaseApp app, Map input) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithCredential$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithCredential$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, input]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, input]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future reauthenticateWithProvider( - AuthPigeonFirebaseApp app, PigeonSignInProvider signInProvider) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithProvider$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future reauthenticateWithProvider( + AuthPigeonFirebaseApp app, InternalSignInProvider signInProvider) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithProvider$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, signInProvider]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, signInProvider]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future reload(AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reload$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future reload(AuthPigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reload$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserDetails?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserDetails; } Future sendEmailVerification(AuthPigeonFirebaseApp app, - PigeonActionCodeSettings? actionCodeSettings) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.sendEmailVerification$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + InternalActionCodeSettings? actionCodeSettings) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.sendEmailVerification$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, actionCodeSettings]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, actionCodeSettings]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future unlink( + Future unlink( AuthPigeonFirebaseApp app, String providerId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.unlink$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.unlink$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, providerId]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, providerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } - Future updateEmail( + Future updateEmail( AuthPigeonFirebaseApp app, String newEmail) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateEmail$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateEmail$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, newEmail]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserDetails?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, newEmail]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserDetails; } - Future updatePassword( + Future updatePassword( AuthPigeonFirebaseApp app, String newPassword) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePassword$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePassword$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, newPassword]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserDetails?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, newPassword]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserDetails; } - Future updatePhoneNumber( + Future updatePhoneNumber( AuthPigeonFirebaseApp app, Map input) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePhoneNumber$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePhoneNumber$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, input]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserDetails?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, input]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserDetails; } - Future updateProfile( - AuthPigeonFirebaseApp app, PigeonUserProfile profile) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateProfile$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future updateProfile( + AuthPigeonFirebaseApp app, InternalUserProfile profile) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateProfile$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app, profile]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserDetails?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, profile]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserDetails; } Future verifyBeforeUpdateEmail(AuthPigeonFirebaseApp app, - String newEmail, PigeonActionCodeSettings? actionCodeSettings) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.verifyBeforeUpdateEmail$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + String newEmail, InternalActionCodeSettings? actionCodeSettings) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.verifyBeforeUpdateEmail$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, newEmail, actionCodeSettings]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } - } -} - -class _MultiFactorUserHostApiCodec extends StandardMessageCodec { - const _MultiFactorUserHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is AuthPigeonFirebaseApp) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorInfo) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorSession) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, newEmail, actionCodeSettings]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return AuthPigeonFirebaseApp.decode(readValue(buffer)!); - case 129: - return PigeonMultiFactorInfo.decode(readValue(buffer)!); - case 130: - return PigeonMultiFactorSession.decode(readValue(buffer)!); - case 131: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } @@ -2076,196 +2249,115 @@ class MultiFactorUserHostApi { /// BinaryMessenger will be used which routes to the host platform. MultiFactorUserHostApi( {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? __pigeon_binaryMessenger; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _MultiFactorUserHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - final String __pigeon_messageChannelSuffix; + final String pigeonVar_messageChannelSuffix; Future enrollPhone(AuthPigeonFirebaseApp app, - PigeonPhoneMultiFactorAssertion assertion, String? displayName) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollPhone$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + InternalPhoneMultiFactorAssertion assertion, String? displayName) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollPhone$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, assertion, displayName]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, assertion, displayName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future enrollTotp(AuthPigeonFirebaseApp app, String assertionId, String? displayName) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollTotp$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollTotp$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, assertionId, displayName]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, assertionId, displayName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future getSession(AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getSession$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future getSession( + AuthPigeonFirebaseApp app) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getSession$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonMultiFactorSession?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalMultiFactorSession; } Future unenroll(AuthPigeonFirebaseApp app, String factorUid) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.unenroll$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.unenroll$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([app, factorUid]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, factorUid]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future> getEnrolledFactors( + Future> getEnrolledFactors( AuthPigeonFirebaseApp app) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getEnrolledFactors$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getEnrolledFactors$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([app]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as List?)! - .cast(); - } - } -} - -class _MultiFactoResolverHostApiCodec extends StandardMessageCodec { - const _MultiFactoResolverHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is PigeonAdditionalUserInfo) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonAuthCredential) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserCredential) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserDetails) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserInfo) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return PigeonAdditionalUserInfo.decode(readValue(buffer)!); - case 129: - return PigeonAuthCredential.decode(readValue(buffer)!); - case 130: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); - case 131: - return PigeonUserCredential.decode(readValue(buffer)!); - case 132: - return PigeonUserDetails.decode(readValue(buffer)!); - case 133: - return PigeonUserInfo.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as List) + .cast(); } } @@ -2275,70 +2367,36 @@ class MultiFactoResolverHostApi { /// BinaryMessenger will be used which routes to the host platform. MultiFactoResolverHostApi( {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? __pigeon_binaryMessenger; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _MultiFactoResolverHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - final String __pigeon_messageChannelSuffix; + final String pigeonVar_messageChannelSuffix; - Future resolveSignIn( + Future resolveSignIn( String resolverId, - PigeonPhoneMultiFactorAssertion? assertion, + InternalPhoneMultiFactorAssertion? assertion, String? totpAssertionId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactoResolverHostApi.resolveSignIn$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactoResolverHostApi.resolveSignIn$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([resolverId, assertion, totpAssertionId]) - as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonUserCredential?)!; - } - } -} - -class _MultiFactorTotpHostApiCodec extends StandardMessageCodec { - const _MultiFactorTotpHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is PigeonTotpSecret) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([resolverId, assertion, totpAssertionId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return PigeonTotpSecret.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalUserCredential; } } @@ -2348,103 +2406,75 @@ class MultiFactorTotpHostApi { /// BinaryMessenger will be used which routes to the host platform. MultiFactorTotpHostApi( {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? __pigeon_binaryMessenger; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _MultiFactorTotpHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - final String __pigeon_messageChannelSuffix; + final String pigeonVar_messageChannelSuffix; - Future generateSecret(String sessionId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.generateSecret$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future generateSecret(String sessionId) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.generateSecret$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([sessionId]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as PigeonTotpSecret?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([sessionId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalTotpSecret; } Future getAssertionForEnrollment( String secretKey, String oneTimePassword) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForEnrollment$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForEnrollment$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([secretKey, oneTimePassword]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([secretKey, oneTimePassword]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future getAssertionForSignIn( String enrollmentId, String oneTimePassword) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForSignIn$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForSignIn$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([enrollmentId, oneTimePassword]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([enrollmentId, oneTimePassword]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } } @@ -2454,91 +2484,53 @@ class MultiFactorTotpSecretHostApi { /// BinaryMessenger will be used which routes to the host platform. MultiFactorTotpSecretHostApi( {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? __pigeon_binaryMessenger; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - final String __pigeon_messageChannelSuffix; + final String pigeonVar_messageChannelSuffix; Future generateQrCodeUrl( String secretKey, String? accountName, String? issuer) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.generateQrCodeUrl$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.generateQrCodeUrl$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([secretKey, accountName, issuer]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else if (__pigeon_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (__pigeon_replyList[0] as String?)!; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([secretKey, accountName, issuer]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future openInOtpApp(String secretKey, String qrCodeUrl) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.openInOtpApp$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.openInOtpApp$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = await __pigeon_channel - .send([secretKey, qrCodeUrl]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } - } -} - -class _GenerateInterfacesCodec extends StandardMessageCodec { - const _GenerateInterfacesCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is PigeonMultiFactorInfo) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([secretKey, qrCodeUrl]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return PigeonMultiFactorInfo.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } @@ -2549,37 +2541,31 @@ class GenerateInterfaces { /// BinaryMessenger will be used which routes to the host platform. GenerateInterfaces( {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? __pigeon_binaryMessenger; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _GenerateInterfacesCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - final String __pigeon_messageChannelSuffix; + final String pigeonVar_messageChannelSuffix; - Future pigeonInterface(PigeonMultiFactorInfo info) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.firebase_auth_platform_interface.GenerateInterfaces.pigeonInterface$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( - __pigeon_channelName, + Future pigeonInterface(InternalMultiFactorInfo info) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_auth_platform_interface.GenerateInterfaces.pigeonInterface$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, - ); - final List? __pigeon_replyList = - await __pigeon_channel.send([info]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { - throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], - ); - } else { - return; - } + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([info]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_firebase_auth.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_firebase_auth.dart index a57a98532592..60377db69238 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_firebase_auth.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_firebase_auth.dart @@ -64,9 +64,9 @@ abstract class FirebaseAuthPlatform extends PlatformInterface { if (currentUser != null) { currentUser as List; - final firstElement = PigeonUserInfo.decode(currentUser[0]!); + final firstElement = InternalUserInfo.decode(currentUser[0]!); final secondElement = currentUser[1]!; - currentUser = PigeonUserDetails.decode([firstElement, secondElement]); + currentUser = InternalUserDetails.decode([firstElement, secondElement]); } return FirebaseAuthPlatform.instance.delegateFor(app: app).setInitialValues( languageCode: pluginConstants['APP_LANGUAGE_CODE'], @@ -107,12 +107,15 @@ abstract class FirebaseAuthPlatform extends PlatformInterface { /// calls. @protected FirebaseAuthPlatform setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { throw UnimplementedError('setInitialValues() is not implemented'); } + /// Disposes resources tied to this platform auth instance. + Future dispose() async {} + /// Returns the current [User] if they are currently signed-in, or `null` if /// not. /// @@ -310,8 +313,12 @@ abstract class FirebaseAuthPlatform extends PlatformInterface { } /// Triggers the Firebase Authentication backend to send a password-reset - /// email to the given email address, which must correspond to an existing - /// user of your app. + /// email to the given email address. + /// + /// If email enumeration protection is enabled for the Firebase project, this + /// method may complete successfully even when the email does not correspond + /// to an existing user. This prevents apps from using password reset requests + /// to discover registered email addresses. Future sendPasswordResetEmail( String email, [ ActionCodeSettings? actionCodeSettings, @@ -717,6 +724,11 @@ abstract class FirebaseAuthPlatform extends PlatformInterface { 'revokeTokenWithAuthorizationCode() is not implemented'); } + /// Android only. Revokes the provided accessToken. Currently supports revoking Apple-issued accessToken only. + Future revokeAccessToken(String accessToken) { + throw UnimplementedError('revokeAccessToken() is not implemented'); + } + /// Initializes the reCAPTCHA Enterprise client proactively to enhance reCAPTCHA signal collection and /// to complete reCAPTCHA-protected flows in a single attempt. Future initializeRecaptchaConfig() { diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_user.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_user.dart index 3d442b33b9af..52a5988059e0 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_user.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/platform_interface/platform_interface_user.dart @@ -11,7 +11,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; /// A user account. abstract class UserPlatform extends PlatformInterface { // ignore: public_member_api_docs - UserPlatform(this.auth, this.multiFactor, PigeonUserDetails user) + UserPlatform(this.auth, this.multiFactor, InternalUserDetails user) : _user = user, super(token: _token); @@ -27,7 +27,7 @@ abstract class UserPlatform extends PlatformInterface { final MultiFactorPlatform multiFactor; - final PigeonUserDetails _user; + final InternalUserDetails _user; /// The users display name. /// diff --git a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/user_info.dart b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/user_info.dart index ddccb0f8cded..b4936290a156 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/lib/src/user_info.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/lib/src/user_info.dart @@ -14,7 +14,7 @@ class UserInfo { @protected UserInfo.fromJson(Map data) - : _data = PigeonUserInfo( + : _data = InternalUserInfo( uid: data['uid'] as String, email: data['email'] as String?, displayName: data['displayName'] as String?, @@ -29,7 +29,7 @@ class UserInfo { lastSignInTimestamp: data['lastSignInTimestamp'] as int?, ); - final PigeonUserInfo _data; + final InternalUserInfo _data; /// The users display name. /// diff --git a/packages/firebase_auth/firebase_auth_platform_interface/pigeons/messages.dart b/packages/firebase_auth/firebase_auth_platform_interface/pigeons/messages.dart index a923a5481bd8..943fa1693b87 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/pigeons/messages.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/pigeons/messages.dart @@ -27,16 +27,16 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', ), ) -class PigeonMultiFactorSession { - const PigeonMultiFactorSession({ +class InternalMultiFactorSession { + const InternalMultiFactorSession({ required this.id, }); final String id; } -class PigeonPhoneMultiFactorAssertion { - const PigeonPhoneMultiFactorAssertion({ +class InternalPhoneMultiFactorAssertion { + const InternalPhoneMultiFactorAssertion({ required this.verificationId, required this.verificationCode, }); @@ -45,8 +45,8 @@ class PigeonPhoneMultiFactorAssertion { final String verificationCode; } -class PigeonMultiFactorInfo { - const PigeonMultiFactorInfo({ +class InternalMultiFactorInfo { + const InternalMultiFactorInfo({ this.displayName, required this.enrollmentTimestamp, this.factorId, @@ -100,8 +100,8 @@ enum ActionCodeInfoOperation { revertSecondFactorAddition, } -class PigeonActionCodeInfoData { - const PigeonActionCodeInfoData({ +class InternalActionCodeInfoData { + const InternalActionCodeInfoData({ this.email, this.previousEmail, }); @@ -110,18 +110,18 @@ class PigeonActionCodeInfoData { final String? previousEmail; } -class PigeonActionCodeInfo { - const PigeonActionCodeInfo({ +class InternalActionCodeInfo { + const InternalActionCodeInfo({ required this.operation, required this.data, }); final ActionCodeInfoOperation operation; - final PigeonActionCodeInfoData data; + final InternalActionCodeInfoData data; } -class PigeonAdditionalUserInfo { - const PigeonAdditionalUserInfo({ +class InternalAdditionalUserInfo { + const InternalAdditionalUserInfo({ required this.isNewUser, required this.providerId, required this.username, @@ -136,8 +136,8 @@ class PigeonAdditionalUserInfo { final Map? profile; } -class PigeonAuthCredential { - const PigeonAuthCredential({ +class InternalAuthCredential { + const InternalAuthCredential({ required this.providerId, required this.signInMethod, required this.nativeId, @@ -150,8 +150,8 @@ class PigeonAuthCredential { final String? accessToken; } -class PigeonUserInfo { - const PigeonUserInfo({ +class InternalUserInfo { + const InternalUserInfo({ required this.uid, required this.email, required this.displayName, @@ -180,30 +180,30 @@ class PigeonUserInfo { final int? lastSignInTimestamp; } -class PigeonUserDetails { - const PigeonUserDetails({ +class InternalUserDetails { + const InternalUserDetails({ required this.userInfo, required this.providerData, }); - final PigeonUserInfo userInfo; + final InternalUserInfo userInfo; final List?> providerData; } -class PigeonUserCredential { - const PigeonUserCredential({ +class InternalUserCredential { + const InternalUserCredential({ required this.user, required this.additionalUserInfo, required this.credential, }); - final PigeonUserDetails? user; - final PigeonAdditionalUserInfo? additionalUserInfo; - final PigeonAuthCredential? credential; + final InternalUserDetails? user; + final InternalAdditionalUserInfo? additionalUserInfo; + final InternalAuthCredential? credential; } -class PigeonAuthCredentialInput { - const PigeonAuthCredentialInput({ +class InternalAuthCredentialInput { + const InternalAuthCredentialInput({ required this.providerId, required this.signInMethod, required this.token, @@ -216,8 +216,8 @@ class PigeonAuthCredentialInput { final String? accessToken; } -class PigeonActionCodeSettings { - const PigeonActionCodeSettings({ +class InternalActionCodeSettings { + const InternalActionCodeSettings({ required this.url, required this.dynamicLinkDomain, required this.linkDomain, @@ -238,8 +238,8 @@ class PigeonActionCodeSettings { final String? linkDomain; } -class PigeonFirebaseAuthSettings { - const PigeonFirebaseAuthSettings({ +class InternalFirebaseAuthSettings { + const InternalFirebaseAuthSettings({ required this.appVerificationDisabledForTesting, required this.userAccessGroup, required this.phoneNumber, @@ -254,8 +254,8 @@ class PigeonFirebaseAuthSettings { final bool? forceRecaptchaFlow; } -class PigeonSignInProvider { - const PigeonSignInProvider({ +class InternalSignInProvider { + const InternalSignInProvider({ required this.providerId, required this.scopes, required this.customParameters, @@ -266,8 +266,8 @@ class PigeonSignInProvider { final Map? customParameters; } -class PigeonVerifyPhoneNumberRequest { - const PigeonVerifyPhoneNumberRequest({ +class InternalVerifyPhoneNumberRequest { + const InternalVerifyPhoneNumberRequest({ required this.phoneNumber, required this.timeout, required this.forceResendingToken, @@ -310,7 +310,7 @@ abstract class FirebaseAuthHostApi { ); @async - PigeonActionCodeInfo checkActionCode( + InternalActionCodeInfo checkActionCode( AuthPigeonFirebaseApp app, String code, ); @@ -323,47 +323,47 @@ abstract class FirebaseAuthHostApi { ); @async - PigeonUserCredential createUserWithEmailAndPassword( + InternalUserCredential createUserWithEmailAndPassword( AuthPigeonFirebaseApp app, String email, String password, ); @async - PigeonUserCredential signInAnonymously( + InternalUserCredential signInAnonymously( AuthPigeonFirebaseApp app, ); @async - PigeonUserCredential signInWithCredential( + InternalUserCredential signInWithCredential( AuthPigeonFirebaseApp app, - Map input, + Map input, ); @async - PigeonUserCredential signInWithCustomToken( + InternalUserCredential signInWithCustomToken( AuthPigeonFirebaseApp app, String token, ); @async - PigeonUserCredential signInWithEmailAndPassword( + InternalUserCredential signInWithEmailAndPassword( AuthPigeonFirebaseApp app, String email, String password, ); @async - PigeonUserCredential signInWithEmailLink( + InternalUserCredential signInWithEmailLink( AuthPigeonFirebaseApp app, String email, String emailLink, ); @async - PigeonUserCredential signInWithProvider( + InternalUserCredential signInWithProvider( AuthPigeonFirebaseApp app, - PigeonSignInProvider signInProvider, + InternalSignInProvider signInProvider, ); @async @@ -381,14 +381,14 @@ abstract class FirebaseAuthHostApi { void sendPasswordResetEmail( AuthPigeonFirebaseApp app, String email, - PigeonActionCodeSettings? actionCodeSettings, + InternalActionCodeSettings? actionCodeSettings, ); @async void sendSignInLinkToEmail( AuthPigeonFirebaseApp app, String email, - PigeonActionCodeSettings actionCodeSettings, + InternalActionCodeSettings actionCodeSettings, ); @async @@ -400,7 +400,7 @@ abstract class FirebaseAuthHostApi { @async void setSettings( AuthPigeonFirebaseApp app, - PigeonFirebaseAuthSettings settings, + InternalFirebaseAuthSettings settings, ); @async @@ -412,7 +412,7 @@ abstract class FirebaseAuthHostApi { @async String verifyPhoneNumber( AuthPigeonFirebaseApp app, - PigeonVerifyPhoneNumberRequest request, + InternalVerifyPhoneNumberRequest request, ); @async void revokeTokenWithAuthorizationCode( @@ -420,14 +420,20 @@ abstract class FirebaseAuthHostApi { String authorizationCode, ); + @async + void revokeAccessToken( + AuthPigeonFirebaseApp app, + String accessToken, + ); + @async void initializeRecaptchaConfig( AuthPigeonFirebaseApp app, ); } -class PigeonIdTokenResult { - const PigeonIdTokenResult({ +class InternalIdTokenResult { + const InternalIdTokenResult({ required this.token, required this.expirationTimestamp, required this.authTimestamp, @@ -446,8 +452,8 @@ class PigeonIdTokenResult { final String? signInSecondFactor; } -class PigeonUserProfile { - const PigeonUserProfile({ +class InternalUserProfile { + const InternalUserProfile({ required this.displayName, required this.photoUrl, required this.displayNameChanged, @@ -468,81 +474,81 @@ abstract class FirebaseAuthUserHostApi { ); @async - PigeonIdTokenResult getIdToken( + InternalIdTokenResult getIdToken( AuthPigeonFirebaseApp app, bool forceRefresh, ); @async - PigeonUserCredential linkWithCredential( + InternalUserCredential linkWithCredential( AuthPigeonFirebaseApp app, - Map input, + Map input, ); @async - PigeonUserCredential linkWithProvider( + InternalUserCredential linkWithProvider( AuthPigeonFirebaseApp app, - PigeonSignInProvider signInProvider, + InternalSignInProvider signInProvider, ); @async - PigeonUserCredential reauthenticateWithCredential( + InternalUserCredential reauthenticateWithCredential( AuthPigeonFirebaseApp app, - Map input, + Map input, ); @async - PigeonUserCredential reauthenticateWithProvider( + InternalUserCredential reauthenticateWithProvider( AuthPigeonFirebaseApp app, - PigeonSignInProvider signInProvider, + InternalSignInProvider signInProvider, ); @async - PigeonUserDetails reload( + InternalUserDetails reload( AuthPigeonFirebaseApp app, ); @async void sendEmailVerification( AuthPigeonFirebaseApp app, - PigeonActionCodeSettings? actionCodeSettings, + InternalActionCodeSettings? actionCodeSettings, ); @async - PigeonUserCredential unlink( + InternalUserCredential unlink( AuthPigeonFirebaseApp app, String providerId, ); @async - PigeonUserDetails updateEmail( + InternalUserDetails updateEmail( AuthPigeonFirebaseApp app, String newEmail, ); @async - PigeonUserDetails updatePassword( + InternalUserDetails updatePassword( AuthPigeonFirebaseApp app, String newPassword, ); @async - PigeonUserDetails updatePhoneNumber( + InternalUserDetails updatePhoneNumber( AuthPigeonFirebaseApp app, - Map input, + Map input, ); @async - PigeonUserDetails updateProfile( + InternalUserDetails updateProfile( AuthPigeonFirebaseApp app, - PigeonUserProfile profile, + InternalUserProfile profile, ); @async void verifyBeforeUpdateEmail( AuthPigeonFirebaseApp app, String newEmail, - PigeonActionCodeSettings? actionCodeSettings, + InternalActionCodeSettings? actionCodeSettings, ); } @@ -551,7 +557,7 @@ abstract class MultiFactorUserHostApi { @async void enrollPhone( AuthPigeonFirebaseApp app, - PigeonPhoneMultiFactorAssertion assertion, + InternalPhoneMultiFactorAssertion assertion, String? displayName, ); @@ -563,7 +569,7 @@ abstract class MultiFactorUserHostApi { ); @async - PigeonMultiFactorSession getSession( + InternalMultiFactorSession getSession( AuthPigeonFirebaseApp app, ); @@ -574,7 +580,7 @@ abstract class MultiFactorUserHostApi { ); @async - List getEnrolledFactors( + List getEnrolledFactors( AuthPigeonFirebaseApp app, ); } @@ -582,15 +588,15 @@ abstract class MultiFactorUserHostApi { @HostApi(dartHostTestHandler: 'TestMultiFactoResolverHostApi') abstract class MultiFactoResolverHostApi { @async - PigeonUserCredential resolveSignIn( + InternalUserCredential resolveSignIn( String resolverId, - PigeonPhoneMultiFactorAssertion? assertion, + InternalPhoneMultiFactorAssertion? assertion, String? totpAssertionId, ); } -class PigeonTotpSecret { - const PigeonTotpSecret({ +class InternalTotpSecret { + const InternalTotpSecret({ required this.codeIntervalSeconds, required this.codeLength, required this.enrollmentCompletionDeadline, @@ -608,7 +614,7 @@ class PigeonTotpSecret { @HostApi(dartHostTestHandler: 'TestMultiFactoResolverHostApi') abstract class MultiFactorTotpHostApi { @async - PigeonTotpSecret generateSecret( + InternalTotpSecret generateSecret( String sessionId, ); @@ -644,5 +650,5 @@ abstract class MultiFactorTotpSecretHostApi { /// Only used to generate the object interface that are use outside of the Pigeon interface @HostApi() abstract class GenerateInterfaces { - void pigeonInterface(PigeonMultiFactorInfo info); + void pigeonInterface(InternalMultiFactorInfo info); } diff --git a/packages/firebase_auth/firebase_auth_platform_interface/pubspec.yaml b/packages/firebase_auth/firebase_auth_platform_interface/pubspec.yaml index fb2e20ce5f56..53a9c3386020 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/pubspec.yaml +++ b/packages/firebase_auth/firebase_auth_platform_interface/pubspec.yaml @@ -4,16 +4,17 @@ homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_au repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_auth/firebase_auth_platform_interface # 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: 8.1.8 +version: 9.0.3 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.16.0' dependencies: - _flutterfire_internals: ^1.3.68 + _flutterfire_internals: ^1.3.73 collection: ^1.16.0 - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter http: ^1.1.0 @@ -21,14 +22,12 @@ dependencies: plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.4.0 - pigeon: 19.0.0 + pigeon: 26.3.4 # NOTE: This is a temporary workaround for Flutter 3.13 watcher: ^1.1.0 -dependency_overrides: - watcher: ^1.1.0 diff --git a/packages/firebase_auth/firebase_auth_platform_interface/test/id_token_result_test.dart b/packages/firebase_auth/firebase_auth_platform_interface/test/id_token_result_test.dart index 556f6645a33e..9a13f6b58be6 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/test/id_token_result_test.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/test/id_token_result_test.dart @@ -18,7 +18,7 @@ void main() { 'claim1': 'value1', }; - final kMockData = PigeonIdTokenResult( + final kMockData = InternalIdTokenResult( claims: kMockClaims, issuedAtTimestamp: kMockIssuedAtTimestamp, authTimestamp: kMockAuthTimestamp, @@ -52,7 +52,7 @@ void main() { }); test('returns null when data[claims] is null', () { - final kMockData = PigeonIdTokenResult( + final kMockData = InternalIdTokenResult( issuedAtTimestamp: kMockIssuedAtTimestamp, authTimestamp: kMockAuthTimestamp, expirationTimestamp: kMockExpirationTimestamp, diff --git a/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/method_channel_user_credential_test.dart b/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/method_channel_user_credential_test.dart index d35bcefe3bba..47fcbbd849d5 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/method_channel_user_credential_test.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/method_channel_user_credential_test.dart @@ -24,9 +24,9 @@ void main() { group('$MethodChannelUserCredential()', () { late MethodChannelUserCredential userCredential; - PigeonUserCredential userData = PigeonUserCredential( - user: PigeonUserDetails( - userInfo: PigeonUserInfo( + InternalUserCredential userData = InternalUserCredential( + user: InternalUserDetails( + userInfo: InternalUserInfo( uid: kMockUid, email: kMockEmail, isAnonymous: false, @@ -34,13 +34,13 @@ void main() { ), providerData: [], ), - additionalUserInfo: PigeonAdditionalUserInfo( + additionalUserInfo: InternalAdditionalUserInfo( isNewUser: true, profile: {'foo': 'bar'}, providerId: 'info$kMockProviderId', username: 'info$kMockUsername', ), - credential: PigeonAuthCredential( + credential: InternalAuthCredential( providerId: 'auth$kMockProviderId', signInMethod: kMockSignInMethod, nativeId: 0, @@ -55,9 +55,9 @@ void main() { }); setUp(() { - final kMockInitialUserData = PigeonUserCredential( - user: PigeonUserDetails( - userInfo: PigeonUserInfo( + final kMockInitialUserData = InternalUserCredential( + user: InternalUserDetails( + userInfo: InternalUserInfo( uid: kMockUid, email: kMockEmail, isAnonymous: false, @@ -65,13 +65,13 @@ void main() { ), providerData: [], ), - additionalUserInfo: PigeonAdditionalUserInfo( + additionalUserInfo: InternalAdditionalUserInfo( isNewUser: true, profile: {'foo': 'bar'}, providerId: 'info$kMockProviderId', username: 'info$kMockUsername', ), - credential: PigeonAuthCredential( + credential: InternalAuthCredential( providerId: 'auth$kMockProviderId', signInMethod: kMockSignInMethod, nativeId: 0, diff --git a/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/utils_tests/exception_test.dart b/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/utils_tests/exception_test.dart index 2fdfb6df6271..5669b1e6aa9e 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/utils_tests/exception_test.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/test/method_channel_tests/utils_tests/exception_test.dart @@ -50,9 +50,47 @@ void main() { ), ); }); + + test('should catch a [PlatformException] with non-Map details', () async { + PlatformException platformException = PlatformException( + code: 'ERROR_INTERNAL_ERROR', + message: 'An internal error has occurred', + details: 'Native error details', + ); + + expect( + () => convertPlatformException(platformException, StackTrace.empty), + throwsA( + isA() + .having((e) => e.code, 'code', 'internal-error') + .having( + (e) => e.message, + 'message', + 'An internal error has occurred', + ), + ), + ); + }); }); group('platformExceptionToFirebaseAuthException()', () { + test('handles non-Map pigeon details', () { + PlatformException platformException = PlatformException( + code: 'ERROR_INTERNAL_ERROR', + message: 'An internal error has occurred', + details: 'Native error details', + ); + + FirebaseAuthException result = platformExceptionToFirebaseAuthException( + platformException, + ) as FirebaseAuthException; + + expect(result.code, equals('internal-error')); + expect(result.message, equals('An internal error has occurred')); + expect(result.email, isNull); + expect(result.credential, isNull); + }); + test('sets code to default value', () { AuthCredential authCredential = const AuthCredential( providerId: 'testProviderId', diff --git a/packages/firebase_auth/firebase_auth_platform_interface/test/pigeon/test_api.dart b/packages/firebase_auth/firebase_auth_platform_interface/test/pigeon/test_api.dart index 91d5f6ef9641..5dcc22207592 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/test/pigeon/test_api.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/test/pigeon/test_api.dart @@ -1,9 +1,9 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v19.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers +// 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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -13,64 +13,73 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:firebase_auth_platform_interface/src/pigeon/messages.pigeon.dart'; -class _TestFirebaseAuthHostApiCodec extends StandardMessageCodec { - const _TestFirebaseAuthHostApiCodec(); +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is AuthPigeonFirebaseApp) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfo) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is ActionCodeInfoOperation) { buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfoData) { + writeValue(buffer, value.index); + } else if (value is InternalMultiFactorSession) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeSettings) { + } else if (value is InternalPhoneMultiFactorAssertion) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is PigeonAdditionalUserInfo) { + } else if (value is InternalMultiFactorInfo) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PigeonAuthCredential) { + } else if (value is AuthPigeonFirebaseApp) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is PigeonFirebaseAuthSettings) { + } else if (value is InternalActionCodeInfoData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is PigeonIdTokenResult) { + } else if (value is InternalActionCodeInfo) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorInfo) { + } else if (value is InternalAdditionalUserInfo) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorSession) { + } else if (value is InternalAuthCredential) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { + } else if (value is InternalUserInfo) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PigeonSignInProvider) { + } else if (value is InternalUserDetails) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PigeonTotpSecret) { + } else if (value is InternalUserCredential) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is PigeonUserCredential) { + } else if (value is InternalAuthCredentialInput) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is PigeonUserDetails) { + } else if (value is InternalActionCodeSettings) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is PigeonUserInfo) { + } else if (value is InternalFirebaseAuthSettings) { buffer.putUint8(143); writeValue(buffer, value.encode()); - } else if (value is PigeonUserProfile) { + } else if (value is InternalSignInProvider) { buffer.putUint8(144); writeValue(buffer, value.encode()); - } else if (value is PigeonVerifyPhoneNumberRequest) { + } else if (value is InternalVerifyPhoneNumberRequest) { buffer.putUint8(145); writeValue(buffer, value.encode()); + } else if (value is InternalIdTokenResult) { + buffer.putUint8(146); + writeValue(buffer, value.encode()); + } else if (value is InternalUserProfile) { + buffer.putUint8(147); + writeValue(buffer, value.encode()); + } else if (value is InternalTotpSecret) { + buffer.putUint8(148); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -79,42 +88,47 @@ class _TestFirebaseAuthHostApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: - return AuthPigeonFirebaseApp.decode(readValue(buffer)!); case 129: - return PigeonActionCodeInfo.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : ActionCodeInfoOperation.values[value]; case 130: - return PigeonActionCodeInfoData.decode(readValue(buffer)!); + return InternalMultiFactorSession.decode(readValue(buffer)!); case 131: - return PigeonActionCodeSettings.decode(readValue(buffer)!); + return InternalPhoneMultiFactorAssertion.decode(readValue(buffer)!); case 132: - return PigeonAdditionalUserInfo.decode(readValue(buffer)!); + return InternalMultiFactorInfo.decode(readValue(buffer)!); case 133: - return PigeonAuthCredential.decode(readValue(buffer)!); + return AuthPigeonFirebaseApp.decode(readValue(buffer)!); case 134: - return PigeonFirebaseAuthSettings.decode(readValue(buffer)!); + return InternalActionCodeInfoData.decode(readValue(buffer)!); case 135: - return PigeonIdTokenResult.decode(readValue(buffer)!); + return InternalActionCodeInfo.decode(readValue(buffer)!); case 136: - return PigeonMultiFactorInfo.decode(readValue(buffer)!); + return InternalAdditionalUserInfo.decode(readValue(buffer)!); case 137: - return PigeonMultiFactorSession.decode(readValue(buffer)!); + return InternalAuthCredential.decode(readValue(buffer)!); case 138: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); + return InternalUserInfo.decode(readValue(buffer)!); case 139: - return PigeonSignInProvider.decode(readValue(buffer)!); + return InternalUserDetails.decode(readValue(buffer)!); case 140: - return PigeonTotpSecret.decode(readValue(buffer)!); + return InternalUserCredential.decode(readValue(buffer)!); case 141: - return PigeonUserCredential.decode(readValue(buffer)!); + return InternalAuthCredentialInput.decode(readValue(buffer)!); case 142: - return PigeonUserDetails.decode(readValue(buffer)!); + return InternalActionCodeSettings.decode(readValue(buffer)!); case 143: - return PigeonUserInfo.decode(readValue(buffer)!); + return InternalFirebaseAuthSettings.decode(readValue(buffer)!); case 144: - return PigeonUserProfile.decode(readValue(buffer)!); + return InternalSignInProvider.decode(readValue(buffer)!); case 145: - return PigeonVerifyPhoneNumberRequest.decode(readValue(buffer)!); + return InternalVerifyPhoneNumberRequest.decode(readValue(buffer)!); + case 146: + return InternalIdTokenResult.decode(readValue(buffer)!); + case 147: + return InternalUserProfile.decode(readValue(buffer)!); + case 148: + return InternalTotpSecret.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -124,8 +138,7 @@ class _TestFirebaseAuthHostApiCodec extends StandardMessageCodec { abstract class TestFirebaseAuthHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - _TestFirebaseAuthHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); Future registerIdTokenListener(AuthPigeonFirebaseApp app); @@ -135,58 +148,60 @@ abstract class TestFirebaseAuthHostApi { Future applyActionCode(AuthPigeonFirebaseApp app, String code); - Future checkActionCode( + Future checkActionCode( AuthPigeonFirebaseApp app, String code); Future confirmPasswordReset( AuthPigeonFirebaseApp app, String code, String newPassword); - Future createUserWithEmailAndPassword( + Future createUserWithEmailAndPassword( AuthPigeonFirebaseApp app, String email, String password); - Future signInAnonymously(AuthPigeonFirebaseApp app); + Future signInAnonymously(AuthPigeonFirebaseApp app); - Future signInWithCredential( + Future signInWithCredential( AuthPigeonFirebaseApp app, Map input); - Future signInWithCustomToken( + Future signInWithCustomToken( AuthPigeonFirebaseApp app, String token); - Future signInWithEmailAndPassword( + Future signInWithEmailAndPassword( AuthPigeonFirebaseApp app, String email, String password); - Future signInWithEmailLink( + Future signInWithEmailLink( AuthPigeonFirebaseApp app, String email, String emailLink); - Future signInWithProvider( - AuthPigeonFirebaseApp app, PigeonSignInProvider signInProvider); + Future signInWithProvider( + AuthPigeonFirebaseApp app, InternalSignInProvider signInProvider); Future signOut(AuthPigeonFirebaseApp app); - Future> fetchSignInMethodsForEmail( + Future> fetchSignInMethodsForEmail( AuthPigeonFirebaseApp app, String email); Future sendPasswordResetEmail(AuthPigeonFirebaseApp app, String email, - PigeonActionCodeSettings? actionCodeSettings); + InternalActionCodeSettings? actionCodeSettings); Future sendSignInLinkToEmail(AuthPigeonFirebaseApp app, String email, - PigeonActionCodeSettings actionCodeSettings); + InternalActionCodeSettings actionCodeSettings); Future setLanguageCode( AuthPigeonFirebaseApp app, String? languageCode); Future setSettings( - AuthPigeonFirebaseApp app, PigeonFirebaseAuthSettings settings); + AuthPigeonFirebaseApp app, InternalFirebaseAuthSettings settings); Future verifyPasswordResetCode( AuthPigeonFirebaseApp app, String code); Future verifyPhoneNumber( - AuthPigeonFirebaseApp app, PigeonVerifyPhoneNumberRequest request); + AuthPigeonFirebaseApp app, InternalVerifyPhoneNumberRequest request); Future revokeTokenWithAuthorizationCode( AuthPigeonFirebaseApp app, String authorizationCode); + Future revokeAccessToken(AuthPigeonFirebaseApp app, String accessToken); + Future initializeRecaptchaConfig(AuthPigeonFirebaseApp app); static void setUp( @@ -197,27 +212,22 @@ abstract class TestFirebaseAuthHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerIdTokenListener$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerIdTokenListener was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerIdTokenListener was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - final String output = await api.registerIdTokenListener(arg_app!); + final String output = await api.registerIdTokenListener(arg_app); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -229,27 +239,22 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerAuthStateListener$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerAuthStateListener was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.registerAuthStateListener was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - final String output = await api.registerAuthStateListener(arg_app!); + final String output = await api.registerAuthStateListener(arg_app); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -261,33 +266,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.useEmulator$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.useEmulator was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.useEmulator was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_host = (args[1] as String?); - assert(arg_host != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.useEmulator was null, expected non-null String.'); - final int? arg_port = (args[2] as int?); - assert(arg_port != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.useEmulator was null, expected non-null int.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_host = args[1]! as String; + final int arg_port = args[2]! as int; try { - await api.useEmulator(arg_app!, arg_host!, arg_port!); + await api.useEmulator(arg_app, arg_host, arg_port); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -299,30 +295,23 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.applyActionCode$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.applyActionCode was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.applyActionCode was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_code = (args[1] as String?); - assert(arg_code != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.applyActionCode was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_code = args[1]! as String; try { - await api.applyActionCode(arg_app!, arg_code!); + await api.applyActionCode(arg_app, arg_code); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -334,31 +323,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.checkActionCode$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.checkActionCode was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.checkActionCode was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_code = (args[1] as String?); - assert(arg_code != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.checkActionCode was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_code = args[1]! as String; try { - final PigeonActionCodeInfo output = - await api.checkActionCode(arg_app!, arg_code!); + final InternalActionCodeInfo output = + await api.checkActionCode(arg_app, arg_code); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -370,34 +352,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.confirmPasswordReset$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.confirmPasswordReset was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.confirmPasswordReset was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_code = (args[1] as String?); - assert(arg_code != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.confirmPasswordReset was null, expected non-null String.'); - final String? arg_newPassword = (args[2] as String?); - assert(arg_newPassword != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.confirmPasswordReset was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_code = args[1]! as String; + final String arg_newPassword = args[2]! as String; try { - await api.confirmPasswordReset( - arg_app!, arg_code!, arg_newPassword!); + await api.confirmPasswordReset(arg_app, arg_code, arg_newPassword); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -409,35 +381,26 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.createUserWithEmailAndPassword$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.createUserWithEmailAndPassword was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.createUserWithEmailAndPassword was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_email = (args[1] as String?); - assert(arg_email != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.createUserWithEmailAndPassword was null, expected non-null String.'); - final String? arg_password = (args[2] as String?); - assert(arg_password != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.createUserWithEmailAndPassword was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_email = args[1]! as String; + final String arg_password = args[2]! as String; try { - final PigeonUserCredential output = + final InternalUserCredential output = await api.createUserWithEmailAndPassword( - arg_app!, arg_email!, arg_password!); + arg_app, arg_email, arg_password); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -449,28 +412,23 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInAnonymously$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInAnonymously was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInAnonymously was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - final PigeonUserCredential output = - await api.signInAnonymously(arg_app!); + final InternalUserCredential output = + await api.signInAnonymously(arg_app); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -482,32 +440,25 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCredential$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCredential was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCredential was null, expected non-null AuthPigeonFirebaseApp.'); - final Map? arg_input = - (args[1] as Map?)?.cast(); - assert(arg_input != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCredential was null, expected non-null Map.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final Map arg_input = + (args[1]! as Map).cast(); try { - final PigeonUserCredential output = - await api.signInWithCredential(arg_app!, arg_input!); + final InternalUserCredential output = + await api.signInWithCredential(arg_app, arg_input); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -519,31 +470,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCustomToken$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCustomToken was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCustomToken was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_token = (args[1] as String?); - assert(arg_token != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithCustomToken was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_token = args[1]! as String; try { - final PigeonUserCredential output = - await api.signInWithCustomToken(arg_app!, arg_token!); + final InternalUserCredential output = + await api.signInWithCustomToken(arg_app, arg_token); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -555,35 +499,25 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailAndPassword$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailAndPassword was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailAndPassword was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_email = (args[1] as String?); - assert(arg_email != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailAndPassword was null, expected non-null String.'); - final String? arg_password = (args[2] as String?); - assert(arg_password != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailAndPassword was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_email = args[1]! as String; + final String arg_password = args[2]! as String; try { - final PigeonUserCredential output = - await api.signInWithEmailAndPassword( - arg_app!, arg_email!, arg_password!); + final InternalUserCredential output = await api + .signInWithEmailAndPassword(arg_app, arg_email, arg_password); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -595,34 +529,25 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailLink$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailLink was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailLink was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_email = (args[1] as String?); - assert(arg_email != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailLink was null, expected non-null String.'); - final String? arg_emailLink = (args[2] as String?); - assert(arg_emailLink != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithEmailLink was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_email = args[1]! as String; + final String arg_emailLink = args[2]! as String; try { - final PigeonUserCredential output = await api.signInWithEmailLink( - arg_app!, arg_email!, arg_emailLink!); + final InternalUserCredential output = await api.signInWithEmailLink( + arg_app, arg_email, arg_emailLink); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -634,32 +559,25 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithProvider$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithProvider was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithProvider was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonSignInProvider? arg_signInProvider = - (args[1] as PigeonSignInProvider?); - assert(arg_signInProvider != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signInWithProvider was null, expected non-null PigeonSignInProvider.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalSignInProvider arg_signInProvider = + args[1]! as InternalSignInProvider; try { - final PigeonUserCredential output = - await api.signInWithProvider(arg_app!, arg_signInProvider!); + final InternalUserCredential output = + await api.signInWithProvider(arg_app, arg_signInProvider); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -671,27 +589,22 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signOut$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signOut was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.signOut was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - await api.signOut(arg_app!); + await api.signOut(arg_app); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -703,31 +616,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.fetchSignInMethodsForEmail$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.fetchSignInMethodsForEmail was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.fetchSignInMethodsForEmail was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_email = (args[1] as String?); - assert(arg_email != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.fetchSignInMethodsForEmail was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_email = args[1]! as String; try { - final List output = - await api.fetchSignInMethodsForEmail(arg_app!, arg_email!); + final List output = + await api.fetchSignInMethodsForEmail(arg_app, arg_email); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -739,33 +645,26 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendPasswordResetEmail$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendPasswordResetEmail was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendPasswordResetEmail was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_email = (args[1] as String?); - assert(arg_email != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendPasswordResetEmail was null, expected non-null String.'); - final PigeonActionCodeSettings? arg_actionCodeSettings = - (args[2] as PigeonActionCodeSettings?); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_email = args[1]! as String; + final InternalActionCodeSettings? arg_actionCodeSettings = + args[2] as InternalActionCodeSettings?; try { await api.sendPasswordResetEmail( - arg_app!, arg_email!, arg_actionCodeSettings); + arg_app, arg_email, arg_actionCodeSettings); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -777,35 +676,26 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendSignInLinkToEmail$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendSignInLinkToEmail was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendSignInLinkToEmail was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_email = (args[1] as String?); - assert(arg_email != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendSignInLinkToEmail was null, expected non-null String.'); - final PigeonActionCodeSettings? arg_actionCodeSettings = - (args[2] as PigeonActionCodeSettings?); - assert(arg_actionCodeSettings != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.sendSignInLinkToEmail was null, expected non-null PigeonActionCodeSettings.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_email = args[1]! as String; + final InternalActionCodeSettings arg_actionCodeSettings = + args[2]! as InternalActionCodeSettings; try { await api.sendSignInLinkToEmail( - arg_app!, arg_email!, arg_actionCodeSettings!); + arg_app, arg_email, arg_actionCodeSettings); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -817,29 +707,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setLanguageCode$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setLanguageCode was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setLanguageCode was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_languageCode = (args[1] as String?); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String? arg_languageCode = args[1] as String?; try { final String output = - await api.setLanguageCode(arg_app!, arg_languageCode); + await api.setLanguageCode(arg_app, arg_languageCode); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -851,31 +736,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setSettings$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setSettings was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setSettings was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonFirebaseAuthSettings? arg_settings = - (args[1] as PigeonFirebaseAuthSettings?); - assert(arg_settings != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.setSettings was null, expected non-null PigeonFirebaseAuthSettings.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalFirebaseAuthSettings arg_settings = + args[1]! as InternalFirebaseAuthSettings; try { - await api.setSettings(arg_app!, arg_settings!); + await api.setSettings(arg_app, arg_settings); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -887,31 +765,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPasswordResetCode$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPasswordResetCode was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPasswordResetCode was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_code = (args[1] as String?); - assert(arg_code != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPasswordResetCode was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_code = args[1]! as String; try { final String output = - await api.verifyPasswordResetCode(arg_app!, arg_code!); + await api.verifyPasswordResetCode(arg_app, arg_code); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -923,32 +794,25 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPhoneNumber$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPhoneNumber was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPhoneNumber was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonVerifyPhoneNumberRequest? arg_request = - (args[1] as PigeonVerifyPhoneNumberRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.verifyPhoneNumber was null, expected non-null PigeonVerifyPhoneNumberRequest.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalVerifyPhoneNumberRequest arg_request = + args[1]! as InternalVerifyPhoneNumberRequest; try { final String output = - await api.verifyPhoneNumber(arg_app!, arg_request!); + await api.verifyPhoneNumber(arg_app, arg_request); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -960,31 +824,24 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeTokenWithAuthorizationCode$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeTokenWithAuthorizationCode was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeTokenWithAuthorizationCode was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_authorizationCode = (args[1] as String?); - assert(arg_authorizationCode != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeTokenWithAuthorizationCode was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_authorizationCode = args[1]! as String; try { await api.revokeTokenWithAuthorizationCode( - arg_app!, arg_authorizationCode!); + arg_app, arg_authorizationCode); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -996,27 +853,23 @@ abstract class TestFirebaseAuthHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig$messageChannelSuffix', + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.revokeAccessToken$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_accessToken = args[1]! as String; try { - await api.initializeRecaptchaConfig(arg_app!); + await api.revokeAccessToken(arg_app, arg_accessToken); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1027,113 +880,32 @@ abstract class TestFirebaseAuthHostApi { }); } } - } -} - -class _TestFirebaseAuthUserHostApiCodec extends StandardMessageCodec { - const _TestFirebaseAuthUserHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is AuthPigeonFirebaseApp) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfo) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeInfoData) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is PigeonActionCodeSettings) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is PigeonAdditionalUserInfo) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is PigeonAuthCredential) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else if (value is PigeonFirebaseAuthSettings) { - buffer.putUint8(134); - writeValue(buffer, value.encode()); - } else if (value is PigeonIdTokenResult) { - buffer.putUint8(135); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorInfo) { - buffer.putUint8(136); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorSession) { - buffer.putUint8(137); - writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { - buffer.putUint8(138); - writeValue(buffer, value.encode()); - } else if (value is PigeonSignInProvider) { - buffer.putUint8(139); - writeValue(buffer, value.encode()); - } else if (value is PigeonTotpSecret) { - buffer.putUint8(140); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserCredential) { - buffer.putUint8(141); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserDetails) { - buffer.putUint8(142); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserInfo) { - buffer.putUint8(143); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserProfile) { - buffer.putUint8(144); - writeValue(buffer, value.encode()); - } else if (value is PigeonVerifyPhoneNumberRequest) { - buffer.putUint8(145); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return AuthPigeonFirebaseApp.decode(readValue(buffer)!); - case 129: - return PigeonActionCodeInfo.decode(readValue(buffer)!); - case 130: - return PigeonActionCodeInfoData.decode(readValue(buffer)!); - case 131: - return PigeonActionCodeSettings.decode(readValue(buffer)!); - case 132: - return PigeonAdditionalUserInfo.decode(readValue(buffer)!); - case 133: - return PigeonAuthCredential.decode(readValue(buffer)!); - case 134: - return PigeonFirebaseAuthSettings.decode(readValue(buffer)!); - case 135: - return PigeonIdTokenResult.decode(readValue(buffer)!); - case 136: - return PigeonMultiFactorInfo.decode(readValue(buffer)!); - case 137: - return PigeonMultiFactorSession.decode(readValue(buffer)!); - case 138: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); - case 139: - return PigeonSignInProvider.decode(readValue(buffer)!); - case 140: - return PigeonTotpSecret.decode(readValue(buffer)!); - case 141: - return PigeonUserCredential.decode(readValue(buffer)!); - case 142: - return PigeonUserDetails.decode(readValue(buffer)!); - case 143: - return PigeonUserInfo.decode(readValue(buffer)!); - case 144: - return PigeonUserProfile.decode(readValue(buffer)!); - case 145: - return PigeonVerifyPhoneNumberRequest.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthHostApi.initializeRecaptchaConfig$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + try { + await api.initializeRecaptchaConfig(arg_app); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } } } } @@ -1141,48 +913,47 @@ class _TestFirebaseAuthUserHostApiCodec extends StandardMessageCodec { abstract class TestFirebaseAuthUserHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - _TestFirebaseAuthUserHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); Future delete(AuthPigeonFirebaseApp app); - Future getIdToken( + Future getIdToken( AuthPigeonFirebaseApp app, bool forceRefresh); - Future linkWithCredential( + Future linkWithCredential( AuthPigeonFirebaseApp app, Map input); - Future linkWithProvider( - AuthPigeonFirebaseApp app, PigeonSignInProvider signInProvider); + Future linkWithProvider( + AuthPigeonFirebaseApp app, InternalSignInProvider signInProvider); - Future reauthenticateWithCredential( + Future reauthenticateWithCredential( AuthPigeonFirebaseApp app, Map input); - Future reauthenticateWithProvider( - AuthPigeonFirebaseApp app, PigeonSignInProvider signInProvider); + Future reauthenticateWithProvider( + AuthPigeonFirebaseApp app, InternalSignInProvider signInProvider); - Future reload(AuthPigeonFirebaseApp app); + Future reload(AuthPigeonFirebaseApp app); - Future sendEmailVerification( - AuthPigeonFirebaseApp app, PigeonActionCodeSettings? actionCodeSettings); + Future sendEmailVerification(AuthPigeonFirebaseApp app, + InternalActionCodeSettings? actionCodeSettings); - Future unlink( + Future unlink( AuthPigeonFirebaseApp app, String providerId); - Future updateEmail( + Future updateEmail( AuthPigeonFirebaseApp app, String newEmail); - Future updatePassword( + Future updatePassword( AuthPigeonFirebaseApp app, String newPassword); - Future updatePhoneNumber( + Future updatePhoneNumber( AuthPigeonFirebaseApp app, Map input); - Future updateProfile( - AuthPigeonFirebaseApp app, PigeonUserProfile profile); + Future updateProfile( + AuthPigeonFirebaseApp app, InternalUserProfile profile); Future verifyBeforeUpdateEmail(AuthPigeonFirebaseApp app, - String newEmail, PigeonActionCodeSettings? actionCodeSettings); + String newEmail, InternalActionCodeSettings? actionCodeSettings); static void setUp( TestFirebaseAuthUserHostApi? api, { @@ -1192,27 +963,22 @@ abstract class TestFirebaseAuthUserHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.delete$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.delete was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.delete was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - await api.delete(arg_app!); + await api.delete(arg_app); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1224,31 +990,24 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.getIdToken$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.getIdToken was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.getIdToken was null, expected non-null AuthPigeonFirebaseApp.'); - final bool? arg_forceRefresh = (args[1] as bool?); - assert(arg_forceRefresh != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.getIdToken was null, expected non-null bool.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final bool arg_forceRefresh = args[1]! as bool; try { - final PigeonIdTokenResult output = - await api.getIdToken(arg_app!, arg_forceRefresh!); + final InternalIdTokenResult output = + await api.getIdToken(arg_app, arg_forceRefresh); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1260,32 +1019,25 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithCredential$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithCredential was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithCredential was null, expected non-null AuthPigeonFirebaseApp.'); - final Map? arg_input = - (args[1] as Map?)?.cast(); - assert(arg_input != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithCredential was null, expected non-null Map.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final Map arg_input = + (args[1]! as Map).cast(); try { - final PigeonUserCredential output = - await api.linkWithCredential(arg_app!, arg_input!); + final InternalUserCredential output = + await api.linkWithCredential(arg_app, arg_input); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1297,32 +1049,25 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithProvider$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithProvider was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithProvider was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonSignInProvider? arg_signInProvider = - (args[1] as PigeonSignInProvider?); - assert(arg_signInProvider != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.linkWithProvider was null, expected non-null PigeonSignInProvider.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalSignInProvider arg_signInProvider = + args[1]! as InternalSignInProvider; try { - final PigeonUserCredential output = - await api.linkWithProvider(arg_app!, arg_signInProvider!); + final InternalUserCredential output = + await api.linkWithProvider(arg_app, arg_signInProvider); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1334,32 +1079,25 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithCredential$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithCredential was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithCredential was null, expected non-null AuthPigeonFirebaseApp.'); - final Map? arg_input = - (args[1] as Map?)?.cast(); - assert(arg_input != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithCredential was null, expected non-null Map.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final Map arg_input = + (args[1]! as Map).cast(); try { - final PigeonUserCredential output = - await api.reauthenticateWithCredential(arg_app!, arg_input!); + final InternalUserCredential output = + await api.reauthenticateWithCredential(arg_app, arg_input); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1371,32 +1109,25 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithProvider$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithProvider was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithProvider was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonSignInProvider? arg_signInProvider = - (args[1] as PigeonSignInProvider?); - assert(arg_signInProvider != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reauthenticateWithProvider was null, expected non-null PigeonSignInProvider.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalSignInProvider arg_signInProvider = + args[1]! as InternalSignInProvider; try { - final PigeonUserCredential output = await api - .reauthenticateWithProvider(arg_app!, arg_signInProvider!); + final InternalUserCredential output = await api + .reauthenticateWithProvider(arg_app, arg_signInProvider); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1408,27 +1139,22 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reload$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reload was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.reload was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - final PigeonUserDetails output = await api.reload(arg_app!); + final InternalUserDetails output = await api.reload(arg_app); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1440,29 +1166,24 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.sendEmailVerification$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.sendEmailVerification was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.sendEmailVerification was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonActionCodeSettings? arg_actionCodeSettings = - (args[1] as PigeonActionCodeSettings?); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalActionCodeSettings? arg_actionCodeSettings = + args[1] as InternalActionCodeSettings?; try { - await api.sendEmailVerification(arg_app!, arg_actionCodeSettings); + await api.sendEmailVerification(arg_app, arg_actionCodeSettings); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1474,31 +1195,24 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.unlink$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.unlink was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.unlink was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_providerId = (args[1] as String?); - assert(arg_providerId != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.unlink was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_providerId = args[1]! as String; try { - final PigeonUserCredential output = - await api.unlink(arg_app!, arg_providerId!); + final InternalUserCredential output = + await api.unlink(arg_app, arg_providerId); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1510,31 +1224,24 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateEmail$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateEmail was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateEmail was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_newEmail = (args[1] as String?); - assert(arg_newEmail != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateEmail was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_newEmail = args[1]! as String; try { - final PigeonUserDetails output = - await api.updateEmail(arg_app!, arg_newEmail!); + final InternalUserDetails output = + await api.updateEmail(arg_app, arg_newEmail); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1546,31 +1253,24 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePassword$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePassword was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePassword was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_newPassword = (args[1] as String?); - assert(arg_newPassword != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePassword was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_newPassword = args[1]! as String; try { - final PigeonUserDetails output = - await api.updatePassword(arg_app!, arg_newPassword!); + final InternalUserDetails output = + await api.updatePassword(arg_app, arg_newPassword); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1582,32 +1282,25 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePhoneNumber$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePhoneNumber was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePhoneNumber was null, expected non-null AuthPigeonFirebaseApp.'); - final Map? arg_input = - (args[1] as Map?)?.cast(); - assert(arg_input != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updatePhoneNumber was null, expected non-null Map.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final Map arg_input = + (args[1]! as Map).cast(); try { - final PigeonUserDetails output = - await api.updatePhoneNumber(arg_app!, arg_input!); + final InternalUserDetails output = + await api.updatePhoneNumber(arg_app, arg_input); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1619,32 +1312,25 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateProfile$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateProfile was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateProfile was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonUserProfile? arg_profile = - (args[1] as PigeonUserProfile?); - assert(arg_profile != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.updateProfile was null, expected non-null PigeonUserProfile.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalUserProfile arg_profile = + args[1]! as InternalUserProfile; try { - final PigeonUserDetails output = - await api.updateProfile(arg_app!, arg_profile!); + final InternalUserDetails output = + await api.updateProfile(arg_app, arg_profile); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1656,33 +1342,26 @@ abstract class TestFirebaseAuthUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.verifyBeforeUpdateEmail$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.verifyBeforeUpdateEmail was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.verifyBeforeUpdateEmail was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_newEmail = (args[1] as String?); - assert(arg_newEmail != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.FirebaseAuthUserHostApi.verifyBeforeUpdateEmail was null, expected non-null String.'); - final PigeonActionCodeSettings? arg_actionCodeSettings = - (args[2] as PigeonActionCodeSettings?); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_newEmail = args[1]! as String; + final InternalActionCodeSettings? arg_actionCodeSettings = + args[2] as InternalActionCodeSettings?; try { await api.verifyBeforeUpdateEmail( - arg_app!, arg_newEmail!, arg_actionCodeSettings); + arg_app, arg_newEmail, arg_actionCodeSettings); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1696,61 +1375,22 @@ abstract class TestFirebaseAuthUserHostApi { } } -class _TestMultiFactorUserHostApiCodec extends StandardMessageCodec { - const _TestMultiFactorUserHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is AuthPigeonFirebaseApp) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorInfo) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonMultiFactorSession) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return AuthPigeonFirebaseApp.decode(readValue(buffer)!); - case 129: - return PigeonMultiFactorInfo.decode(readValue(buffer)!); - case 130: - return PigeonMultiFactorSession.decode(readValue(buffer)!); - case 131: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - abstract class TestMultiFactorUserHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - _TestMultiFactorUserHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); Future enrollPhone(AuthPigeonFirebaseApp app, - PigeonPhoneMultiFactorAssertion assertion, String? displayName); + InternalPhoneMultiFactorAssertion assertion, String? displayName); Future enrollTotp( AuthPigeonFirebaseApp app, String assertionId, String? displayName); - Future getSession(AuthPigeonFirebaseApp app); + Future getSession(AuthPigeonFirebaseApp app); Future unenroll(AuthPigeonFirebaseApp app, String factorUid); - Future> getEnrolledFactors( + Future> getEnrolledFactors( AuthPigeonFirebaseApp app); static void setUp( @@ -1761,32 +1401,25 @@ abstract class TestMultiFactorUserHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollPhone$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollPhone was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollPhone was null, expected non-null AuthPigeonFirebaseApp.'); - final PigeonPhoneMultiFactorAssertion? arg_assertion = - (args[1] as PigeonPhoneMultiFactorAssertion?); - assert(arg_assertion != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollPhone was null, expected non-null PigeonPhoneMultiFactorAssertion.'); - final String? arg_displayName = (args[2] as String?); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final InternalPhoneMultiFactorAssertion arg_assertion = + args[1]! as InternalPhoneMultiFactorAssertion; + final String? arg_displayName = args[2] as String?; try { - await api.enrollPhone(arg_app!, arg_assertion!, arg_displayName); + await api.enrollPhone(arg_app, arg_assertion, arg_displayName); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1798,31 +1431,24 @@ abstract class TestMultiFactorUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollTotp$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollTotp was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollTotp was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_assertionId = (args[1] as String?); - assert(arg_assertionId != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.enrollTotp was null, expected non-null String.'); - final String? arg_displayName = (args[2] as String?); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_assertionId = args[1]! as String; + final String? arg_displayName = args[2] as String?; try { - await api.enrollTotp(arg_app!, arg_assertionId!, arg_displayName); + await api.enrollTotp(arg_app, arg_assertionId, arg_displayName); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1834,28 +1460,23 @@ abstract class TestMultiFactorUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getSession$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getSession was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getSession was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - final PigeonMultiFactorSession output = - await api.getSession(arg_app!); + final InternalMultiFactorSession output = + await api.getSession(arg_app); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1867,30 +1488,23 @@ abstract class TestMultiFactorUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.unenroll$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.unenroll was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.unenroll was null, expected non-null AuthPigeonFirebaseApp.'); - final String? arg_factorUid = (args[1] as String?); - assert(arg_factorUid != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.unenroll was null, expected non-null String.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; + final String arg_factorUid = args[1]! as String; try { - await api.unenroll(arg_app!, arg_factorUid!); + await api.unenroll(arg_app, arg_factorUid); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1902,28 +1516,23 @@ abstract class TestMultiFactorUserHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getEnrolledFactors$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getEnrolledFactors was null.'); - final List args = (message as List?)!; - final AuthPigeonFirebaseApp? arg_app = - (args[0] as AuthPigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorUserHostApi.getEnrolledFactors was null, expected non-null AuthPigeonFirebaseApp.'); + final List args = message! as List; + final AuthPigeonFirebaseApp arg_app = + args[0]! as AuthPigeonFirebaseApp; try { - final List output = - await api.getEnrolledFactors(arg_app!); + final List output = + await api.getEnrolledFactors(arg_app); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1937,62 +1546,13 @@ abstract class TestMultiFactorUserHostApi { } } -class _TestMultiFactoResolverHostApiCodec extends StandardMessageCodec { - const _TestMultiFactoResolverHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is PigeonAdditionalUserInfo) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonAuthCredential) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonPhoneMultiFactorAssertion) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserCredential) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserDetails) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is PigeonUserInfo) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return PigeonAdditionalUserInfo.decode(readValue(buffer)!); - case 129: - return PigeonAuthCredential.decode(readValue(buffer)!); - case 130: - return PigeonPhoneMultiFactorAssertion.decode(readValue(buffer)!); - case 131: - return PigeonUserCredential.decode(readValue(buffer)!); - case 132: - return PigeonUserDetails.decode(readValue(buffer)!); - case 133: - return PigeonUserInfo.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - abstract class TestMultiFactoResolverHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - _TestMultiFactoResolverHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - Future resolveSignIn(String resolverId, - PigeonPhoneMultiFactorAssertion? assertion, String? totpAssertionId); + Future resolveSignIn(String resolverId, + InternalPhoneMultiFactorAssertion? assertion, String? totpAssertionId); static void setUp( TestMultiFactoResolverHostApi? api, { @@ -2002,30 +1562,25 @@ abstract class TestMultiFactoResolverHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactoResolverHostApi.resolveSignIn$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactoResolverHostApi.resolveSignIn was null.'); - final List args = (message as List?)!; - final String? arg_resolverId = (args[0] as String?); - assert(arg_resolverId != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactoResolverHostApi.resolveSignIn was null, expected non-null String.'); - final PigeonPhoneMultiFactorAssertion? arg_assertion = - (args[1] as PigeonPhoneMultiFactorAssertion?); - final String? arg_totpAssertionId = (args[2] as String?); + final List args = message! as List; + final String arg_resolverId = args[0]! as String; + final InternalPhoneMultiFactorAssertion? arg_assertion = + args[1] as InternalPhoneMultiFactorAssertion?; + final String? arg_totpAssertionId = args[2] as String?; try { - final PigeonUserCredential output = await api.resolveSignIn( - arg_resolverId!, arg_assertion, arg_totpAssertionId); + final InternalUserCredential output = await api.resolveSignIn( + arg_resolverId, arg_assertion, arg_totpAssertionId); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -2039,36 +1594,12 @@ abstract class TestMultiFactoResolverHostApi { } } -class _TestMultiFactoResolverHostApiCodec extends StandardMessageCodec { - const _TestMultiFactoResolverHostApiCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is PigeonTotpSecret) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return PigeonTotpSecret.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - abstract class TestMultiFactoResolverHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - _TestMultiFactoResolverHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - Future generateSecret(String sessionId); + Future generateSecret(String sessionId); Future getAssertionForEnrollment( String secretKey, String oneTimePassword); @@ -2084,27 +1615,22 @@ abstract class TestMultiFactoResolverHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.generateSecret$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.generateSecret was null.'); - final List args = (message as List?)!; - final String? arg_sessionId = (args[0] as String?); - assert(arg_sessionId != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.generateSecret was null, expected non-null String.'); + final List args = message! as List; + final String arg_sessionId = args[0]! as String; try { - final PigeonTotpSecret output = - await api.generateSecret(arg_sessionId!); + final InternalTotpSecret output = + await api.generateSecret(arg_sessionId); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -2116,30 +1642,23 @@ abstract class TestMultiFactoResolverHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForEnrollment$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForEnrollment was null.'); - final List args = (message as List?)!; - final String? arg_secretKey = (args[0] as String?); - assert(arg_secretKey != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForEnrollment was null, expected non-null String.'); - final String? arg_oneTimePassword = (args[1] as String?); - assert(arg_oneTimePassword != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForEnrollment was null, expected non-null String.'); + final List args = message! as List; + final String arg_secretKey = args[0]! as String; + final String arg_oneTimePassword = args[1]! as String; try { final String output = await api.getAssertionForEnrollment( - arg_secretKey!, arg_oneTimePassword!); + arg_secretKey, arg_oneTimePassword); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -2151,30 +1670,23 @@ abstract class TestMultiFactoResolverHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForSignIn$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForSignIn was null.'); - final List args = (message as List?)!; - final String? arg_enrollmentId = (args[0] as String?); - assert(arg_enrollmentId != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForSignIn was null, expected non-null String.'); - final String? arg_oneTimePassword = (args[1] as String?); - assert(arg_oneTimePassword != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpHostApi.getAssertionForSignIn was null, expected non-null String.'); + final List args = message! as List; + final String arg_enrollmentId = args[0]! as String; + final String arg_oneTimePassword = args[1]! as String; try { final String output = await api.getAssertionForSignIn( - arg_enrollmentId!, arg_oneTimePassword!); + arg_enrollmentId, arg_oneTimePassword); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -2191,8 +1703,7 @@ abstract class TestMultiFactoResolverHostApi { abstract class TestMultiFactoResolverHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = - StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); Future generateQrCodeUrl( String secretKey, String? accountName, String? issuer); @@ -2207,29 +1718,24 @@ abstract class TestMultiFactoResolverHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.generateQrCodeUrl$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.generateQrCodeUrl was null.'); - final List args = (message as List?)!; - final String? arg_secretKey = (args[0] as String?); - assert(arg_secretKey != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.generateQrCodeUrl was null, expected non-null String.'); - final String? arg_accountName = (args[1] as String?); - final String? arg_issuer = (args[2] as String?); + final List args = message! as List; + final String arg_secretKey = args[0]! as String; + final String? arg_accountName = args[1] as String?; + final String? arg_issuer = args[2] as String?; try { final String output = await api.generateQrCodeUrl( - arg_secretKey!, arg_accountName, arg_issuer); + arg_secretKey, arg_accountName, arg_issuer); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -2241,29 +1747,22 @@ abstract class TestMultiFactoResolverHostApi { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.openInOtpApp$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, null); + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(__pigeon_channel, + .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.openInOtpApp was null.'); - final List args = (message as List?)!; - final String? arg_secretKey = (args[0] as String?); - assert(arg_secretKey != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.openInOtpApp was null, expected non-null String.'); - final String? arg_qrCodeUrl = (args[1] as String?); - assert(arg_qrCodeUrl != null, - 'Argument for dev.flutter.pigeon.firebase_auth_platform_interface.MultiFactorTotpSecretHostApi.openInOtpApp was null, expected non-null String.'); + final List args = message! as List; + final String arg_secretKey = args[0]! as String; + final String arg_qrCodeUrl = args[1]! as String; try { - await api.openInOtpApp(arg_secretKey!, arg_qrCodeUrl!); + await api.openInOtpApp(arg_secretKey, arg_qrCodeUrl); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_auth_test.dart b/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_auth_test.dart index fd6ad03188b1..94371d8568af 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_auth_test.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_auth_test.dart @@ -353,6 +353,13 @@ void main() { throwsUnimplementedError, ); }); + + test('throws if revokeAccessToken()', () async { + await expectLater( + () => firebaseAuthPlatform.revokeAccessToken('token'), + throwsUnimplementedError, + ); + }); }); } diff --git a/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_credential_test.dart b/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_credential_test.dart index 5adffbf2faf3..ea8540a8842d 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_credential_test.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_credential_test.dart @@ -19,8 +19,8 @@ void main() { const String kMockEmail = 'test@example.com'; const String kMockPassword = 'test-password'; - final kMockUserData = PigeonUserDetails( - userInfo: PigeonUserInfo( + final kMockUserData = InternalUserDetails( + userInfo: InternalUserInfo( uid: kMockUid, email: kMockEmail, isAnonymous: false, @@ -103,7 +103,7 @@ void main() { class TestUserPlatform extends UserPlatform { TestUserPlatform(FirebaseAuthPlatform auth, - MultiFactorPlatform multiFactorPlatform, PigeonUserDetails data) + MultiFactorPlatform multiFactorPlatform, InternalUserDetails data) : super(auth, multiFactorPlatform, data); } diff --git a/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_test.dart b/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_test.dart index abc49090f128..9247c1796229 100644 --- a/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_test.dart +++ b/packages/firebase_auth/firebase_auth_platform_interface/test/platform_interface_tests/platform_interface_user_test.dart @@ -40,13 +40,13 @@ void main() { }, ]; group('$UserPlatform()', () { - PigeonUserDetails kMockUser; + InternalUserDetails kMockUser; setUpAll(() async { await Firebase.initializeApp(); auth = FirebaseAuthPlatform.instance; - kMockUser = PigeonUserDetails( - userInfo: PigeonUserInfo( + kMockUser = InternalUserDetails( + userInfo: InternalUserInfo( uid: kMockUid, isAnonymous: true, email: kMockEmail, @@ -289,6 +289,6 @@ void main() { class TestUserPlatform extends UserPlatform { TestUserPlatform(FirebaseAuthPlatform auth, MultiFactorPlatform multiFactor, - PigeonUserDetails data) + InternalUserDetails data) : super(auth, multiFactor, data); } diff --git a/packages/firebase_auth/firebase_auth_web/CHANGELOG.md b/packages/firebase_auth/firebase_auth_web/CHANGELOG.md index fbfb4ffac53a..e4f18e958bc1 100644 --- a/packages/firebase_auth/firebase_auth_web/CHANGELOG.md +++ b/packages/firebase_auth/firebase_auth_web/CHANGELOG.md @@ -1,3 +1,24 @@ +## 6.2.3 + + - Update a dependency to the latest release. + +## 6.2.2 + + - Update a dependency to the latest release. + +## 6.2.1 + + - Update a dependency to the latest release. + +## 6.2.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 6.1.5 + + - Update a dependency to the latest release. + ## 6.1.4 - **FIX**(auth): fix inconsistence in casing in the native iOS SDK and Web SDK ([#18086](https://github.com/firebase/flutterfire/issues/18086)). ([60b5cd5c](https://github.com/firebase/flutterfire/commit/60b5cd5c7888fa932124958125e87bd39e1c323c)) diff --git a/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart b/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart index 62dd3bc843b2..5ee8031d52ac 100644 --- a/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart +++ b/packages/firebase_auth/firebase_auth_web/lib/firebase_auth_web.dart @@ -219,7 +219,7 @@ class FirebaseAuthWeb extends FirebaseAuthPlatform { @override FirebaseAuthWeb setInitialValues({ - PigeonUserDetails? currentUser, + InternalUserDetails? currentUser, String? languageCode, }) { // Values are already set on web diff --git a/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_version.dart b/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_version.dart index ab9c93b832a8..ec220d667792 100644 --- a/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_version.dart +++ b/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '6.3.0'; +const packageVersion = '6.5.4'; diff --git a/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_web_user.dart b/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_web_user.dart index 28f6efeb6fe4..20c345d90a32 100644 --- a/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_web_user.dart +++ b/packages/firebase_auth/firebase_auth_web/lib/src/firebase_auth_web_user.dart @@ -25,8 +25,8 @@ class UserWeb extends UserPlatform { ) : super( auth, multiFactor, - PigeonUserDetails( - userInfo: PigeonUserInfo( + InternalUserDetails( + userInfo: InternalUserInfo( displayName: _webUser.displayName, email: _webUser.email, isEmailVerified: _webUser.emailVerified, diff --git a/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart b/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart index 7e215a3299b4..1dd2681fc52e 100644 --- a/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart +++ b/packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart @@ -221,7 +221,7 @@ IdTokenResult convertWebIdTokenResult( auth_interop.IdTokenResult webIdTokenResult, ) { return IdTokenResult( - PigeonIdTokenResult( + InternalIdTokenResult( claims: webIdTokenResult.claims, token: webIdTokenResult.token, authTimestamp: webIdTokenResult.authTime.millisecondsSinceEpoch, diff --git a/packages/firebase_auth/firebase_auth_web/pubspec.yaml b/packages/firebase_auth/firebase_auth_web/pubspec.yaml index 3ee1a64e0ad1..a004fee15b81 100644 --- a/packages/firebase_auth/firebase_auth_web/pubspec.yaml +++ b/packages/firebase_auth/firebase_auth_web/pubspec.yaml @@ -2,16 +2,17 @@ name: firebase_auth_web description: The web implementation of firebase_auth homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_auth/firebase_auth_web repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_auth/firebase_auth_web -version: 6.1.4 +version: 6.2.3 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - firebase_auth_platform_interface: ^8.1.8 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 + firebase_auth_platform_interface: ^9.0.3 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/firebase_core/analysis_options.yaml b/packages/firebase_core/analysis_options.yaml index 9afc3598d90a..9d4eb42ab944 100644 --- a/packages/firebase_core/analysis_options.yaml +++ b/packages/firebase_core/analysis_options.yaml @@ -9,6 +9,7 @@ analyzer: exclude: - firebase_core_platform_interface/lib/src/pigeon/messages.pigeon.dart - firebase_core_platform_interface/lib/src/pigeon/test_api.dart + - firebase_core_platform_interface/pigeons/messages.dart linter: rules: diff --git a/packages/firebase_core/firebase_core/CHANGELOG.md b/packages/firebase_core/firebase_core/CHANGELOG.md index 724d4582bc4d..a6a7c965ecbd 100644 --- a/packages/firebase_core/firebase_core/CHANGELOG.md +++ b/packages/firebase_core/firebase_core/CHANGELOG.md @@ -1,3 +1,37 @@ +## 4.11.0 + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + - **FEAT**(core): bump Firebase ios SDK to 12.15.0 ([#18375](https://github.com/firebase/flutterfire/issues/18375)). ([4d083764](https://github.com/firebase/flutterfire/commit/4d083764c3abd94d6e4590a170fbdaaa4b161202)) + - **FEAT**(core): bump Firebase android SDK to 34.15.0 ([#18374](https://github.com/firebase/flutterfire/issues/18374)). ([1cd3a0bd](https://github.com/firebase/flutterfire/commit/1cd3a0bd76fd594139356519fabee0e0d2b12f31)) + - **FEAT**(core): Add Recaptcha Site Key to FirebaseOptions ([#18334](https://github.com/firebase/flutterfire/issues/18334)). ([57be7027](https://github.com/firebase/flutterfire/commit/57be702778d34b9b7e86b40817d93acaec4c3ca4)) + +## 4.10.0 + + - **FEAT**(core): bump Firebase ios SDK to 12.14.0 ([#18330](https://github.com/firebase/flutterfire/issues/18330)). ([b1cfe745](https://github.com/firebase/flutterfire/commit/b1cfe745d221f09665943762c83cdd64684c6e6c)) + - **FEAT**(core): bump Firebase android SDK to 34.14.0 ([#18329](https://github.com/firebase/flutterfire/issues/18329)). ([1562eace](https://github.com/firebase/flutterfire/commit/1562eace5196227ad0058df9b5426950b0094f83)) + +## 4.9.0 + + - **FIX**(core,iOS): use namespaced iOS Pigeon header import ([#18281](https://github.com/firebase/flutterfire/issues/18281)). ([7c1257e7](https://github.com/firebase/flutterfire/commit/7c1257e7295f9ba67f3f5820493f105a14d34d52)) + - **FEAT**: bump Firebase iOS SDK to 12.13.0 ([#18273](https://github.com/firebase/flutterfire/issues/18273)). ([78e10f02](https://github.com/firebase/flutterfire/commit/78e10f0222f4e23c96b636c63c29935ba5aa82e6)) + - **FEAT**: bump Firebase android SDK to 34.13.0 ([#18272](https://github.com/firebase/flutterfire/issues/18272)). ([d10e0ffa](https://github.com/firebase/flutterfire/commit/d10e0ffa2980a21a5899dbe67952fc772a3c6c01)) + +## 4.8.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FEAT**(core): Add Auth and AppCheck as App's registered service. ([#18237](https://github.com/firebase/flutterfire/issues/18237)). ([7ce191cb](https://github.com/firebase/flutterfire/commit/7ce191cbd598b299cd0ec64b45d1366914367a5d)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 4.7.0 + + - **FEAT**(core): bump Firebase Android SDK to 34.12.0 ([#18185](https://github.com/firebase/flutterfire/issues/18185)). ([346a048f](https://github.com/firebase/flutterfire/commit/346a048f098090e6848fdd0f61a8bf7d01394676)) + - **FEAT**: bump Firebase iOS SDK to 12.12.0 ([#18187](https://github.com/firebase/flutterfire/issues/18187)). ([cc063bd9](https://github.com/firebase/flutterfire/commit/cc063bd9df1c59dd3bb8c25d067f8655bc268523)) + - **FEAT**: bump iOS SDK to version 12.11.0 ([#18161](https://github.com/firebase/flutterfire/issues/18161)). ([2664b2c2](https://github.com/firebase/flutterfire/commit/2664b2c2dab4d0147461ce4d3f7862267e880542)) + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + - **FEAT**: bump Firebase android SDK to 34.11.0 ([#18146](https://github.com/firebase/flutterfire/issues/18146)). ([2b50061a](https://github.com/firebase/flutterfire/commit/2b50061a689634957efba8bd17c196dd548a08a2)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 4.6.0 - **FIX**(remote_config,windows): release mode wasn't linking properly for windows ([#18073](https://github.com/firebase/flutterfire/issues/18073)). ([ea1f309a](https://github.com/firebase/flutterfire/commit/ea1f309a33075fc06c082819f0653976c6d5214b)) diff --git a/packages/firebase_core/firebase_core/android/gradle.properties b/packages/firebase_core/firebase_core/android/gradle.properties index 12e57d452ef2..afd66c21b0e2 100644 --- a/packages/firebase_core/firebase_core/android/gradle.properties +++ b/packages/firebase_core/firebase_core/android/gradle.properties @@ -1,2 +1,2 @@ # https://firebase.google.com/support/release-notes/android -FirebaseSDKVersion=34.11.0 +FirebaseSDKVersion=34.15.0 diff --git a/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/FlutterFirebaseCorePlugin.java b/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/FlutterFirebaseCorePlugin.java index 53b7e6356457..9763cfd0ab57 100644 --- a/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/FlutterFirebaseCorePlugin.java +++ b/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/FlutterFirebaseCorePlugin.java @@ -63,6 +63,9 @@ private GeneratedAndroidFirebaseCore.CoreFirebaseOptions firebaseOptionsToMap( firebaseOptions.setDatabaseURL(options.getDatabaseUrl()); firebaseOptions.setStorageBucket(options.getStorageBucket()); firebaseOptions.setTrackingId(options.getGaTrackingId()); + if (options.getRecaptchaSiteKey() != null) { + firebaseOptions.setRecaptchaSiteKey(options.getRecaptchaSiteKey()); + } return firebaseOptions.build(); } @@ -149,10 +152,13 @@ public void initializeApp( .setProjectId(initializeAppRequest.getProjectId()) .setStorageBucket(initializeAppRequest.getStorageBucket()) .setGaTrackingId(initializeAppRequest.getTrackingId()) + .setRecaptchaSiteKey(initializeAppRequest.getRecaptchaSiteKey()) .build(); // TODO(Salakar) hacky workaround a bug with FirebaseInAppMessaging causing the error: - // Can't create handler inside thread Thread[pool-3-thread-1,5,main] that has not called Looper.prepare() - // at com.google.firebase.inappmessaging.internal.ForegroundNotifier.(ForegroundNotifier.java:61) + // Can't create handler inside thread Thread[pool-3-thread-1,5,main] that has not + // called Looper.prepare() + // at + // com.google.firebase.inappmessaging.internal.ForegroundNotifier.(ForegroundNotifier.java:61) try { Looper.prepare(); } catch (Exception e) { @@ -221,7 +227,8 @@ public void optionsFromResource( if (options == null) { taskCompletionSource.setException( new Exception( - "Failed to load FirebaseOptions from resource. Check that you have defined values.xml correctly.")); + "Failed to load FirebaseOptions from resource. Check that you have defined" + + " values.xml correctly.")); return; } taskCompletionSource.setResult(firebaseOptionsToMap(options)); @@ -285,12 +292,10 @@ public void delete(@NonNull String appName, GeneratedAndroidFirebaseCore.VoidRes () -> { try { FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); - try { - firebaseApp.delete(); - } catch (IllegalStateException appNotFoundException) { - // Ignore app not found exceptions. - } - + firebaseApp.delete(); + taskCompletionSource.setResult(null); + } catch (IllegalStateException appNotFoundException) { + // Ignore app not found exceptions. taskCompletionSource.setResult(null); } catch (Exception e) { taskCompletionSource.setException(e); diff --git a/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/GeneratedAndroidFirebaseCore.java b/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/GeneratedAndroidFirebaseCore.java index af1dec848522..fdae685adbd8 100644 --- a/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/GeneratedAndroidFirebaseCore.java +++ b/packages/firebase_core/firebase_core/android/src/main/java/io/flutter/plugins/firebase/core/GeneratedAndroidFirebaseCore.java @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.firebase.core; @@ -21,13 +21,170 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Objects; /** Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class GeneratedAndroidFirebaseCore { + static boolean pigeonDoubleEquals(double a, double b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b)); + } + + static boolean pigeonFloatEquals(float a, float b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b)); + } + + static int pigeonDoubleHashCode(double d) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (d == 0.0) { + d = 0.0; + } + long bits = Double.doubleToLongBits(d); + return (int) (bits ^ (bits >>> 32)); + } + + static int pigeonFloatHashCode(float f) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (f == 0.0f) { + f = 0.0f; + } + return Float.floatToIntBits(f); + } + + static boolean pigeonDeepEquals(Object a, Object b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + if (a instanceof byte[] && b instanceof byte[]) { + return Arrays.equals((byte[]) a, (byte[]) b); + } + if (a instanceof int[] && b instanceof int[]) { + return Arrays.equals((int[]) a, (int[]) b); + } + if (a instanceof long[] && b instanceof long[]) { + return Arrays.equals((long[]) a, (long[]) b); + } + if (a instanceof double[] && b instanceof double[]) { + double[] da = (double[]) a; + double[] db = (double[]) b; + if (da.length != db.length) { + return false; + } + for (int i = 0; i < da.length; i++) { + if (!pigeonDoubleEquals(da[i], db[i])) { + return false; + } + } + return true; + } + if (a instanceof List && b instanceof List) { + List listA = (List) a; + List listB = (List) b; + if (listA.size() != listB.size()) { + return false; + } + for (int i = 0; i < listA.size(); i++) { + if (!pigeonDeepEquals(listA.get(i), listB.get(i))) { + return false; + } + } + return true; + } + if (a instanceof Map && b instanceof Map) { + Map mapA = (Map) a; + Map mapB = (Map) b; + if (mapA.size() != mapB.size()) { + return false; + } + for (Map.Entry entryA : mapA.entrySet()) { + Object keyA = entryA.getKey(); + Object valueA = entryA.getValue(); + boolean found = false; + for (Map.Entry entryB : mapB.entrySet()) { + Object keyB = entryB.getKey(); + if (pigeonDeepEquals(keyA, keyB)) { + Object valueB = entryB.getValue(); + if (pigeonDeepEquals(valueA, valueB)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + if (a instanceof Double && b instanceof Double) { + return pigeonDoubleEquals((double) a, (double) b); + } + if (a instanceof Float && b instanceof Float) { + return pigeonFloatEquals((float) a, (float) b); + } + return a.equals(b); + } + + static int pigeonDeepHashCode(Object value) { + if (value == null) { + return 0; + } + if (value instanceof byte[]) { + return Arrays.hashCode((byte[]) value); + } + if (value instanceof int[]) { + return Arrays.hashCode((int[]) value); + } + if (value instanceof long[]) { + return Arrays.hashCode((long[]) value); + } + if (value instanceof double[]) { + double[] da = (double[]) value; + int result = 1; + for (double d : da) { + result = 31 * result + pigeonDoubleHashCode(d); + } + return result; + } + if (value instanceof List) { + int result = 1; + for (Object item : (List) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Map) { + int result = 0; + for (Map.Entry entry : ((Map) value).entrySet()) { + result += + ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue())); + } + return result; + } + if (value instanceof Object[]) { + int result = 1; + for (Object item : (Object[]) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Double) { + return pigeonDoubleHashCode((double) value); + } + if (value instanceof Float) { + return pigeonFloatHashCode((float) value); + } + return value.hashCode(); + } /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ public static class FlutterError extends RuntimeException { @@ -220,6 +377,16 @@ public void setAppGroupId(@Nullable String setterArg) { this.appGroupId = setterArg; } + private @Nullable String recaptchaSiteKey; + + public @Nullable String getRecaptchaSiteKey() { + return recaptchaSiteKey; + } + + public void setRecaptchaSiteKey(@Nullable String setterArg) { + this.recaptchaSiteKey = setterArg; + } + /** Constructor is non-public to enforce null safety; use Builder. */ CoreFirebaseOptions() {} @@ -232,39 +399,45 @@ public boolean equals(Object o) { return false; } CoreFirebaseOptions that = (CoreFirebaseOptions) o; - return apiKey.equals(that.apiKey) - && appId.equals(that.appId) - && messagingSenderId.equals(that.messagingSenderId) - && projectId.equals(that.projectId) - && Objects.equals(authDomain, that.authDomain) - && Objects.equals(databaseURL, that.databaseURL) - && Objects.equals(storageBucket, that.storageBucket) - && Objects.equals(measurementId, that.measurementId) - && Objects.equals(trackingId, that.trackingId) - && Objects.equals(deepLinkURLScheme, that.deepLinkURLScheme) - && Objects.equals(androidClientId, that.androidClientId) - && Objects.equals(iosClientId, that.iosClientId) - && Objects.equals(iosBundleId, that.iosBundleId) - && Objects.equals(appGroupId, that.appGroupId); + return pigeonDeepEquals(apiKey, that.apiKey) + && pigeonDeepEquals(appId, that.appId) + && pigeonDeepEquals(messagingSenderId, that.messagingSenderId) + && pigeonDeepEquals(projectId, that.projectId) + && pigeonDeepEquals(authDomain, that.authDomain) + && pigeonDeepEquals(databaseURL, that.databaseURL) + && pigeonDeepEquals(storageBucket, that.storageBucket) + && pigeonDeepEquals(measurementId, that.measurementId) + && pigeonDeepEquals(trackingId, that.trackingId) + && pigeonDeepEquals(deepLinkURLScheme, that.deepLinkURLScheme) + && pigeonDeepEquals(androidClientId, that.androidClientId) + && pigeonDeepEquals(iosClientId, that.iosClientId) + && pigeonDeepEquals(iosBundleId, that.iosBundleId) + && pigeonDeepEquals(appGroupId, that.appGroupId) + && pigeonDeepEquals(recaptchaSiteKey, that.recaptchaSiteKey); } @Override public int hashCode() { - return Objects.hash( - apiKey, - appId, - messagingSenderId, - projectId, - authDomain, - databaseURL, - storageBucket, - measurementId, - trackingId, - deepLinkURLScheme, - androidClientId, - iosClientId, - iosBundleId, - appGroupId); + Object[] fields = + new Object[] { + getClass(), + apiKey, + appId, + messagingSenderId, + projectId, + authDomain, + databaseURL, + storageBucket, + measurementId, + trackingId, + deepLinkURLScheme, + androidClientId, + iosClientId, + iosBundleId, + appGroupId, + recaptchaSiteKey + }; + return pigeonDeepHashCode(fields); } public static final class Builder { @@ -381,6 +554,14 @@ public static final class Builder { return this; } + private @Nullable String recaptchaSiteKey; + + @CanIgnoreReturnValue + public @NonNull Builder setRecaptchaSiteKey(@Nullable String setterArg) { + this.recaptchaSiteKey = setterArg; + return this; + } + public @NonNull CoreFirebaseOptions build() { CoreFirebaseOptions pigeonReturn = new CoreFirebaseOptions(); pigeonReturn.setApiKey(apiKey); @@ -397,13 +578,14 @@ public static final class Builder { pigeonReturn.setIosClientId(iosClientId); pigeonReturn.setIosBundleId(iosBundleId); pigeonReturn.setAppGroupId(appGroupId); + pigeonReturn.setRecaptchaSiteKey(recaptchaSiteKey); return pigeonReturn; } } @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList<>(14); + public ArrayList toList() { + ArrayList toListResult = new ArrayList<>(15); toListResult.add(apiKey); toListResult.add(appId); toListResult.add(messagingSenderId); @@ -418,6 +600,7 @@ ArrayList toList() { toListResult.add(iosClientId); toListResult.add(iosBundleId); toListResult.add(appGroupId); + toListResult.add(recaptchaSiteKey); return toListResult; } @@ -451,6 +634,8 @@ ArrayList toList() { pigeonResult.setIosBundleId((String) iosBundleId); Object appGroupId = pigeonVar_list.get(13); pigeonResult.setAppGroupId((String) appGroupId); + Object recaptchaSiteKey = pigeonVar_list.get(14); + pigeonResult.setRecaptchaSiteKey((String) recaptchaSiteKey); return pigeonResult; } } @@ -518,15 +703,20 @@ public boolean equals(Object o) { return false; } CoreInitializeResponse that = (CoreInitializeResponse) o; - return name.equals(that.name) - && options.equals(that.options) - && Objects.equals(isAutomaticDataCollectionEnabled, that.isAutomaticDataCollectionEnabled) - && pluginConstants.equals(that.pluginConstants); + return pigeonDeepEquals(name, that.name) + && pigeonDeepEquals(options, that.options) + && pigeonDeepEquals( + isAutomaticDataCollectionEnabled, that.isAutomaticDataCollectionEnabled) + && pigeonDeepEquals(pluginConstants, that.pluginConstants); } @Override public int hashCode() { - return Objects.hash(name, options, isAutomaticDataCollectionEnabled, pluginConstants); + Object[] fields = + new Object[] { + getClass(), name, options, isAutomaticDataCollectionEnabled, pluginConstants + }; + return pigeonDeepHashCode(fields); } public static final class Builder { @@ -574,7 +764,7 @@ public static final class Builder { } @NonNull - ArrayList toList() { + public ArrayList toList() { ArrayList toListResult = new ArrayList<>(4); toListResult.add(name); toListResult.add(options); @@ -636,6 +826,7 @@ public interface Result { /** Failure case callback method for handling errors. */ void error(@NonNull Throwable error); } + /** Asynchronous error handling return type for nullable API method returns. */ public interface NullableResult { /** Success case callback method for handling returns. */ @@ -644,6 +835,7 @@ public interface NullableResult { /** Failure case callback method for handling errors. */ void error(@NonNull Throwable error); } + /** Asynchronous error handling return type for void API method returns. */ public interface VoidResult { /** Success case callback method for handling returns. */ @@ -652,6 +844,7 @@ public interface VoidResult { /** Failure case callback method for handling errors. */ void error(@NonNull Throwable error); } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FirebaseCoreHostApi { @@ -668,6 +861,7 @@ void initializeApp( static @NonNull MessageCodec getCodec() { return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `FirebaseCoreHostApi` to handle messages through the * `binaryMessenger`. @@ -776,6 +970,7 @@ public void error(Throwable error) { } } } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FirebaseAppHostApi { @@ -791,6 +986,7 @@ void setAutomaticResourceManagementEnabled( static @NonNull MessageCodec getCodec() { return PigeonCodec.INSTANCE; } + /** * Sets up an instance of `FirebaseAppHostApi` to handle messages through the `binaryMessenger`. */ diff --git a/packages/firebase_core/firebase_core/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecoreexample/MainActivity.kt b/packages/firebase_core/firebase_core/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecoreexample/MainActivity.kt index 8757d9f10dbd..2416ea61ea9d 100644 --- a/packages/firebase_core/firebase_core/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecoreexample/MainActivity.kt +++ b/packages/firebase_core/firebase_core/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecoreexample/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebasecoreexample import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_core/firebase_core/example/pubspec.yaml b/packages/firebase_core/firebase_core/example/pubspec.yaml index 03cbbe0dac94..e90d66a06680 100644 --- a/packages/firebase_core/firebase_core/example/pubspec.yaml +++ b/packages/firebase_core/firebase_core/example/pubspec.yaml @@ -1,11 +1,13 @@ name: firebase_core_example description: Demonstrates how to use the firebase_core plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter diff --git a/packages/firebase_core/firebase_core/ios/firebase_core/Package.swift b/packages/firebase_core/firebase_core/ios/firebase_core/Package.swift index 4dbfd4acebd5..2aa49bcdf308 100644 --- a/packages/firebase_core/firebase_core/ios/firebase_core/Package.swift +++ b/packages/firebase_core/firebase_core/ios/firebase_core/Package.swift @@ -7,35 +7,35 @@ import PackageDescription -let library_version_string = "4.5.0" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersionString = "4.11.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_core", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-core", targets: ["firebase_core"]), + .library(name: "firebase-core", targets: ["firebase_core"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion) ], targets: [ .target( name: "firebase_core", dependencies: [ // No product for firebase-core so we pull in the smallest one - .product(name: "FirebaseInstallations", package: "firebase-ios-sdk"), + .product(name: "FirebaseInstallations", package: "firebase-ios-sdk") ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include/firebase_core"), - .define("LIBRARY_VERSION", to: "\"\(library_version_string)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersionString)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-core\""), ] - ), + ) ] ) diff --git a/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/FLTFirebaseCorePlugin.m b/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/FLTFirebaseCorePlugin.m index 23b3f7b9433b..192f984e59fb 100644 --- a/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/FLTFirebaseCorePlugin.m +++ b/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/FLTFirebaseCorePlugin.m @@ -93,6 +93,8 @@ - (CoreFirebaseOptions *)optionsFromFIROptions:(FIROptions *)options { pigeonOptions.iosBundleId = (id)options.bundleID ?: [NSNull null]; pigeonOptions.iosClientId = (id)options.clientID ?: [NSNull null]; pigeonOptions.appGroupId = (id)options.appGroupID ?: [NSNull null]; + // recaptchaSiteKey is currently only exposed by Firebase JS options. + pigeonOptions.recaptchaSiteKey = [NSNull null]; return pigeonOptions; } diff --git a/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/include/firebase_core/messages.g.h b/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/include/firebase_core/messages.g.h index cc06241e7cb1..561383cc4135 100644 --- a/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/include/firebase_core/messages.g.h +++ b/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/include/firebase_core/messages.g.h @@ -1,10 +1,10 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import +@import Foundation; @protocol FlutterBinaryMessenger; @protocol FlutterMessageCodec; @@ -32,7 +32,8 @@ NS_ASSUME_NONNULL_BEGIN androidClientId:(nullable NSString *)androidClientId iosClientId:(nullable NSString *)iosClientId iosBundleId:(nullable NSString *)iosBundleId - appGroupId:(nullable NSString *)appGroupId; + appGroupId:(nullable NSString *)appGroupId + recaptchaSiteKey:(nullable NSString *)recaptchaSiteKey; @property(nonatomic, copy) NSString *apiKey; @property(nonatomic, copy) NSString *appId; @property(nonatomic, copy) NSString *messagingSenderId; @@ -47,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, copy, nullable) NSString *iosClientId; @property(nonatomic, copy, nullable) NSString *iosBundleId; @property(nonatomic, copy, nullable) NSString *appGroupId; +@property(nonatomic, copy, nullable) NSString *recaptchaSiteKey; @end @interface CoreInitializeResponse : NSObject diff --git a/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/messages.g.m b/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/messages.g.m index cf3e439b92ac..449944316866 100644 --- a/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/messages.g.m +++ b/packages/firebase_core/firebase_core/ios/firebase_core/Sources/firebase_core/messages.g.m @@ -1,24 +1,107 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -#if __has_include("include/firebase_core/messages.g.h") #import "include/firebase_core/messages.g.h" -#else -#import "include/messages.g.h" -#endif #if TARGET_OS_OSX -#import +@import FlutterMacOS; #else -#import +@import Flutter; #endif -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. -#endif +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return + [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} static NSArray *wrapResult(id result, FlutterError *error) { if (error) { @@ -60,7 +143,8 @@ + (instancetype)makeWithApiKey:(NSString *)apiKey androidClientId:(nullable NSString *)androidClientId iosClientId:(nullable NSString *)iosClientId iosBundleId:(nullable NSString *)iosBundleId - appGroupId:(nullable NSString *)appGroupId { + appGroupId:(nullable NSString *)appGroupId + recaptchaSiteKey:(nullable NSString *)recaptchaSiteKey { CoreFirebaseOptions *pigeonResult = [[CoreFirebaseOptions alloc] init]; pigeonResult.apiKey = apiKey; pigeonResult.appId = appId; @@ -76,6 +160,7 @@ + (instancetype)makeWithApiKey:(NSString *)apiKey pigeonResult.iosClientId = iosClientId; pigeonResult.iosBundleId = iosBundleId; pigeonResult.appGroupId = appGroupId; + pigeonResult.recaptchaSiteKey = recaptchaSiteKey; return pigeonResult; } + (CoreFirebaseOptions *)fromList:(NSArray *)list { @@ -94,6 +179,7 @@ + (CoreFirebaseOptions *)fromList:(NSArray *)list { pigeonResult.iosClientId = GetNullableObjectAtIndex(list, 11); pigeonResult.iosBundleId = GetNullableObjectAtIndex(list, 12); pigeonResult.appGroupId = GetNullableObjectAtIndex(list, 13); + pigeonResult.recaptchaSiteKey = GetNullableObjectAtIndex(list, 14); return pigeonResult; } + (nullable CoreFirebaseOptions *)nullableFromList:(NSArray *)list { @@ -115,8 +201,53 @@ + (nullable CoreFirebaseOptions *)nullableFromList:(NSArray *)list { self.iosClientId ?: [NSNull null], self.iosBundleId ?: [NSNull null], self.appGroupId ?: [NSNull null], + self.recaptchaSiteKey ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + CoreFirebaseOptions *other = (CoreFirebaseOptions *)object; + return FLTPigeonDeepEquals(self.apiKey, other.apiKey) && + FLTPigeonDeepEquals(self.appId, other.appId) && + FLTPigeonDeepEquals(self.messagingSenderId, other.messagingSenderId) && + FLTPigeonDeepEquals(self.projectId, other.projectId) && + FLTPigeonDeepEquals(self.authDomain, other.authDomain) && + FLTPigeonDeepEquals(self.databaseURL, other.databaseURL) && + FLTPigeonDeepEquals(self.storageBucket, other.storageBucket) && + FLTPigeonDeepEquals(self.measurementId, other.measurementId) && + FLTPigeonDeepEquals(self.trackingId, other.trackingId) && + FLTPigeonDeepEquals(self.deepLinkURLScheme, other.deepLinkURLScheme) && + FLTPigeonDeepEquals(self.androidClientId, other.androidClientId) && + FLTPigeonDeepEquals(self.iosClientId, other.iosClientId) && + FLTPigeonDeepEquals(self.iosBundleId, other.iosBundleId) && + FLTPigeonDeepEquals(self.appGroupId, other.appGroupId) && + FLTPigeonDeepEquals(self.recaptchaSiteKey, other.recaptchaSiteKey); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.apiKey); + result = result * 31 + FLTPigeonDeepHash(self.appId); + result = result * 31 + FLTPigeonDeepHash(self.messagingSenderId); + result = result * 31 + FLTPigeonDeepHash(self.projectId); + result = result * 31 + FLTPigeonDeepHash(self.authDomain); + result = result * 31 + FLTPigeonDeepHash(self.databaseURL); + result = result * 31 + FLTPigeonDeepHash(self.storageBucket); + result = result * 31 + FLTPigeonDeepHash(self.measurementId); + result = result * 31 + FLTPigeonDeepHash(self.trackingId); + result = result * 31 + FLTPigeonDeepHash(self.deepLinkURLScheme); + result = result * 31 + FLTPigeonDeepHash(self.androidClientId); + result = result * 31 + FLTPigeonDeepHash(self.iosClientId); + result = result * 31 + FLTPigeonDeepHash(self.iosBundleId); + result = result * 31 + FLTPigeonDeepHash(self.appGroupId); + result = result * 31 + FLTPigeonDeepHash(self.recaptchaSiteKey); + return result; +} @end @implementation CoreInitializeResponse @@ -150,6 +281,29 @@ + (nullable CoreInitializeResponse *)nullableFromList:(NSArray *)list { self.pluginConstants ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + CoreInitializeResponse *other = (CoreInitializeResponse *)object; + return FLTPigeonDeepEquals(self.name, other.name) && + FLTPigeonDeepEquals(self.options, other.options) && + FLTPigeonDeepEquals(self.isAutomaticDataCollectionEnabled, + other.isAutomaticDataCollectionEnabled) && + FLTPigeonDeepEquals(self.pluginConstants, other.pluginConstants); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.name); + result = result * 31 + FLTPigeonDeepHash(self.options); + result = result * 31 + FLTPigeonDeepHash(self.isAutomaticDataCollectionEnabled); + result = result * 31 + FLTPigeonDeepHash(self.pluginConstants); + return result; +} @end @interface nullMessagesPigeonCodecReader : FlutterStandardReader diff --git a/packages/firebase_core/firebase_core/ios/firebase_sdk_version.rb b/packages/firebase_core/firebase_core/ios/firebase_sdk_version.rb index accce4bb66a3..b4c1fd28f447 100644 --- a/packages/firebase_core/firebase_core/ios/firebase_sdk_version.rb +++ b/packages/firebase_core/firebase_core/ios/firebase_sdk_version.rb @@ -1,4 +1,4 @@ # https://firebase.google.com/support/release-notes/ios def firebase_sdk_version!() - '12.9.0' + '12.15.0' end diff --git a/packages/firebase_core/firebase_core/lib/src/firebase_app.dart b/packages/firebase_core/firebase_core/lib/src/firebase_app.dart index 841255c8dd7f..2dd14a80e0cc 100644 --- a/packages/firebase_core/firebase_core/lib/src/firebase_app.dart +++ b/packages/firebase_core/firebase_core/lib/src/firebase_app.dart @@ -27,7 +27,17 @@ class FirebaseApp { /// /// Deleting the default app is not possible and throws an exception. Future delete() async { + final registry = _registries[name]; + if (registry != null) { + await Future.wait( + registry.values.map((service) { + return service.dispose().catchError((_) {}); + }), + ); + } + await _delegate.delete(); + _registries.remove(name); } /// The name of this [FirebaseApp]. @@ -71,4 +81,38 @@ class FirebaseApp { @override String toString() => '$FirebaseApp($name)'; + + static final Map> _registries = + {}; + + /// Registers a service instance for this app. + void registerService( + T service, { + Future Function(T service)? dispose, + }) { + final registry = _registries.putIfAbsent(name, () => {}); + registry[T] = _RegisteredFirebaseService( + service, + dispose == null ? null : () => dispose(service), + ); + } + + /// Returns a registered service instance for this app. + T? getService() { + return _registries[name]?[T]?.service as T?; + } +} + +/// A marker interface for Firebase services that can be registered in [FirebaseApp]. +abstract class FirebaseService {} + +class _RegisteredFirebaseService { + _RegisteredFirebaseService(this.service, this._dispose); + + final FirebaseService service; + final Future Function()? _dispose; + + Future dispose() async { + await _dispose?.call(); + } } diff --git a/packages/firebase_core/firebase_core/macos/firebase_core/Package.swift b/packages/firebase_core/firebase_core/macos/firebase_core/Package.swift index 65aaba7915d1..ebf94947c34b 100644 --- a/packages/firebase_core/firebase_core/macos/firebase_core/Package.swift +++ b/packages/firebase_core/firebase_core/macos/firebase_core/Package.swift @@ -7,35 +7,35 @@ import PackageDescription -let library_version_string = "4.5.0" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersionString = "4.11.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_core", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-core", targets: ["firebase_core"]), + .library(name: "firebase-core", targets: ["firebase_core"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion) ], targets: [ .target( name: "firebase_core", dependencies: [ // No product for firebase-core so we pull in the smallest one - .product(name: "FirebaseInstallations", package: "firebase-ios-sdk"), + .product(name: "FirebaseInstallations", package: "firebase-ios-sdk") ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include/firebase_core"), - .define("LIBRARY_VERSION", to: "\"\(library_version_string)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersionString)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-core\""), ] - ), + ) ] ) diff --git a/packages/firebase_core/firebase_core/pubspec.yaml b/packages/firebase_core/firebase_core/pubspec.yaml index 721448faa3f0..7cd0fe7e59cb 100644 --- a/packages/firebase_core/firebase_core/pubspec.yaml +++ b/packages/firebase_core/firebase_core/pubspec.yaml @@ -3,7 +3,8 @@ description: Flutter plugin for Firebase Core, enabling connecting to multiple Firebase apps. homepage: https://firebase.google.com/docs/flutter/setup repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_core/firebase_core -version: 4.6.0 +version: 4.11.0 +resolution: workspace topics: - firebase - core @@ -12,12 +13,12 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core_platform_interface: ^6.0.3 - firebase_core_web: ^3.5.1 + firebase_core_platform_interface: ^7.1.0 + firebase_core_web: ^3.9.0 flutter: sdk: flutter meta: ^1.8.0 @@ -44,6 +45,6 @@ flutter: pluginClass: FirebaseCorePluginCApi firebase: - google_services_gradle_plugin_version: '4.3.15' - crashlytics_gradle_plugin_version: '2.8.1' - performance_gradle_plugin_version: '1.4.1' + google_services_gradle_plugin_version: '4.4.4' + crashlytics_gradle_plugin_version: '3.0.7' + performance_gradle_plugin_version: '2.0.2' diff --git a/packages/firebase_core/firebase_core/test/firebase_core_test.dart b/packages/firebase_core/firebase_core/test/firebase_core_test.dart index 87e4abe4718b..cb2bf2f67e12 100755 --- a/packages/firebase_core/firebase_core/test/firebase_core_test.dart +++ b/packages/firebase_core/firebase_core/test/firebase_core_test.dart @@ -5,6 +5,9 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; +import 'package:firebase_core_platform_interface/src/pigeon/messages.pigeon.dart' + as pigeon; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -64,6 +67,57 @@ void main() { mock.app(testAppName), ]); }); + + test('.registerService() and .getService()', () { + FirebaseApp app = Firebase.app(testAppName); + + final testService = TestService(); + app.registerService(testService); + + expect(app.getService(), testService); + }); + + test('.getService() returns null when registry is null', () { + String nullAppName = 'nullApp'; + final FirebaseAppPlatform nullPlatformApp = + FirebaseAppPlatform(nullAppName, testOptions); + when(mock.app(nullAppName)).thenReturn(nullPlatformApp); + + FirebaseApp app = Firebase.app(nullAppName); + expect(app.getService(), isNull); + }); + + test('.getService() returns null when service is not registered', () { + FirebaseApp app = Firebase.app(testAppName); + expect(app.getService(), isNull); + }); + + test('.delete() disposes registered services before deleting app', + () async { + final calls = []; + final platformApp = TestFirebaseAppPlatform( + testAppName, + testOptions, + onDelete: () async { + calls.add('app'); + }, + ); + when(mock.app(testAppName)).thenReturn(platformApp); + + FirebaseApp app = Firebase.app(testAppName); + final testService = TestService(); + app.registerService( + testService, + dispose: (_) async { + calls.add('service'); + }, + ); + + await app.delete(); + + expect(calls, ['service', 'app']); + expect(app.getService(), isNull); + }); }); test('.initializeApp() with demoProjectId', () async { @@ -104,6 +158,73 @@ void main() { mock.app(expectedName), ]); }); + + test('.initializeApp() preserves recaptchaSiteKey if native drops it', + () async { + Firebase.delegatePackingProperty = null; + MethodChannelFirebase.appInstances.clear(); + MethodChannelFirebase.isCoreInitialized = false; + + const String appName = 'recaptcha-test-app'; + const FirebaseOptions options = FirebaseOptions( + apiKey: 'apiKey', + appId: 'appId', + messagingSenderId: 'messagingSenderId', + projectId: 'projectId', + recaptchaSiteKey: 'test-recaptcha-site-key', + ); + + final TestDefaultBinaryMessenger messenger = + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + + messenger.setMockMessageHandler( + 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeCore', + (ByteData? message) async { + return pigeon.FirebaseCoreHostApi.pigeonChannelCodec.encodeMessage( + [[]], + ); + }, + ); + messenger.setMockMessageHandler( + 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeApp', + (ByteData? message) async { + return pigeon.FirebaseCoreHostApi.pigeonChannelCodec.encodeMessage( + [ + pigeon.CoreInitializeResponse( + name: appName, + options: pigeon.CoreFirebaseOptions( + apiKey: options.apiKey, + appId: options.appId, + messagingSenderId: options.messagingSenderId, + projectId: options.projectId, + ), + pluginConstants: const {}, + ), + ], + ); + }, + ); + addTearDown(() { + messenger.setMockMessageHandler( + 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeCore', + null, + ); + messenger.setMockMessageHandler( + 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeApp', + null, + ); + MethodChannelFirebase.appInstances.clear(); + MethodChannelFirebase.isCoreInitialized = false; + Firebase.delegatePackingProperty = null; + }); + + final FirebaseApp app = await Firebase.initializeApp( + name: appName, + options: options, + ); + + expect(app.options.recaptchaSiteKey, 'test-recaptcha-site-key'); + }); } class MockFirebaseCore extends Mock @@ -152,3 +273,22 @@ class MockFirebaseCore extends Mock // ignore: avoid_implementing_value_types class FakeFirebaseAppPlatform extends Fake implements FirebaseAppPlatform {} + +class TestFirebaseAppPlatform extends FirebaseAppPlatform { + TestFirebaseAppPlatform( + super.name, + super.options, { + this.onDelete, + }); + + final Future Function()? onDelete; + + @override + Future delete() async { + await onDelete?.call(); + } +} + +class TestService implements FirebaseService {} + +class AnotherTestService implements FirebaseService {} diff --git a/packages/firebase_core/firebase_core/windows/CMakeLists.txt b/packages/firebase_core/firebase_core/windows/CMakeLists.txt index d944944c8c8a..3d856893cd65 100644 --- a/packages/firebase_core/firebase_core/windows/CMakeLists.txt +++ b/packages/firebase_core/firebase_core/windows/CMakeLists.txt @@ -4,7 +4,7 @@ # customers of the plugin. cmake_minimum_required(VERSION 3.14) -set(FIREBASE_SDK_VERSION "13.5.0") +set(FIREBASE_SDK_VERSION "13.9.0") if (EXISTS $ENV{FIREBASE_CPP_SDK_DIR}/include/firebase/version.h) file(READ "$ENV{FIREBASE_CPP_SDK_DIR}/include/firebase/version.h" existing_version) @@ -122,7 +122,7 @@ add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) target_include_directories(${PLUGIN_NAME} INTERFACE "${FIREBASE_CPP_SDK_DIR}/include") -set(FIREBASE_RELEASE_PATH_LIBS firebase_app firebase_auth firebase_remote_config firebase_storage firebase_firestore firebase_database) +set(FIREBASE_RELEASE_PATH_LIBS firebase_app firebase_auth firebase_remote_config firebase_storage firebase_firestore firebase_database firebase_app_check) foreach(firebase_lib IN ITEMS ${FIREBASE_RELEASE_PATH_LIBS}) get_target_property(firebase_lib_path ${firebase_lib} IMPORTED_LOCATION) string(REPLACE "Debug" "Release" firebase_lib_release_path ${firebase_lib_path}) diff --git a/packages/firebase_core/firebase_core/windows/messages.g.cpp b/packages/firebase_core/firebase_core/windows/messages.g.cpp index dfa5388d5c6c..4bfc5dcbf9c4 100644 --- a/packages/firebase_core/firebase_core/windows/messages.g.cpp +++ b/packages/firebase_core/firebase_core/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace firebase_core_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,6 +33,212 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace // CoreFirebaseOptions CoreFirebaseOptions::CoreFirebaseOptions(const std::string& api_key, @@ -49,7 +257,8 @@ CoreFirebaseOptions::CoreFirebaseOptions( const std::string* storage_bucket, const std::string* measurement_id, const std::string* tracking_id, const std::string* deep_link_u_r_l_scheme, const std::string* android_client_id, const std::string* ios_client_id, - const std::string* ios_bundle_id, const std::string* app_group_id) + const std::string* ios_bundle_id, const std::string* app_group_id, + const std::string* recaptcha_site_key) : api_key_(api_key), app_id_(app_id), messaging_sender_id_(messaging_sender_id), @@ -79,7 +288,10 @@ CoreFirebaseOptions::CoreFirebaseOptions( ios_bundle_id_(ios_bundle_id ? std::optional(*ios_bundle_id) : std::nullopt), app_group_id_(app_group_id ? std::optional(*app_group_id) - : std::nullopt) {} + : std::nullopt), + recaptcha_site_key_(recaptcha_site_key + ? std::optional(*recaptcha_site_key) + : std::nullopt) {} const std::string& CoreFirebaseOptions::api_key() const { return api_key_; } @@ -245,9 +457,23 @@ void CoreFirebaseOptions::set_app_group_id(std::string_view value_arg) { app_group_id_ = value_arg; } +const std::string* CoreFirebaseOptions::recaptcha_site_key() const { + return recaptcha_site_key_ ? &(*recaptcha_site_key_) : nullptr; +} + +void CoreFirebaseOptions::set_recaptcha_site_key( + const std::string_view* value_arg) { + recaptcha_site_key_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void CoreFirebaseOptions::set_recaptcha_site_key(std::string_view value_arg) { + recaptcha_site_key_ = value_arg; +} + EncodableList CoreFirebaseOptions::ToEncodableList() const { EncodableList list; - list.reserve(14); + list.reserve(15); list.push_back(EncodableValue(api_key_)); list.push_back(EncodableValue(app_id_)); list.push_back(EncodableValue(messaging_sender_id_)); @@ -273,6 +499,8 @@ EncodableList CoreFirebaseOptions::ToEncodableList() const { : EncodableValue()); list.push_back(app_group_id_ ? EncodableValue(*app_group_id_) : EncodableValue()); + list.push_back(recaptcha_site_key_ ? EncodableValue(*recaptcha_site_key_) + : EncodableValue()); return list; } @@ -323,9 +551,62 @@ CoreFirebaseOptions CoreFirebaseOptions::FromEncodableList( if (!encodable_app_group_id.IsNull()) { decoded.set_app_group_id(std::get(encodable_app_group_id)); } + auto& encodable_recaptcha_site_key = list[14]; + if (!encodable_recaptcha_site_key.IsNull()) { + decoded.set_recaptcha_site_key( + std::get(encodable_recaptcha_site_key)); + } return decoded; } +bool CoreFirebaseOptions::operator==(const CoreFirebaseOptions& other) const { + return PigeonInternalDeepEquals(api_key_, other.api_key_) && + PigeonInternalDeepEquals(app_id_, other.app_id_) && + PigeonInternalDeepEquals(messaging_sender_id_, + other.messaging_sender_id_) && + PigeonInternalDeepEquals(project_id_, other.project_id_) && + PigeonInternalDeepEquals(auth_domain_, other.auth_domain_) && + PigeonInternalDeepEquals(database_u_r_l_, other.database_u_r_l_) && + PigeonInternalDeepEquals(storage_bucket_, other.storage_bucket_) && + PigeonInternalDeepEquals(measurement_id_, other.measurement_id_) && + PigeonInternalDeepEquals(tracking_id_, other.tracking_id_) && + PigeonInternalDeepEquals(deep_link_u_r_l_scheme_, + other.deep_link_u_r_l_scheme_) && + PigeonInternalDeepEquals(android_client_id_, + other.android_client_id_) && + PigeonInternalDeepEquals(ios_client_id_, other.ios_client_id_) && + PigeonInternalDeepEquals(ios_bundle_id_, other.ios_bundle_id_) && + PigeonInternalDeepEquals(app_group_id_, other.app_group_id_) && + PigeonInternalDeepEquals(recaptcha_site_key_, + other.recaptcha_site_key_); +} + +bool CoreFirebaseOptions::operator!=(const CoreFirebaseOptions& other) const { + return !(*this == other); +} + +size_t CoreFirebaseOptions::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(api_key_); + result = result * 31 + PigeonInternalDeepHash(app_id_); + result = result * 31 + PigeonInternalDeepHash(messaging_sender_id_); + result = result * 31 + PigeonInternalDeepHash(project_id_); + result = result * 31 + PigeonInternalDeepHash(auth_domain_); + result = result * 31 + PigeonInternalDeepHash(database_u_r_l_); + result = result * 31 + PigeonInternalDeepHash(storage_bucket_); + result = result * 31 + PigeonInternalDeepHash(measurement_id_); + result = result * 31 + PigeonInternalDeepHash(tracking_id_); + result = result * 31 + PigeonInternalDeepHash(deep_link_u_r_l_scheme_); + result = result * 31 + PigeonInternalDeepHash(android_client_id_); + result = result * 31 + PigeonInternalDeepHash(ios_client_id_); + result = result * 31 + PigeonInternalDeepHash(ios_bundle_id_); + result = result * 31 + PigeonInternalDeepHash(app_group_id_); + result = result * 31 + PigeonInternalDeepHash(recaptcha_site_key_); + return result; +} + +size_t PigeonInternalDeepHash(const CoreFirebaseOptions& v) { return v.Hash(); } + // CoreInitializeResponse CoreInitializeResponse::CoreInitializeResponse( @@ -435,10 +716,39 @@ CoreInitializeResponse CoreInitializeResponse::FromEncodableList( return decoded; } +bool CoreInitializeResponse::operator==( + const CoreInitializeResponse& other) const { + return PigeonInternalDeepEquals(name_, other.name_) && + PigeonInternalDeepEquals(options_, other.options_) && + PigeonInternalDeepEquals( + is_automatic_data_collection_enabled_, + other.is_automatic_data_collection_enabled_) && + PigeonInternalDeepEquals(plugin_constants_, other.plugin_constants_); +} + +bool CoreInitializeResponse::operator!=( + const CoreInitializeResponse& other) const { + return !(*this == other); +} + +size_t CoreInitializeResponse::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(name_); + result = result * 31 + PigeonInternalDeepHash(options_); + result = result * 31 + + PigeonInternalDeepHash(is_automatic_data_collection_enabled_); + result = result * 31 + PigeonInternalDeepHash(plugin_constants_); + return result; +} + +size_t PigeonInternalDeepHash(const CoreInitializeResponse& v) { + return v.Hash(); +} + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { case 129: { return CustomEncodableValue(CoreFirebaseOptions::FromEncodableList( @@ -449,12 +759,12 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( std::get(ReadValue(stream)))); } default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { if (custom_value->type() == typeid(CoreFirebaseOptions)) { @@ -474,23 +784,23 @@ void PigeonInternalCodecSerializer::WriteValue( return; } } - flutter::StandardCodecSerializer::WriteValue(value, stream); + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebaseCoreHostApi. -const flutter::StandardMessageCodec& FirebaseCoreHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebaseCoreHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseCoreHostApi` to handle messages through the // `binary_messenger`. -void FirebaseCoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void FirebaseCoreHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseCoreHostApi* api) { FirebaseCoreHostApi::SetUp(binary_messenger, api, ""); } -void FirebaseCoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void FirebaseCoreHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseCoreHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -507,7 +817,7 @@ void FirebaseCoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -557,7 +867,7 @@ void FirebaseCoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { api->InitializeCore([reply](ErrorOr&& output) { if (output.has_error()) { @@ -587,7 +897,7 @@ void FirebaseCoreHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { api->OptionsFromResource( [reply](ErrorOr&& output) { @@ -623,19 +933,19 @@ EncodableValue FirebaseCoreHostApi::WrapError(const FlutterError& error) { } /// The codec used by FirebaseAppHostApi. -const flutter::StandardMessageCodec& FirebaseAppHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebaseAppHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseAppHostApi` to handle messages through the // `binary_messenger`. -void FirebaseAppHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void FirebaseAppHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAppHostApi* api) { FirebaseAppHostApi::SetUp(binary_messenger, api, ""); } -void FirebaseAppHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void FirebaseAppHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAppHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -652,7 +962,7 @@ void FirebaseAppHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -697,7 +1007,7 @@ void FirebaseAppHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -741,7 +1051,7 @@ void FirebaseAppHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); diff --git a/packages/firebase_core/firebase_core/windows/messages.g.h b/packages/firebase_core/firebase_core/windows/messages.g.h index 68a41114984b..a55996f5b067 100644 --- a/packages/firebase_core/firebase_core/windows/messages.g.h +++ b/packages/firebase_core/firebase_core/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -76,7 +76,8 @@ class CoreFirebaseOptions { const std::string* storage_bucket, const std::string* measurement_id, const std::string* tracking_id, const std::string* deep_link_u_r_l_scheme, const std::string* android_client_id, const std::string* ios_client_id, - const std::string* ios_bundle_id, const std::string* app_group_id); + const std::string* ios_bundle_id, const std::string* app_group_id, + const std::string* recaptcha_site_key); const std::string& api_key() const; void set_api_key(std::string_view value_arg); @@ -130,10 +131,24 @@ class CoreFirebaseOptions { void set_app_group_id(const std::string_view* value_arg); void set_app_group_id(std::string_view value_arg); + const std::string* recaptcha_site_key() const; + void set_recaptcha_site_key(const std::string_view* value_arg); + void set_recaptcha_site_key(std::string_view value_arg); + + bool operator==(const CoreFirebaseOptions& other) const; + bool operator!=(const CoreFirebaseOptions& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static CoreFirebaseOptions FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + + private: friend class CoreInitializeResponse; friend class FirebaseCoreHostApi; friend class FirebaseAppHostApi; @@ -152,6 +167,7 @@ class CoreFirebaseOptions { std::optional ios_client_id_; std::optional ios_bundle_id_; std::optional app_group_id_; + std::optional recaptcha_site_key_; }; // Generated class from Pigeon that represents data sent in messages. @@ -160,13 +176,13 @@ class CoreInitializeResponse { // Constructs an object setting all non-nullable fields. explicit CoreInitializeResponse( const std::string& name, const CoreFirebaseOptions& options, - const flutter::EncodableMap& plugin_constants); + const ::flutter::EncodableMap& plugin_constants); // Constructs an object setting all fields. explicit CoreInitializeResponse( const std::string& name, const CoreFirebaseOptions& options, const bool* is_automatic_data_collection_enabled, - const flutter::EncodableMap& plugin_constants); + const ::flutter::EncodableMap& plugin_constants); ~CoreInitializeResponse() = default; CoreInitializeResponse(const CoreInitializeResponse& other); @@ -184,23 +200,34 @@ class CoreInitializeResponse { void set_is_automatic_data_collection_enabled(const bool* value_arg); void set_is_automatic_data_collection_enabled(bool value_arg); - const flutter::EncodableMap& plugin_constants() const; - void set_plugin_constants(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap& plugin_constants() const; + void set_plugin_constants(const ::flutter::EncodableMap& value_arg); + + bool operator==(const CoreInitializeResponse& other) const; + bool operator!=(const CoreInitializeResponse& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; private: static CoreInitializeResponse FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + ::flutter::EncodableList ToEncodableList() const; + + private: friend class FirebaseCoreHostApi; friend class FirebaseAppHostApi; friend class PigeonInternalCodecSerializer; std::string name_; std::unique_ptr options_; std::optional is_automatic_data_collection_enabled_; - flutter::EncodableMap plugin_constants_; + ::flutter::EncodableMap plugin_constants_; }; -class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -208,12 +235,12 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -228,21 +255,21 @@ class FirebaseCoreHostApi { const CoreFirebaseOptions& initialize_app_request, std::function reply)> result) = 0; virtual void InitializeCore( - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void OptionsFromResource( std::function reply)> result) = 0; // The codec used by FirebaseCoreHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseCoreHostApi` to handle messages through the // `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseCoreHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseCoreHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseCoreHostApi() = default; @@ -265,16 +292,16 @@ class FirebaseAppHostApi { std::function reply)> result) = 0; // The codec used by FirebaseAppHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseAppHostApi` to handle messages through the // `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAppHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseAppHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseAppHostApi() = default; diff --git a/packages/firebase_core/firebase_core_platform_interface/CHANGELOG.md b/packages/firebase_core/firebase_core_platform_interface/CHANGELOG.md index 07ddb5c0bc17..7a2bed6fa102 100644 --- a/packages/firebase_core/firebase_core_platform_interface/CHANGELOG.md +++ b/packages/firebase_core/firebase_core_platform_interface/CHANGELOG.md @@ -1,3 +1,19 @@ +## 7.1.0 + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + - **FEAT**(core): Add Recaptcha Site Key to FirebaseOptions ([#18334](https://github.com/firebase/flutterfire/issues/18334)). ([57be7027](https://github.com/firebase/flutterfire/commit/57be702778d34b9b7e86b40817d93acaec4c3ca4)) + +## 7.0.1 + + - **FIX**(core,iOS): use namespaced iOS Pigeon header import ([#18281](https://github.com/firebase/flutterfire/issues/18281)). ([7c1257e7](https://github.com/firebase/flutterfire/commit/7c1257e7295f9ba67f3f5820493f105a14d34d52)) + +## 7.0.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + ## 6.0.3 - **REFACTOR**: fix formatting and analysis issues across the repo ([#18124](https://github.com/firebase/flutterfire/issues/18124)). ([ab79fd93](https://github.com/firebase/flutterfire/commit/ab79fd93ee4ccfeb478687623134b1cf8ab71c74)) diff --git a/packages/firebase_core/firebase_core_platform_interface/lib/src/firebase_options.dart b/packages/firebase_core/firebase_core_platform_interface/lib/src/firebase_options.dart index ad0e580e88a8..dbc740a71ffe 100644 --- a/packages/firebase_core/firebase_core_platform_interface/lib/src/firebase_options.dart +++ b/packages/firebase_core/firebase_core_platform_interface/lib/src/firebase_options.dart @@ -50,6 +50,7 @@ class FirebaseOptions { this.iosClientId, this.iosBundleId, this.appGroupId, + this.recaptchaSiteKey, }); /// Named constructor to create [FirebaseOptions] from a the response of Pigeon channel. @@ -71,7 +72,8 @@ class FirebaseOptions { androidClientId = options.androidClientId, iosClientId = options.iosClientId, iosBundleId = options.iosBundleId, - appGroupId = options.appGroupId; + appGroupId = options.appGroupId, + recaptchaSiteKey = options.recaptchaSiteKey; /// Returns a copy of this FirebaseOptions with the given fields replaced with /// the new values. @@ -90,6 +92,7 @@ class FirebaseOptions { String? iosClientId, String? iosBundleId, String? appGroupId, + String? recaptchaSiteKey, }) { return FirebaseOptions( apiKey: apiKey ?? this.apiKey, @@ -106,6 +109,7 @@ class FirebaseOptions { iosClientId: iosClientId ?? this.iosClientId, iosBundleId: iosBundleId ?? this.iosBundleId, appGroupId: appGroupId ?? this.appGroupId, + recaptchaSiteKey: recaptchaSiteKey ?? this.recaptchaSiteKey, ); } @@ -174,6 +178,9 @@ class FirebaseOptions { /// This property is used on iOS only. final String? appGroupId; + /// The reCAPTCHA site key used for App Check. + final String? recaptchaSiteKey; + /// The current instance as a [Map]. Map get asMap { return { @@ -191,6 +198,7 @@ class FirebaseOptions { 'iosClientId': iosClientId, 'iosBundleId': iosBundleId, 'appGroupId': appGroupId, + 'recaptchaSiteKey': recaptchaSiteKey, }; } diff --git a/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase.dart b/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase.dart index bb5d2636000f..dff6228fdce1 100644 --- a/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase.dart +++ b/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase.dart @@ -33,7 +33,7 @@ class MethodChannelFirebase extends FirebasePlatform { } /// Creates and attaches a new [MethodChannelFirebaseApp] to the [MethodChannelFirebase] - /// and adds any constants to the [FirebasePluginPlatform] class. + /// and adds any constants to the [FirebasePlugin] class. void _initializeFirebaseAppFromMap(CoreInitializeResponse response) { MethodChannelFirebaseApp methodChannelFirebaseApp = MethodChannelFirebaseApp( @@ -45,11 +45,44 @@ class MethodChannelFirebase extends FirebasePlatform { appInstances[methodChannelFirebaseApp.name] = methodChannelFirebaseApp; - FirebasePluginPlatform - ._constantsForPluginApps[methodChannelFirebaseApp.name] = + FirebasePlugin._constantsForPluginApps[methodChannelFirebaseApp.name] = response.pluginConstants; } + CoreInitializeResponse _preserveRecaptchaSiteKey( + CoreInitializeResponse response, + FirebaseOptions options, + ) { + if (response.options.recaptchaSiteKey != null || + options.recaptchaSiteKey == null) { + return response; + } + + return CoreInitializeResponse( + name: response.name, + options: CoreFirebaseOptions( + apiKey: response.options.apiKey, + appId: response.options.appId, + messagingSenderId: response.options.messagingSenderId, + projectId: response.options.projectId, + authDomain: response.options.authDomain, + databaseURL: response.options.databaseURL, + storageBucket: response.options.storageBucket, + measurementId: response.options.measurementId, + trackingId: response.options.trackingId, + deepLinkURLScheme: response.options.deepLinkURLScheme, + androidClientId: response.options.androidClientId, + iosClientId: response.options.iosClientId, + iosBundleId: response.options.iosBundleId, + appGroupId: response.options.appGroupId, + recaptchaSiteKey: options.recaptchaSiteKey, + ), + isAutomaticDataCollectionEnabled: + response.isAutomaticDataCollectionEnabled, + pluginConstants: response.pluginConstants, + ); + } + /// Returns the created [FirebaseAppPlatform] instances. @override List get apps { @@ -90,24 +123,27 @@ class MethodChannelFirebase extends FirebasePlatform { // If no options are present & no default app has been setup, the user is // trying to initialize default from Dart if (defaultApp == null && _options != null) { - _initializeFirebaseAppFromMap(await api.initializeApp( - defaultFirebaseAppName, - CoreFirebaseOptions( - apiKey: _options.apiKey, - appId: _options.appId, - messagingSenderId: _options.messagingSenderId, - projectId: _options.projectId, - authDomain: _options.authDomain, - databaseURL: _options.databaseURL, - storageBucket: _options.storageBucket, - measurementId: _options.measurementId, - trackingId: _options.trackingId, - deepLinkURLScheme: _options.deepLinkURLScheme, - androidClientId: _options.androidClientId, - iosClientId: _options.iosClientId, - iosBundleId: _options.iosBundleId, - appGroupId: _options.appGroupId, - ))); + _initializeFirebaseAppFromMap(_preserveRecaptchaSiteKey( + await api.initializeApp( + defaultFirebaseAppName, + CoreFirebaseOptions( + apiKey: _options.apiKey, + appId: _options.appId, + messagingSenderId: _options.messagingSenderId, + projectId: _options.projectId, + authDomain: _options.authDomain, + databaseURL: _options.databaseURL, + storageBucket: _options.storageBucket, + measurementId: _options.measurementId, + trackingId: _options.trackingId, + deepLinkURLScheme: _options.deepLinkURLScheme, + androidClientId: _options.androidClientId, + iosClientId: _options.iosClientId, + iosBundleId: _options.iosBundleId, + appGroupId: _options.appGroupId, + recaptchaSiteKey: _options.recaptchaSiteKey, + )), + _options)); defaultApp = appInstances[defaultFirebaseAppName]; } @@ -156,24 +192,27 @@ class MethodChannelFirebase extends FirebasePlatform { } } - _initializeFirebaseAppFromMap(await api.initializeApp( - name, - CoreFirebaseOptions( - apiKey: options!.apiKey, - appId: options.appId, - messagingSenderId: options.messagingSenderId, - projectId: options.projectId, - authDomain: options.authDomain, - databaseURL: options.databaseURL, - storageBucket: options.storageBucket, - measurementId: options.measurementId, - trackingId: options.trackingId, - deepLinkURLScheme: options.deepLinkURLScheme, - androidClientId: options.androidClientId, - iosClientId: options.iosClientId, - iosBundleId: options.iosBundleId, - appGroupId: options.appGroupId, - ))); + _initializeFirebaseAppFromMap(_preserveRecaptchaSiteKey( + await api.initializeApp( + name, + CoreFirebaseOptions( + apiKey: options!.apiKey, + appId: options.appId, + messagingSenderId: options.messagingSenderId, + projectId: options.projectId, + authDomain: options.authDomain, + databaseURL: options.databaseURL, + storageBucket: options.storageBucket, + measurementId: options.measurementId, + trackingId: options.trackingId, + deepLinkURLScheme: options.deepLinkURLScheme, + androidClientId: options.androidClientId, + iosClientId: options.iosClientId, + iosBundleId: options.iosBundleId, + appGroupId: options.appGroupId, + recaptchaSiteKey: options.recaptchaSiteKey, + )), + options)); return appInstances[name]!; } diff --git a/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase_app.dart b/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase_app.dart index 6689c25ae0e2..1b33d6cb28a1 100644 --- a/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase_app.dart +++ b/packages/firebase_core/firebase_core_platform_interface/lib/src/method_channel/method_channel_firebase_app.dart @@ -52,7 +52,7 @@ class MethodChannelFirebaseApp extends FirebaseAppPlatform { await _api.delete(name); MethodChannelFirebase.appInstances.remove(name); - FirebasePluginPlatform._constantsForPluginApps.remove(name); + FirebasePlugin._constantsForPluginApps.remove(name); _isDeleted = true; } diff --git a/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/messages.pigeon.dart index d07a1ca4b84a..9bb893bb3aba 100644 --- a/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,21 +1,40 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.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, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -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".', - ); +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } List wrapResponse( @@ -30,20 +49,67 @@ List wrapResponse( } bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } if (a is List && b is List) { return a.length == b.length && a.indexed .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; } return a == b; } +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + class CoreFirebaseOptions { CoreFirebaseOptions({ required this.apiKey, @@ -60,6 +126,7 @@ class CoreFirebaseOptions { this.iosClientId, this.iosBundleId, this.appGroupId, + this.recaptchaSiteKey, }); String apiKey; @@ -90,6 +157,8 @@ class CoreFirebaseOptions { String? appGroupId; + String? recaptchaSiteKey; + List _toList() { return [ apiKey, @@ -106,6 +175,7 @@ class CoreFirebaseOptions { iosClientId, iosBundleId, appGroupId, + recaptchaSiteKey, ]; } @@ -130,6 +200,7 @@ class CoreFirebaseOptions { iosClientId: result[11] as String?, iosBundleId: result[12] as String?, appGroupId: result[13] as String?, + recaptchaSiteKey: result[14] as String?, ); } @@ -142,12 +213,26 @@ class CoreFirebaseOptions { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(apiKey, other.apiKey) && + _deepEquals(appId, other.appId) && + _deepEquals(messagingSenderId, other.messagingSenderId) && + _deepEquals(projectId, other.projectId) && + _deepEquals(authDomain, other.authDomain) && + _deepEquals(databaseURL, other.databaseURL) && + _deepEquals(storageBucket, other.storageBucket) && + _deepEquals(measurementId, other.measurementId) && + _deepEquals(trackingId, other.trackingId) && + _deepEquals(deepLinkURLScheme, other.deepLinkURLScheme) && + _deepEquals(androidClientId, other.androidClientId) && + _deepEquals(iosClientId, other.iosClientId) && + _deepEquals(iosBundleId, other.iosBundleId) && + _deepEquals(appGroupId, other.appGroupId) && + _deepEquals(recaptchaSiteKey, other.recaptchaSiteKey); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class CoreInitializeResponse { @@ -186,7 +271,7 @@ class CoreInitializeResponse { options: result[1]! as CoreFirebaseOptions, isAutomaticDataCollectionEnabled: result[2] as bool?, pluginConstants: - (result[3] as Map?)!.cast(), + (result[3]! as Map).cast(), ); } @@ -199,12 +284,16 @@ class CoreInitializeResponse { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(name, other.name) && + _deepEquals(options, other.options) && + _deepEquals(isAutomaticDataCollectionEnabled, + other.isAutomaticDataCollectionEnabled) && + _deepEquals(pluginConstants, other.pluginConstants); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class _PigeonCodec extends StandardMessageCodec { @@ -255,95 +344,62 @@ class FirebaseCoreHostApi { Future initializeApp( String appName, CoreFirebaseOptions initializeAppRequest) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeApp$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, initializeAppRequest]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 CoreInitializeResponse?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as CoreInitializeResponse; } Future> initializeCore() async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeCore$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 List?)! - .cast(); - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as List) + .cast(); } Future optionsFromResource() async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.optionsFromResource$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 CoreFirebaseOptions?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as CoreFirebaseOptions; } } @@ -364,81 +420,60 @@ class FirebaseAppHostApi { Future setAutomaticDataCollectionEnabled( String appName, bool enabled) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticDataCollectionEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, enabled]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setAutomaticResourceManagementEnabled( String appName, bool enabled) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticResourceManagementEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, enabled]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future delete(String appName) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.delete$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } diff --git a/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/test_api.dart b/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/test_api.dart index fbf2394b4022..9dd32fe50dba 100644 --- a/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/test_api.dart +++ b/packages/firebase_core/firebase_core_platform_interface/lib/src/pigeon/test_api.dart @@ -1,9 +1,9 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers +// 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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -64,9 +64,7 @@ abstract class TestFirebaseCoreHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeApp$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -77,19 +75,13 @@ abstract class TestFirebaseCoreHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeApp was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeApp was null, expected non-null String.'); - final CoreFirebaseOptions? arg_initializeAppRequest = - (args[1] as CoreFirebaseOptions?); - assert(arg_initializeAppRequest != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeApp was null, expected non-null CoreFirebaseOptions.'); + final List args = message! as List; + final String arg_appName = args[0]! as String; + final CoreFirebaseOptions arg_initializeAppRequest = + args[1]! as CoreFirebaseOptions; try { - final CoreInitializeResponse output = await api.initializeApp( - arg_appName!, arg_initializeAppRequest!); + final CoreInitializeResponse output = + await api.initializeApp(arg_appName, arg_initializeAppRequest); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -101,9 +93,7 @@ abstract class TestFirebaseCoreHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.initializeCore$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -128,9 +118,7 @@ abstract class TestFirebaseCoreHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseCoreHostApi.optionsFromResource$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -176,9 +164,7 @@ abstract class TestFirebaseAppHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticDataCollectionEnabled$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -189,18 +175,12 @@ abstract class TestFirebaseAppHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticDataCollectionEnabled was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticDataCollectionEnabled was null, expected non-null String.'); - final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticDataCollectionEnabled was null, expected non-null bool.'); + final List args = message! as List; + final String arg_appName = args[0]! as String; + final bool arg_enabled = args[1]! as bool; try { await api.setAutomaticDataCollectionEnabled( - arg_appName!, arg_enabled!); + arg_appName, arg_enabled); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -212,9 +192,7 @@ abstract class TestFirebaseAppHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticResourceManagementEnabled$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -225,18 +203,12 @@ abstract class TestFirebaseAppHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticResourceManagementEnabled was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticResourceManagementEnabled was null, expected non-null String.'); - final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.setAutomaticResourceManagementEnabled was null, expected non-null bool.'); + final List args = message! as List; + final String arg_appName = args[0]! as String; + final bool arg_enabled = args[1]! as bool; try { await api.setAutomaticResourceManagementEnabled( - arg_appName!, arg_enabled!); + arg_appName, arg_enabled); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -248,9 +220,7 @@ abstract class TestFirebaseAppHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.delete$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -261,14 +231,10 @@ abstract class TestFirebaseAppHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.delete was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_core_platform_interface.FirebaseAppHostApi.delete was null, expected non-null String.'); + final List args = message! as List; + final String arg_appName = args[0]! as String; try { - await api.delete(arg_appName!); + await api.delete(arg_appName); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/firebase_core/firebase_core_platform_interface/lib/src/platform_interface/platform_interface_firebase_plugin.dart b/packages/firebase_core/firebase_core_platform_interface/lib/src/platform_interface/platform_interface_firebase_plugin.dart index afd5dcfbb499..42e470fdc1b6 100644 --- a/packages/firebase_core/firebase_core_platform_interface/lib/src/platform_interface/platform_interface_firebase_plugin.dart +++ b/packages/firebase_core/firebase_core_platform_interface/lib/src/platform_interface/platform_interface_firebase_plugin.dart @@ -5,14 +5,13 @@ part of '../../firebase_core_platform_interface.dart'; -/// The interface that other FlutterFire plugins must extend. +/// The base class that other FlutterFire plugins must extend. /// /// This class provides access to common plugin properties and constants which /// are available once the user has initialized FlutterFire. -abstract class FirebasePluginPlatform extends PlatformInterface { +abstract class FirebasePlugin { // ignore: public_member_api_docs - FirebasePluginPlatform(this._appName, this._methodChannelName) - : super(token: _token); + FirebasePlugin(this._appName, this._methodChannelName); /// The global data store for all constants, for each plugin and [FirebaseAppPlatform] instance. /// @@ -26,13 +25,6 @@ abstract class FirebasePluginPlatform extends PlatformInterface { final String _methodChannelName; - static final Object _token = Object(); - - // ignore: public_member_api_docs - static void verify(FirebasePluginPlatform instance) { - PlatformInterface.verify(instance, _token); - } - /// Returns any plugin constants this plugin app instance has initialized. Map get pluginConstants { final appConstants = diff --git a/packages/firebase_core/firebase_core_platform_interface/pigeons/messages.dart b/packages/firebase_core/firebase_core_platform_interface/pigeons/messages.dart index 977270c87dc1..a42278d0f4e7 100644 --- a/packages/firebase_core/firebase_core_platform_interface/pigeons/messages.dart +++ b/packages/firebase_core/firebase_core_platform_interface/pigeons/messages.dart @@ -19,6 +19,9 @@ import 'package:pigeon/pigeon.dart'; '../firebase_core/ios/firebase_core/Sources/firebase_core/include/firebase_core/messages.g.h', objcSourceOut: '../firebase_core/ios/firebase_core/Sources/firebase_core/messages.g.m', + objcOptions: ObjcOptions( + headerIncludePath: 'include/firebase_core/messages.g.h', + ), cppHeaderOut: '../firebase_core/windows/messages.g.h', cppSourceOut: '../firebase_core/windows/messages.g.cpp', cppOptions: CppOptions(namespace: 'firebase_core_windows'), @@ -41,6 +44,7 @@ class CoreFirebaseOptions { required this.databaseURL, required this.storageBucket, required this.trackingId, + required this.recaptchaSiteKey, }); final String apiKey; @@ -70,6 +74,8 @@ class CoreFirebaseOptions { final String? iosBundleId; final String? appGroupId; + + final String? recaptchaSiteKey; } class CoreInitializeResponse { diff --git a/packages/firebase_core/firebase_core_platform_interface/pubspec.yaml b/packages/firebase_core/firebase_core_platform_interface/pubspec.yaml index 907abb8617f6..5e994a589e5a 100644 --- a/packages/firebase_core/firebase_core_platform_interface/pubspec.yaml +++ b/packages/firebase_core/firebase_core_platform_interface/pubspec.yaml @@ -4,11 +4,12 @@ homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_co repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_core/firebase_core_platform_interface # 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: 6.0.3 +version: 7.1.0 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: collection: ^1.0.0 @@ -22,4 +23,4 @@ dependencies: dev_dependencies: mockito: ^5.4.0 - pigeon: 25.3.2 + pigeon: 26.3.4 diff --git a/packages/firebase_core/firebase_core_platform_interface/test/firebase_options_test.dart b/packages/firebase_core/firebase_core_platform_interface/test/firebase_options_test.dart index 8b84f78e9525..2291efa88878 100644 --- a/packages/firebase_core/firebase_core_platform_interface/test/firebase_options_test.dart +++ b/packages/firebase_core/firebase_core_platform_interface/test/firebase_options_test.dart @@ -55,6 +55,7 @@ void main() { appId: 'appId', messagingSenderId: 'messagingSenderId', projectId: 'projectId', + recaptchaSiteKey: 'recaptchaSiteKey', ), ); @@ -63,6 +64,7 @@ void main() { appId: 'appId', messagingSenderId: 'messagingSenderId', projectId: 'projectId', + recaptchaSiteKey: 'recaptchaSiteKey', ); expect(options1 == options2, isTrue); @@ -91,6 +93,7 @@ void main() { iosClientId: 'newIosClientId', iosBundleId: 'newIosBundleId', appGroupId: 'newAppGroupId', + recaptchaSiteKey: 'newRecaptchaSiteKey', ); expect( @@ -110,6 +113,7 @@ void main() { iosClientId: 'newIosClientId', iosBundleId: 'newIosBundleId', appGroupId: 'newAppGroupId', + recaptchaSiteKey: 'newRecaptchaSiteKey', ), ); }); @@ -130,6 +134,7 @@ void main() { iosBundleId: 'iosBundleId', iosClientId: 'iosClientId', appGroupId: 'appGroupId', + recaptchaSiteKey: 'recaptchaSiteKey', ); expect(options.asMap, { @@ -147,6 +152,7 @@ void main() { 'iosBundleId': 'iosBundleId', 'iosClientId': 'iosClientId', 'appGroupId': 'appGroupId', + 'recaptchaSiteKey': 'recaptchaSiteKey', }); }); }); diff --git a/packages/firebase_core/firebase_core_platform_interface/test/platform_interface_tests/platform_interface_firebase_core_test.dart b/packages/firebase_core/firebase_core_platform_interface/test/platform_interface_tests/platform_interface_firebase_core_test.dart index 63e1e24534d9..5393a22f99d8 100644 --- a/packages/firebase_core/firebase_core_platform_interface/test/platform_interface_tests/platform_interface_firebase_core_test.dart +++ b/packages/firebase_core/firebase_core_platform_interface/test/platform_interface_tests/platform_interface_firebase_core_test.dart @@ -39,6 +39,12 @@ void main() { FirebasePlatform.instance = mock; }); }); + + group('$FirebasePlugin', () { + test('is not a platform interface', () { + expect(TestFirebasePlugin(), isNot(isA())); + }); + }); } class ImplementsFirebasePlatform implements FirebasePlatform { @@ -70,3 +76,7 @@ class FirebaseCoreMockPlatform extends Mock MockPlatformInterfaceMixin implements FirebasePlatform {} + +class TestFirebasePlugin extends FirebasePlugin { + TestFirebasePlugin() : super(defaultFirebaseAppName, 'test_plugin'); +} diff --git a/packages/firebase_core/firebase_core_web/CHANGELOG.md b/packages/firebase_core/firebase_core_web/CHANGELOG.md index 0f7cd03e6ed1..5fc2c8fd90a6 100644 --- a/packages/firebase_core/firebase_core_web/CHANGELOG.md +++ b/packages/firebase_core/firebase_core_web/CHANGELOG.md @@ -1,3 +1,27 @@ +## 3.9.0 + + - **FEAT**(appcheck): appcheck reCAPTCHA mobile support (gradually rolling out) ([#18261](https://github.com/firebase/flutterfire/issues/18261)). ([036a860a](https://github.com/firebase/flutterfire/commit/036a860a0e66d46b5c57eb3df3a0f9e5846ef00b)) + - **FEAT**(core): bump Firebase web SDK to 12.15.0 ([#18376](https://github.com/firebase/flutterfire/issues/18376)). ([22eb4d5d](https://github.com/firebase/flutterfire/commit/22eb4d5d0f3f14207e080e9c9fc8373052258ef4)) + - **FEAT**(core): Add Recaptcha Site Key to FirebaseOptions ([#18334](https://github.com/firebase/flutterfire/issues/18334)). ([57be7027](https://github.com/firebase/flutterfire/commit/57be702778d34b9b7e86b40817d93acaec4c3ca4)) + +## 3.8.0 + + - **FEAT**(core): bump Firebase web SDK to 12.14.0 ([#18331](https://github.com/firebase/flutterfire/issues/18331)). ([3f31a88a](https://github.com/firebase/flutterfire/commit/3f31a88ab6ad96914f427e292b919b6465cf4996)) + +## 3.7.0 + + - **FEAT**: bump Firebase JS SDK to 12.13.0 ([#18274](https://github.com/firebase/flutterfire/issues/18274)). ([bb8ad546](https://github.com/firebase/flutterfire/commit/bb8ad546f114146b6e1cd26c3296825e2964745d)) + +## 3.6.1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 3.6.0 + + - **FEAT**: support for Firestore Pipelines ([#18183](https://github.com/firebase/flutterfire/issues/18183)). ([d734cf08](https://github.com/firebase/flutterfire/commit/d734cf0885f6d9403c2fb3ac48d6c52e14199309)) + - **FEAT**: bump JS SDK to version 12.12.0 ([#18186](https://github.com/firebase/flutterfire/issues/18186)). ([3d943ed4](https://github.com/firebase/flutterfire/commit/3d943ed4154eb61617746825fc5c1c90f1e73d88)) + - **FEAT**: bump JS SDK to version 12.11.0 ([#18160](https://github.com/firebase/flutterfire/issues/18160)). ([b3ab0003](https://github.com/firebase/flutterfire/commit/b3ab00036c70debca59414ea236c5012fb841a63)) + ## 3.5.1 - Update a dependency to the latest release. diff --git a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart index 34729d337821..4ce9bf96a5a6 100644 --- a/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/firebase_core_web.dart @@ -27,17 +27,23 @@ FirebaseAppPlatform _createFromJsApp(firebase.App jsApp) { return FirebaseAppWeb._(jsApp.name, _createFromJsOptions(jsApp.options)); } +String? _safeToDart(JSString? jsString) { + if (jsString == null || jsString.isUndefinedOrNull) return null; + return jsString.toDart; +} + /// Returns a [FirebaseOptions] instance from [firebase.FirebaseOptions]. FirebaseOptions _createFromJsOptions(firebase.FirebaseOptions options) { return FirebaseOptions( - apiKey: options.apiKey?.toDart ?? '', - projectId: options.projectId?.toDart ?? '', - authDomain: options.authDomain?.toDart, - databaseURL: options.databaseURL?.toDart, - storageBucket: options.storageBucket?.toDart, - messagingSenderId: options.messagingSenderId?.toDart ?? '', - appId: options.appId?.toDart ?? '', - measurementId: options.measurementId?.toDart, + apiKey: _safeToDart(options.apiKey) ?? '', + projectId: _safeToDart(options.projectId) ?? '', + authDomain: _safeToDart(options.authDomain), + databaseURL: _safeToDart(options.databaseURL), + storageBucket: _safeToDart(options.storageBucket), + messagingSenderId: _safeToDart(options.messagingSenderId) ?? '', + appId: _safeToDart(options.appId) ?? '', + measurementId: _safeToDart(options.measurementId), + recaptchaSiteKey: _safeToDart(options.recaptchaSiteKey), ); } diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_version.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_version.dart index b92a03e487fe..b30db3ebafec 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_version.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '4.6.0'; +const packageVersion = '4.11.0'; diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart index b70f503d9ff3..1cd96ad51f99 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart @@ -214,6 +214,17 @@ class FirebaseCoreWeb extends FirebasePlatform { return Future.value(); } + const firestoreServiceName = 'firestore'; + + if (service.name == firestoreServiceName) { + // Inject the Firestore Pipelines script. This bundle supports both + // Pipeline operations (Enterprise edition) and standard Firestore queries. + return injectSrcScript( + 'https://www.gstatic.com/firebasejs/$version/firebase-firestore-pipelines.js', + 'firebase_$firestoreServiceName', + ); + } + return injectSrcScript( 'https://www.gstatic.com/firebasejs/$version/firebase-${service.name}.js', 'firebase_${service.override ?? service.name}', @@ -332,6 +343,7 @@ class FirebaseCoreWeb extends FirebasePlatform { messagingSenderId: options.messagingSenderId, appId: options.appId, measurementId: options.measurementId, + recaptchaSiteKey: options.recaptchaSiteKey, ); } } @@ -354,6 +366,7 @@ class FirebaseCoreWeb extends FirebasePlatform { messagingSenderId: options.messagingSenderId, appId: options.appId, measurementId: options.measurementId, + recaptchaSiteKey: options.recaptchaSiteKey, ); } catch (e) { if (_getJSErrorCode(e as JSError) == 'app/duplicate-app') { diff --git a/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart b/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart index 3e04c6928134..9da313efa4b7 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart @@ -6,4 +6,4 @@ part of '../firebase_core_web.dart'; /// The currently supported Firebase JS SDK version. -const String supportedFirebaseJsSdkVersion = '12.9.0'; +const String supportedFirebaseJsSdkVersion = '12.15.0'; diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart index 36f439e65689..254d07f38292 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/core.dart @@ -33,6 +33,7 @@ App initializeApp({ String? name, String? measurementId, String? appId, + String? recaptchaSiteKey, }) { name ??= defaultFirebaseAppName; @@ -47,6 +48,7 @@ App initializeApp({ messagingSenderId: messagingSenderId?.toJS, measurementId: measurementId?.toJS, appId: appId?.toJS, + recaptchaSiteKey: recaptchaSiteKey?.toJS, ), name.toJS, ), diff --git a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart index c4f3be7aa58e..1aeaafe4ebd0 100644 --- a/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart +++ b/packages/firebase_core/firebase_core_web/lib/src/interop/core_interop.dart @@ -62,6 +62,7 @@ extension type FirebaseOptions._(JSObject _) implements JSObject { JSString? messagingSenderId, JSString? measurementId, JSString? appId, + JSString? recaptchaSiteKey, }); } @@ -82,4 +83,6 @@ extension FirebaseOptionsExtension on FirebaseOptions { external set measurementId(JSString? s); external JSString? get appId; external set appId(JSString? s); + external JSString? get recaptchaSiteKey; + external set recaptchaSiteKey(JSString? s); } diff --git a/packages/firebase_core/firebase_core_web/pubspec.yaml b/packages/firebase_core/firebase_core_web/pubspec.yaml index 54ce8f07fbca..c04a1f978ffb 100644 --- a/packages/firebase_core/firebase_core_web/pubspec.yaml +++ b/packages/firebase_core/firebase_core_web/pubspec.yaml @@ -2,14 +2,15 @@ name: firebase_core_web description: The web implementation of firebase_core homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_core/firebase_core_web repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_core/firebase_core_web -version: 3.5.1 +version: 3.9.0 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/firebase_crashlytics/firebase_crashlytics/CHANGELOG.md b/packages/firebase_crashlytics/firebase_crashlytics/CHANGELOG.md index e51572825aaf..3935a1bb77e5 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/CHANGELOG.md +++ b/packages/firebase_crashlytics/firebase_crashlytics/CHANGELOG.md @@ -1,3 +1,24 @@ +## 5.2.4 + + - Update a dependency to the latest release. + +## 5.2.3 + + - Update a dependency to the latest release. + +## 5.2.2 + + - Update a dependency to the latest release. + +## 5.2.1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 5.2.0 + + - **FIX**(crashlytics,android): fix an issue with deobfuscating flavored builds ([#18085](https://github.com/firebase/flutterfire/issues/18085)). ([55a7f6ff](https://github.com/firebase/flutterfire/commit/55a7f6ff17940487e29d8bc78779ca4cfce24b0c)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 5.1.0 - **FEAT**(ios): migrate iOS to UIScene lifecycle ([#18054](https://github.com/firebase/flutterfire/issues/18054)). ([3ffa4110](https://github.com/firebase/flutterfire/commit/3ffa411098132fd5182a84be4e7a226106bc7451)) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/ElfBuildIdReader.java b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/ElfBuildIdReader.java new file mode 100644 index 000000000000..0e6b9561168a --- /dev/null +++ b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/ElfBuildIdReader.java @@ -0,0 +1,398 @@ +// Copyright 2024 The Chromium 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.firebase.crashlytics; + +import android.content.Context; +import android.util.Log; +import java.io.File; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Reads the ELF build ID from libapp.so at runtime. + * + *

The Firebase CLI's {@code crashlytics:symbols:upload} command uses the ELF build ID (from the + * {@code .note.gnu.build-id} section) when uploading symbols. To ensure Crashlytics can match crash + * reports to uploaded symbols, the plugin must report the same ELF build ID rather than the Dart + * VM's internal snapshot build ID (which may differ, especially for AAB + flavor builds). + */ +final class ElfBuildIdReader { + + private static final String TAG = "FLTFirebaseCrashlytics"; + + private static final byte[] ELF_MAGIC = {0x7f, 'E', 'L', 'F'}; + private static final int ELFCLASS64 = 2; + private static final int PT_NOTE = 4; + private static final int NT_GNU_BUILD_ID = 3; + private static final String GNU_NOTE_NAME = "GNU"; + + private ElfBuildIdReader() {} + + /** + * Reads the ELF build ID from libapp.so. + * + *

First checks the native library directory (for devices that extract native libs). If not + * found there, reads libapp.so from inside the APK (for devices with extractNativeLibs=false). + * + * @return the build ID as a lowercase hex string, or {@code null} if it cannot be read. + */ + static String readBuildId(Context context) { + try { + // Try extracted native library first. + String nativeLibDir = context.getApplicationInfo().nativeLibraryDir; + File libApp = new File(nativeLibDir, "libapp.so"); + if (libApp.exists()) { + return readBuildIdFromElf(libApp); + } + + // Fall back to reading from inside the APK (or split APKs for AAB installs). + return readBuildIdFromApk(context); + } catch (Exception e) { + Log.d(TAG, "Could not read ELF build ID from libapp.so", e); + return null; + } + } + + /** + * Reads the ELF build ID from libapp.so stored inside the APK. On newer Android versions, native + * libraries may not be extracted to the filesystem. + */ + private static String readBuildIdFromApk(Context context) throws Exception { + // Check the base APK first. + String result = readBuildIdFromZip(context.getApplicationInfo().sourceDir); + if (result != null) { + return result; + } + + // For AAB installs, libapp.so is in a split APK (e.g., split_config.arm64_v8a.apk). + String[] splitDirs = context.getApplicationInfo().splitSourceDirs; + if (splitDirs != null) { + for (String splitDir : splitDirs) { + result = readBuildIdFromZip(splitDir); + if (result != null) { + return result; + } + } + } + return null; + } + + private static String readBuildIdFromZip(String apkPath) throws Exception { + try (ZipFile zipFile = new ZipFile(apkPath)) { + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (entry.getName().endsWith("/libapp.so")) { + try (InputStream is = zipFile.getInputStream(entry)) { + byte[] elfData = new byte[(int) entry.getSize()]; + int offset = 0; + while (offset < elfData.length) { + int read = is.read(elfData, offset, elfData.length - offset); + if (read < 0) break; + offset += read; + } + return readBuildIdFromBytes(elfData); + } + } + } + } + return null; + } + + private static String readBuildIdFromElf(File elfFile) throws Exception { + try (RandomAccessFile raf = new RandomAccessFile(elfFile, "r")) { + return readBuildIdFromRaf(raf); + } + } + + private static String readBuildIdFromBytes(byte[] data) { + try { + ByteBuffer buf = ByteBuffer.wrap(data); + + // Verify ELF magic bytes. + for (int i = 0; i < 4; i++) { + if (buf.get() != ELF_MAGIC[i]) { + return null; + } + } + + int elfClass = buf.get() & 0xFF; // 1 = 32-bit, 2 = 64-bit + boolean is64 = elfClass == ELFCLASS64; + + int dataEncoding = buf.get() & 0xFF; // 1 = little-endian, 2 = big-endian + ByteOrder order = dataEncoding == 1 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; + buf.order(order); + + if (is64) { + return readBuildIdFromBuffer64(buf); + } else { + return readBuildIdFromBuffer32(buf); + } + } catch (Exception e) { + Log.d(TAG, "Could not parse ELF from APK", e); + return null; + } + } + + private static String readBuildIdFromBuffer64(ByteBuffer buf) { + // e_phoff is at offset 32 in the 64-bit ELF header. + buf.position(32); + long phoff = buf.getLong(); + + // e_phentsize is at offset 54, e_phnum at offset 56. + buf.position(54); + int phentsize = buf.getShort() & 0xFFFF; + int phnum = buf.getShort() & 0xFFFF; + + for (int i = 0; i < phnum; i++) { + int phdr = (int) (phoff + (long) i * phentsize); + buf.position(phdr); + int type = buf.getInt(); + if (type == PT_NOTE) { + // p_offset is at phdr + 8, p_filesz at phdr + 32 for 64-bit. + buf.position(phdr + 8); + long noteOffset = buf.getLong(); + buf.position(phdr + 32); + long noteSize = buf.getLong(); + + String buildId = findGnuBuildIdInBuffer(buf, noteOffset, noteSize); + if (buildId != null) { + return buildId; + } + } + } + return null; + } + + private static String readBuildIdFromBuffer32(ByteBuffer buf) { + // e_phoff is at offset 28 in the 32-bit ELF header. + buf.position(28); + long phoff = buf.getInt() & 0xFFFFFFFFL; + + // e_phentsize is at offset 42, e_phnum at offset 44. + buf.position(42); + int phentsize = buf.getShort() & 0xFFFF; + int phnum = buf.getShort() & 0xFFFF; + + for (int i = 0; i < phnum; i++) { + int phdr = (int) (phoff + (long) i * phentsize); + buf.position(phdr); + int type = buf.getInt(); + if (type == PT_NOTE) { + // p_offset is at phdr + 4, p_filesz at phdr + 16 for 32-bit. + buf.position(phdr + 4); + long noteOffset = buf.getInt() & 0xFFFFFFFFL; + buf.position(phdr + 16); + long noteSize = buf.getInt() & 0xFFFFFFFFL; + + String buildId = findGnuBuildIdInBuffer(buf, noteOffset, noteSize); + if (buildId != null) { + return buildId; + } + } + } + return null; + } + + private static String findGnuBuildIdInBuffer(ByteBuffer buf, long offset, long size) { + long end = offset + size; + long pos = offset; + + while (pos + 12 <= end) { + buf.position((int) pos); + int namesz = buf.getInt(); + int descsz = buf.getInt(); + int type = buf.getInt(); + + if (namesz < 0 || descsz < 0 || namesz > 256) { + break; + } + + int nameAligned = align4(namesz); + long descPos = pos + 12 + nameAligned; + + if (namesz > 0 && type == NT_GNU_BUILD_ID && descPos + descsz <= end) { + byte[] nameBytes = new byte[namesz]; + buf.get(nameBytes); + String name = + new String( + nameBytes, 0, Math.max(0, namesz - 1), java.nio.charset.StandardCharsets.US_ASCII); + + if (GNU_NOTE_NAME.equals(name) && descsz > 0) { + buf.position((int) descPos); + byte[] desc = new byte[descsz]; + buf.get(desc); + return bytesToHex(desc); + } + } + + pos = descPos + align4(descsz); + } + return null; + } + + private static String readBuildIdFromRaf(RandomAccessFile raf) throws Exception { + // Verify ELF magic bytes. + byte[] magic = new byte[4]; + raf.readFully(magic); + for (int i = 0; i < 4; i++) { + if (magic[i] != ELF_MAGIC[i]) { + return null; + } + } + + int elfClass = raf.read(); // 1 = 32-bit, 2 = 64-bit + boolean is64 = elfClass == ELFCLASS64; + + int dataEncoding = raf.read(); // 1 = little-endian, 2 = big-endian + ByteOrder order = dataEncoding == 1 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; + + if (is64) { + return readBuildIdFromElf64(raf, order); + } else { + return readBuildIdFromElf32(raf, order); + } + } + + private static String readBuildIdFromElf64(RandomAccessFile raf, ByteOrder order) + throws Exception { + // e_phoff is at offset 32 in the 64-bit ELF header. + raf.seek(32); + long phoff = readLong(raf, order); + + // e_phentsize is at offset 54, e_phnum at offset 56. + raf.seek(54); + int phentsize = readUnsignedShort(raf, order); + int phnum = readUnsignedShort(raf, order); + + for (int i = 0; i < phnum; i++) { + long phdr = phoff + (long) i * phentsize; + raf.seek(phdr); + int type = readInt(raf, order); + if (type == PT_NOTE) { + // p_offset is at phdr + 8, p_filesz at phdr + 32 for 64-bit. + raf.seek(phdr + 8); + long noteOffset = readLong(raf, order); + raf.seek(phdr + 32); + long noteSize = readLong(raf, order); + + String buildId = findGnuBuildId(raf, noteOffset, noteSize, order); + if (buildId != null) { + return buildId; + } + } + } + return null; + } + + private static String readBuildIdFromElf32(RandomAccessFile raf, ByteOrder order) + throws Exception { + // e_phoff is at offset 28 in the 32-bit ELF header. + raf.seek(28); + long phoff = readInt(raf, order) & 0xFFFFFFFFL; + + // e_phentsize is at offset 42, e_phnum at offset 44. + raf.seek(42); + int phentsize = readUnsignedShort(raf, order); + int phnum = readUnsignedShort(raf, order); + + for (int i = 0; i < phnum; i++) { + long phdr = phoff + (long) i * phentsize; + raf.seek(phdr); + int type = readInt(raf, order); + if (type == PT_NOTE) { + // p_offset is at phdr + 4, p_filesz at phdr + 16 for 32-bit. + raf.seek(phdr + 4); + long noteOffset = readInt(raf, order) & 0xFFFFFFFFL; + raf.seek(phdr + 16); + long noteSize = readInt(raf, order) & 0xFFFFFFFFL; + + String buildId = findGnuBuildId(raf, noteOffset, noteSize, order); + if (buildId != null) { + return buildId; + } + } + } + return null; + } + + /** + * Searches a PT_NOTE segment for the GNU build ID note. + * + *

Note format: namesz (4) | descsz (4) | type (4) | name (aligned to 4) | desc (aligned to 4) + */ + private static String findGnuBuildId( + RandomAccessFile raf, long offset, long size, ByteOrder order) throws Exception { + long end = offset + size; + long pos = offset; + + while (pos + 12 <= end) { + raf.seek(pos); + int namesz = readInt(raf, order); + int descsz = readInt(raf, order); + int type = readInt(raf, order); + + if (namesz < 0 || descsz < 0 || namesz > 256) { + break; + } + + int nameAligned = align4(namesz); + long descPos = pos + 12 + nameAligned; + + if (namesz > 0 && type == NT_GNU_BUILD_ID && descPos + descsz <= end) { + byte[] nameBytes = new byte[namesz]; + raf.readFully(nameBytes); + // Name is null-terminated. + String name = + namesz > 0 ? new String(nameBytes, 0, Math.max(0, namesz - 1), "US-ASCII") : ""; + + if (GNU_NOTE_NAME.equals(name) && descsz > 0) { + raf.seek(descPos); + byte[] desc = new byte[descsz]; + raf.readFully(desc); + return bytesToHex(desc); + } + } + + pos = descPos + align4(descsz); + } + return null; + } + + private static int align4(int value) { + return (value + 3) & ~3; + } + + private static int readInt(RandomAccessFile raf, ByteOrder order) throws Exception { + byte[] buf = new byte[4]; + raf.readFully(buf); + return ByteBuffer.wrap(buf).order(order).getInt(); + } + + private static long readLong(RandomAccessFile raf, ByteOrder order) throws Exception { + byte[] buf = new byte[8]; + raf.readFully(buf); + return ByteBuffer.wrap(buf).order(order).getLong(); + } + + private static int readUnsignedShort(RandomAccessFile raf, ByteOrder order) throws Exception { + byte[] buf = new byte[2]; + raf.readFully(buf); + return ByteBuffer.wrap(buf).order(order).getShort() & 0xFFFF; + } + + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + sb.append(String.format("%02x", b & 0xff)); + } + return sb.toString(); + } +} diff --git a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java index 18a55bd9f530..0ad27c015091 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java +++ b/packages/firebase_crashlytics/firebase_crashlytics/android/src/main/java/io/flutter/plugins/firebase/crashlytics/FlutterFirebaseCrashlyticsPlugin.java @@ -40,6 +40,13 @@ public class FlutterFirebaseCrashlyticsPlugin private MethodChannel channel; private EventChannel testEventChannel; private EventChannel.EventSink testEventSink; + private Context applicationContext; + + // Cached ELF build ID read from libapp.so at startup. This is the build ID that the + // firebase-crashlytics-buildtools JAR extracts from .symbols files during upload, so using + // it ensures crash reports match uploaded symbols (even when the Dart VM's internal snapshot + // build ID differs, which happens with AAB + flavor + obfuscation builds). + private String elfBuildId; private static final String FIREBASE_CRASHLYTICS_COLLECTION_ENABLED = "firebase_crashlytics_collection_enabled"; @@ -56,6 +63,8 @@ private void initInstance(BinaryMessenger messenger) { @Override public void onAttachedToEngine(FlutterPluginBinding binding) { + applicationContext = binding.getApplicationContext(); + elfBuildId = ElfBuildIdReader.readBuildId(applicationContext); initInstance(binding.getBinaryMessenger()); } @@ -157,14 +166,18 @@ private Task recordError(final Map arguments) { final String information = (String) Objects.requireNonNull(arguments.get(Constants.INFORMATION)); final boolean fatal = (boolean) Objects.requireNonNull(arguments.get(Constants.FATAL)); - final String buildId = + final String dartBuildId = (String) Objects.requireNonNull(arguments.get(Constants.BUILD_ID)); @SuppressWarnings("unchecked") final List loadingUnits = (List) Objects.requireNonNull(arguments.get(Constants.LOADING_UNITS)); - if (buildId.length() > 0) { - FlutterFirebaseCrashlyticsInternal.setFlutterBuildId(buildId); + // Prefer the ELF build ID from libapp.so over the Dart VM's snapshot build ID. + // The firebase-crashlytics-buildtools JAR uses the ELF build ID when uploading + // symbols, so we must report the same ID for Crashlytics to match them. + String effectiveBuildId = elfBuildId != null ? elfBuildId : dartBuildId; + if (effectiveBuildId.length() > 0) { + FlutterFirebaseCrashlyticsInternal.setFlutterBuildId(effectiveBuildId); } FlutterFirebaseCrashlyticsInternal.setLoadingUnits(loadingUnits); diff --git a/packages/firebase_crashlytics/firebase_crashlytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecrashlyticsexample/MainActivity.kt b/packages/firebase_crashlytics/firebase_crashlytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecrashlyticsexample/MainActivity.kt index 2fb355214b7b..87963034f34b 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecrashlyticsexample/MainActivity.kt +++ b/packages/firebase_crashlytics/firebase_crashlytics/example/android/app/src/main/kotlin/io/flutter/plugins/firebasecrashlyticsexample/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebasecrashlyticsexample import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_crashlytics/firebase_crashlytics/example/pubspec.yaml b/packages/firebase_crashlytics/firebase_crashlytics/example/pubspec.yaml index 7c21ce86575f..80285ca58403 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/example/pubspec.yaml +++ b/packages/firebase_crashlytics/firebase_crashlytics/example/pubspec.yaml @@ -1,14 +1,15 @@ name: firebase_crashlytics_example description: Demonstrates how to use the firebase_crashlytics plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_analytics: ^12.2.0 - firebase_core: ^4.6.0 - firebase_crashlytics: ^5.1.0 + firebase_analytics: ^12.4.3 + firebase_core: ^4.11.0 + firebase_crashlytics: ^5.2.4 flutter: sdk: flutter diff --git a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Package.swift b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Package.swift index 586eeda0b7ff..304276713365 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Package.swift +++ b/packages/firebase_crashlytics/firebase_crashlytics/ios/firebase_crashlytics/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "5.0.8" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "5.2.4" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_crashlytics", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-crashlytics", targets: ["firebase_crashlytics"]), + .library(name: "firebase-crashlytics", targets: ["firebase_crashlytics"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-cls\""), ] - ), + ) ] ) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/lib/firebase_crashlytics.dart b/packages/firebase_crashlytics/firebase_crashlytics/lib/firebase_crashlytics.dart index 835af28b344f..800d683e306b 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/lib/firebase_crashlytics.dart +++ b/packages/firebase_crashlytics/firebase_crashlytics/lib/firebase_crashlytics.dart @@ -5,7 +5,7 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:firebase_crashlytics_platform_interface/firebase_crashlytics_platform_interface.dart'; import 'package:flutter/foundation.dart' show DiagnosticLevel, FlutterError, FlutterErrorDetails, kDebugMode; diff --git a/packages/firebase_crashlytics/firebase_crashlytics/lib/src/firebase_crashlytics.dart b/packages/firebase_crashlytics/firebase_crashlytics/lib/src/firebase_crashlytics.dart index a43208f8563b..e6b07ca2efb0 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/lib/src/firebase_crashlytics.dart +++ b/packages/firebase_crashlytics/firebase_crashlytics/lib/src/firebase_crashlytics.dart @@ -8,7 +8,7 @@ part of '../firebase_crashlytics.dart'; /// The entry point for accessing a [FirebaseCrashlytics]. /// /// You can get an instance by calling [FirebaseCrashlytics.instance]. -class FirebaseCrashlytics extends FirebasePluginPlatform { +class FirebaseCrashlytics extends FirebasePlugin { FirebaseCrashlytics._({required this.app}) : super(app.name, 'plugins.flutter.io/firebase_crashlytics'); diff --git a/packages/firebase_crashlytics/firebase_crashlytics/macos/firebase_crashlytics/Package.swift b/packages/firebase_crashlytics/firebase_crashlytics/macos/firebase_crashlytics/Package.swift index 61ffe48c3b67..f386382b93cf 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/macos/firebase_crashlytics/Package.swift +++ b/packages/firebase_crashlytics/firebase_crashlytics/macos/firebase_crashlytics/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "5.0.8" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "5.2.4" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_crashlytics", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-crashlytics", targets: ["firebase_crashlytics"]), + .library(name: "firebase-crashlytics", targets: ["firebase_crashlytics"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-cls\""), ] - ), + ) ] ) diff --git a/packages/firebase_crashlytics/firebase_crashlytics/pubspec.yaml b/packages/firebase_crashlytics/firebase_crashlytics/pubspec.yaml index 37cee9a65d0e..9c7b33d58dc0 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics/pubspec.yaml +++ b/packages/firebase_crashlytics/firebase_crashlytics/pubspec.yaml @@ -2,7 +2,8 @@ name: firebase_crashlytics description: Flutter plugin for Firebase Crashlytics. It reports uncaught errors to the Firebase console. -version: 5.1.0 +version: 5.2.4 +resolution: workspace homepage: https://firebase.google.com/docs/crashlytics repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_crashlytics/firebase_crashlytics topics: @@ -15,13 +16,13 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_crashlytics_platform_interface: ^3.8.19 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_crashlytics_platform_interface: ^3.8.24 flutter: sdk: flutter stack_trace: ^1.10.0 diff --git a/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/CHANGELOG.md b/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/CHANGELOG.md index 9849ee09191c..518c200d25aa 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/CHANGELOG.md +++ b/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/CHANGELOG.md @@ -1,3 +1,23 @@ +## 3.8.24 + + - Update a dependency to the latest release. + +## 3.8.23 + + - Update a dependency to the latest release. + +## 3.8.22 + + - Update a dependency to the latest release. + +## 3.8.21 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 3.8.20 + + - Update a dependency to the latest release. + ## 3.8.19 - Update a dependency to the latest release. diff --git a/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/pubspec.yaml b/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/pubspec.yaml index 49ab3d585a14..f2a9fbbc723a 100644 --- a/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/pubspec.yaml +++ b/packages/firebase_crashlytics/firebase_crashlytics_platform_interface/pubspec.yaml @@ -1,24 +1,25 @@ name: firebase_crashlytics_platform_interface description: A common platform interface for the firebase_crashlytics plugin. -version: 3.8.19 +version: 3.8.24 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_crashlytics/firebase_crashlytics_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_crashlytics/firebase_crashlytics_platform_interface environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 + _flutterfire_internals: ^1.3.73 collection: ^1.15.0 - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_data_connect/firebase_data_connect/CHANGELOG.md b/packages/firebase_data_connect/firebase_data_connect/CHANGELOG.md index 47ddabb56175..471ecf4d462a 100644 --- a/packages/firebase_data_connect/firebase_data_connect/CHANGELOG.md +++ b/packages/firebase_data_connect/firebase_data_connect/CHANGELOG.md @@ -1,3 +1,31 @@ +## 0.3.0+5 + + - Update a dependency to the latest release. + +## 0.3.0+4 + + - Update a dependency to the latest release. + +## 0.3.0+3 + + - Update a dependency to the latest release. + +## 0.3.0+2 + + - Update a dependency to the latest release. + +## 0.3.0+1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**: update core, auth and app-check logic so internal resources on method channels are properly disposed ([#18268](https://github.com/firebase/flutterfire/issues/18268)). ([a0de4ed8](https://github.com/firebase/flutterfire/commit/a0de4ed86b0dff89bb9e557f2a54f38cd2546016)) + - **FIX**(fdc): block reconnecting if there are no active subscribers or pending unary calls. ([#18265](https://github.com/firebase/flutterfire/issues/18265)). ([330bbb83](https://github.com/firebase/flutterfire/commit/330bbb83399f37911f938a59dc660ed84a0c83a3)) + - **FIX**(fdc): remove unused logs ([#18197](https://github.com/firebase/flutterfire/issues/18197)). ([4c17ca87](https://github.com/firebase/flutterfire/commit/4c17ca870a78ae6afeaad6006ca68e7999711ffd)) + +## 0.3.0 + + - **FEAT**(fdc): Streaming implementation for data connect ([#18174](https://github.com/firebase/flutterfire/issues/18174)). ([6ce6f6b2](https://github.com/firebase/flutterfire/commit/6ce6f6b2369b9d43e69b24b284d8ef816c430e31)) + - **FEAT**(app_check,windows): add support for AppCheck for Windows ([#18140](https://github.com/firebase/flutterfire/issues/18140)). ([81f30325](https://github.com/firebase/flutterfire/commit/81f30325fc926fe94b630e49f56b795c781a4cbe)) + ## 0.2.4 - **FIX**(data_connect): fix UTF 8 characters decoding in data connect ([#18120](https://github.com/firebase/flutterfire/issues/18120)). ([25ec5c42](https://github.com/firebase/flutterfire/commit/25ec5c429863c34f8473daad7f83487a31dcd7a1)) diff --git a/packages/firebase_data_connect/firebase_data_connect/analysis_options.yaml b/packages/firebase_data_connect/firebase_data_connect/analysis_options.yaml new file mode 100644 index 000000000000..f9b303465f19 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/analysis_options.yaml @@ -0,0 +1 @@ +include: package:flutter_lints/flutter.yaml diff --git a/packages/firebase_data_connect/firebase_data_connect/example/.gitignore b/packages/firebase_data_connect/firebase_data_connect/example/.gitignore index fa1ffd5b7c5d..e101d9f0a871 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/.gitignore +++ b/packages/firebase_data_connect/firebase_data_connect/example/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/firebase_data_connect/firebase_data_connect/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/dataconnect/example/MainActivity.kt b/packages/firebase_data_connect/firebase_data_connect/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/dataconnect/example/MainActivity.kt index edca5bed7fed..5f6a78ea469a 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/dataconnect/example/MainActivity.kt +++ b/packages/firebase_data_connect/firebase_data_connect/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/dataconnect/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.dataconnect.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql new file mode 100644 index 000000000000..f3cc340c8b71 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/implicit.gql @@ -0,0 +1,34 @@ +extend type DirectedBy { + """ + ✨ Implicit foreign key field based on `DirectedBy`.`movie`. It must match the value of `Movie`.`id`. See `@ref` for how to customize it. + """ + movieId: UUID! @fdc_generated(from: "DirectedBy.movie", purpose: IMPLICIT_REF_FIELD) + """ + ✨ Implicit foreign key field based on `DirectedBy`.`directedby`. It must match the value of `Person`.`id`. See `@ref` for how to customize it. + """ + directedbyId: UUID! @fdc_generated(from: "DirectedBy.directedby", purpose: IMPLICIT_REF_FIELD) +} +extend type Movie { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "Movie", purpose: IMPLICIT_KEY_FIELD) +} +extend type Person { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "Person", purpose: IMPLICIT_KEY_FIELD) +} +extend type Thing { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "Thing", purpose: IMPLICIT_KEY_FIELD) +} +extend type TimestampHolder { + """ + ✨ Implicit primary key field. It's a UUID column default to a generated new value. See `@table` for how to customize it. + """ + id: UUID! @default(expr: "uuidV4()") @fdc_generated(from: "TimestampHolder", purpose: IMPLICIT_KEY_FIELD) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql new file mode 100644 index 000000000000..bb9b46b78467 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/input.gql @@ -0,0 +1,1049 @@ +""" +✨ `DirectedBy_KeyOutput` returns the primary key fields of table type `DirectedBy`. + +It has the same format as `DirectedBy_Key`, but is only used as mutation return value. +""" +scalar DirectedBy_KeyOutput +""" +✨ `Movie_KeyOutput` returns the primary key fields of table type `Movie`. + +It has the same format as `Movie_Key`, but is only used as mutation return value. +""" +scalar Movie_KeyOutput +""" +✨ `Person_KeyOutput` returns the primary key fields of table type `Person`. + +It has the same format as `Person_Key`, but is only used as mutation return value. +""" +scalar Person_KeyOutput +""" +✨ `Thing_KeyOutput` returns the primary key fields of table type `Thing`. + +It has the same format as `Thing_Key`, but is only used as mutation return value. +""" +scalar Thing_KeyOutput +""" +✨ `TimestampHolder_KeyOutput` returns the primary key fields of table type `TimestampHolder`. + +It has the same format as `TimestampHolder_Key`, but is only used as mutation return value. +""" +scalar TimestampHolder_KeyOutput +""" +✨ Generated data input type for table 'DirectedBy'. It includes all necessary fields for creating or upserting rows into table. +""" +input DirectedBy_Data { + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: UUID + """ + ✨ `_expr` server value variant of `movieId` (✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!`) + """ + movieId_expr: UUID_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: UUID + """ + ✨ `_expr` server value variant of `directedbyId` (✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!`) + """ + directedbyId_expr: UUID_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `DirectedBy`.`directedby` of type `Person!` + """ + directedby: Person_Key @fdc_forbiddenInVariables + """ + ✨ Generated from Field `DirectedBy`.`movie` of type `Movie!` + """ + movie: Movie_Key @fdc_forbiddenInVariables +} +""" +✨ Generated filter input type for table 'DirectedBy'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input DirectedBy_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [DirectedBy_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: DirectedBy_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [DirectedBy_Filter!] + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: UUID_Filter + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: UUID_Filter + """ + ✨ Generated from Field `DirectedBy`.`directedby` of type `Person!` + """ + directedby: Person_Filter + """ + ✨ Generated from Field `DirectedBy`.`movie` of type `Movie!` + """ + movie: Movie_Filter +} +""" +✨ Generated first-row input type for table 'DirectedBy'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input DirectedBy_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [DirectedBy_Order!] + """ + Filters rows based on the specified conditions. + """ + where: DirectedBy_Filter +} +""" +✨ Generated having input type for table 'DirectedBy'. This input allows you to filter groups during aggregate queries using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input DirectedBy_Having { + """ + Apply multiple Having conditions using `AND` logic. + """ + _and: [DirectedBy_Having!] + """ + Whether to apply DISTINCT to the aggregate function. + """ + _distinct: Boolean + """ + Negate the result of the provided Having condition. + """ + _not: DirectedBy_Having + """ + Apply multiple Having conditions using `OR` logic. + """ + _or: [DirectedBy_Having!] + """ + ✨ Generated from Field `DirectedBy`.`_count` of type `Int!` + """ + _count: Int_Filter + """ + ✨ Generated from Field `DirectedBy`.`directedbyId_count` of type `Int!` + """ + directedbyId_count: Int_Filter + """ + ✨ Generated from Field `DirectedBy`.`movieId_count` of type `Int!` + """ + movieId_count: Int_Filter +} +""" +✨ Generated key input type for table 'DirectedBy'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input DirectedBy_Key { + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: UUID + """ + ✨ `_expr` server value variant of `movieId` (✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!`) + """ + movieId_expr: UUID_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: UUID + """ + ✨ `_expr` server value variant of `directedbyId` (✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!`) + """ + directedbyId_expr: UUID_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated list filter input type for table 'DirectedBy'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input DirectedBy_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: DirectedBy_Filter +} +""" +✨ Generated order input type for table 'DirectedBy'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input DirectedBy_Order { + """ + ✨ Generated from Field `DirectedBy`.`movieId` of type `UUID!` + """ + movieId: OrderDirection + """ + ✨ Generated from Field `DirectedBy`.`directedbyId` of type `UUID!` + """ + directedbyId: OrderDirection + """ + ✨ Generated from Field `DirectedBy`.`directedby` of type `Person!` + """ + directedby: Person_Order + """ + ✨ Generated from Field `DirectedBy`.`movie` of type `Movie!` + """ + movie: Movie_Order + """ + ✨ Generated from Field `DirectedBy`.`_count` of type `Int!` + """ + _count: OrderDirection + """ + ✨ Generated from Field `DirectedBy`.`directedbyId_count` of type `Int!` + """ + directedbyId_count: OrderDirection + """ + ✨ Generated from Field `DirectedBy`.`movieId_count` of type `Int!` + """ + movieId_count: OrderDirection +} +""" +✨ Generated data input type for table 'Movie'. It includes all necessary fields for creating or upserting rows into table. +""" +input Movie_Data { + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Movie`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `Movie`.`description` of type `String` + """ + description: String + """ + ✨ `_expr` server value variant of `description` (✨ Generated from Field `Movie`.`description` of type `String`) + """ + description_expr: String_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `Movie`.`genre` of type `String!` + """ + genre: String + """ + ✨ `_expr` server value variant of `genre` (✨ Generated from Field `Movie`.`genre` of type `String!`) + """ + genre_expr: String_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `Movie`.`rating` of type `Float` + """ + rating: Float + """ + ✨ `_expr` server value variant of `rating` (✨ Generated from Field `Movie`.`rating` of type `Float`) + """ + rating_expr: Float_Expr @fdc_forbiddenInVariables + """ + ✨ `_update` server value variant of `rating` (✨ Generated from Field `Movie`.`rating` of type `Float`) + """ + rating_update: [Float_Update!] @fdc_forbiddenInVariables + """ + ✨ Generated from Field `Movie`.`releaseYear` of type `Int` + """ + releaseYear: Int + """ + ✨ `_expr` server value variant of `releaseYear` (✨ Generated from Field `Movie`.`releaseYear` of type `Int`) + """ + releaseYear_expr: Int_Expr @fdc_forbiddenInVariables + """ + ✨ `_update` server value variant of `releaseYear` (✨ Generated from Field `Movie`.`releaseYear` of type `Int`) + """ + releaseYear_update: [Int_Update!] @fdc_forbiddenInVariables + """ + ✨ Generated from Field `Movie`.`title` of type `String!` + """ + title: String + """ + ✨ `_expr` server value variant of `title` (✨ Generated from Field `Movie`.`title` of type `String!`) + """ + title_expr: String_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated filter input type for table 'Movie'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Movie_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [Movie_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: Movie_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [Movie_Filter!] + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `Movie`.`description` of type `String` + """ + description: String_Filter + """ + ✨ Generated from Field `Movie`.`genre` of type `String!` + """ + genre: String_Filter + """ + ✨ Generated from Field `Movie`.`rating` of type `Float` + """ + rating: Float_Filter + """ + ✨ Generated from Field `Movie`.`releaseYear` of type `Int` + """ + releaseYear: Int_Filter + """ + ✨ Generated from Field `Movie`.`title` of type `String!` + """ + title: String_Filter + """ + ✨ Generated from Field `Movie`.`directedBies_on_movie` of type `[DirectedBy!]!` + """ + directedBies_on_movie: DirectedBy_ListFilter + """ + ✨ Generated from Field `Movie`.`people_via_DirectedBy` of type `[Person!]!` + """ + people_via_DirectedBy: Person_ListFilter +} +""" +✨ Generated first-row input type for table 'Movie'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input Movie_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [Movie_Order!] + """ + Filters rows based on the specified conditions. + """ + where: Movie_Filter +} +""" +✨ Generated having input type for table 'Movie'. This input allows you to filter groups during aggregate queries using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Movie_Having { + """ + Apply multiple Having conditions using `AND` logic. + """ + _and: [Movie_Having!] + """ + Whether to apply DISTINCT to the aggregate function. + """ + _distinct: Boolean + """ + Negate the result of the provided Having condition. + """ + _not: Movie_Having + """ + Apply multiple Having conditions using `OR` logic. + """ + _or: [Movie_Having!] + """ + ✨ Generated from Field `Movie`.`_count` of type `Int!` + """ + _count: Int_Filter + """ + ✨ Generated from Field `Movie`.`description_count` of type `Int!` + """ + description_count: Int_Filter + """ + ✨ Generated from Field `Movie`.`genre_count` of type `Int!` + """ + genre_count: Int_Filter + """ + ✨ Generated from Field `Movie`.`id_count` of type `Int!` + """ + id_count: Int_Filter + """ + ✨ Generated from Field `Movie`.`rating_count` of type `Int!` + """ + rating_count: Int_Filter + """ + ✨ Generated from Field `Movie`.`releaseYear_count` of type `Int!` + """ + releaseYear_count: Int_Filter + """ + ✨ Generated from Field `Movie`.`title_count` of type `Int!` + """ + title_count: Int_Filter + """ + ✨ Generated from Field `Movie`.`rating_sum` of type `Float` + """ + rating_sum: Float_Filter + """ + ✨ Generated from Field `Movie`.`releaseYear_sum` of type `Int` + """ + releaseYear_sum: Int_Filter + """ + ✨ Generated from Field `Movie`.`rating_avg` of type `Float` + """ + rating_avg: Float_Filter + """ + ✨ Generated from Field `Movie`.`releaseYear_avg` of type `Float` + """ + releaseYear_avg: Float_Filter + """ + ✨ Generated from Field `Movie`.`rating_min` of type `Float` + """ + rating_min: Float_Filter + """ + ✨ Generated from Field `Movie`.`releaseYear_min` of type `Int` + """ + releaseYear_min: Int_Filter + """ + ✨ Generated from Field `Movie`.`rating_max` of type `Float` + """ + rating_max: Float_Filter + """ + ✨ Generated from Field `Movie`.`releaseYear_max` of type `Int` + """ + releaseYear_max: Int_Filter +} +""" +✨ Generated key input type for table 'Movie'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input Movie_Key { + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Movie`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated list filter input type for table 'Movie'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input Movie_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: Movie_Filter +} +""" +✨ Generated order input type for table 'Movie'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input Movie_Order { + """ + ✨ Generated from Field `Movie`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `Movie`.`description` of type `String` + """ + description: OrderDirection + """ + ✨ Generated from Field `Movie`.`genre` of type `String!` + """ + genre: OrderDirection + """ + ✨ Generated from Field `Movie`.`rating` of type `Float` + """ + rating: OrderDirection + """ + ✨ Generated from Field `Movie`.`releaseYear` of type `Int` + """ + releaseYear: OrderDirection + """ + ✨ Generated from Field `Movie`.`title` of type `String!` + """ + title: OrderDirection + """ + ✨ Generated from Field `Movie`.`_count` of type `Int!` + """ + _count: OrderDirection + """ + ✨ Generated from Field `Movie`.`description_count` of type `Int!` + """ + description_count: OrderDirection + """ + ✨ Generated from Field `Movie`.`genre_count` of type `Int!` + """ + genre_count: OrderDirection + """ + ✨ Generated from Field `Movie`.`id_count` of type `Int!` + """ + id_count: OrderDirection + """ + ✨ Generated from Field `Movie`.`rating_count` of type `Int!` + """ + rating_count: OrderDirection + """ + ✨ Generated from Field `Movie`.`releaseYear_count` of type `Int!` + """ + releaseYear_count: OrderDirection + """ + ✨ Generated from Field `Movie`.`title_count` of type `Int!` + """ + title_count: OrderDirection + """ + ✨ Generated from Field `Movie`.`rating_sum` of type `Float` + """ + rating_sum: OrderDirection + """ + ✨ Generated from Field `Movie`.`releaseYear_sum` of type `Int` + """ + releaseYear_sum: OrderDirection + """ + ✨ Generated from Field `Movie`.`rating_avg` of type `Float` + """ + rating_avg: OrderDirection + """ + ✨ Generated from Field `Movie`.`releaseYear_avg` of type `Float` + """ + releaseYear_avg: OrderDirection + """ + ✨ Generated from Field `Movie`.`rating_min` of type `Float` + """ + rating_min: OrderDirection + """ + ✨ Generated from Field `Movie`.`releaseYear_min` of type `Int` + """ + releaseYear_min: OrderDirection + """ + ✨ Generated from Field `Movie`.`rating_max` of type `Float` + """ + rating_max: OrderDirection + """ + ✨ Generated from Field `Movie`.`releaseYear_max` of type `Int` + """ + releaseYear_max: OrderDirection +} +""" +✨ Generated data input type for table 'Person'. It includes all necessary fields for creating or upserting rows into table. +""" +input Person_Data { + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Person`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `Person`.`name` of type `String!` + """ + name: String + """ + ✨ `_expr` server value variant of `name` (✨ Generated from Field `Person`.`name` of type `String!`) + """ + name_expr: String_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated filter input type for table 'Person'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Person_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [Person_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: Person_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [Person_Filter!] + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `Person`.`name` of type `String!` + """ + name: String_Filter + """ + ✨ Generated from Field `Person`.`directedBies_on_directedby` of type `[DirectedBy!]!` + """ + directedBies_on_directedby: DirectedBy_ListFilter + """ + ✨ Generated from Field `Person`.`movies_via_DirectedBy` of type `[Movie!]!` + """ + movies_via_DirectedBy: Movie_ListFilter +} +""" +✨ Generated first-row input type for table 'Person'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input Person_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [Person_Order!] + """ + Filters rows based on the specified conditions. + """ + where: Person_Filter +} +""" +✨ Generated having input type for table 'Person'. This input allows you to filter groups during aggregate queries using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Person_Having { + """ + Apply multiple Having conditions using `AND` logic. + """ + _and: [Person_Having!] + """ + Whether to apply DISTINCT to the aggregate function. + """ + _distinct: Boolean + """ + Negate the result of the provided Having condition. + """ + _not: Person_Having + """ + Apply multiple Having conditions using `OR` logic. + """ + _or: [Person_Having!] + """ + ✨ Generated from Field `Person`.`_count` of type `Int!` + """ + _count: Int_Filter + """ + ✨ Generated from Field `Person`.`id_count` of type `Int!` + """ + id_count: Int_Filter + """ + ✨ Generated from Field `Person`.`name_count` of type `Int!` + """ + name_count: Int_Filter +} +""" +✨ Generated key input type for table 'Person'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input Person_Key { + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Person`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated list filter input type for table 'Person'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input Person_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: Person_Filter +} +""" +✨ Generated order input type for table 'Person'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input Person_Order { + """ + ✨ Generated from Field `Person`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `Person`.`name` of type `String!` + """ + name: OrderDirection + """ + ✨ Generated from Field `Person`.`_count` of type `Int!` + """ + _count: OrderDirection + """ + ✨ Generated from Field `Person`.`id_count` of type `Int!` + """ + id_count: OrderDirection + """ + ✨ Generated from Field `Person`.`name_count` of type `Int!` + """ + name_count: OrderDirection +} +""" +✨ Generated data input type for table 'Thing'. It includes all necessary fields for creating or upserting rows into table. +""" +input Thing_Data { + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Thing`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `Thing`.`title` of type `Any!` + """ + title: Any + """ + ✨ `_expr` server value variant of `title` (✨ Generated from Field `Thing`.`title` of type `Any!`) + """ + title_expr: Any_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated filter input type for table 'Thing'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Thing_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [Thing_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: Thing_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [Thing_Filter!] + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `Thing`.`title` of type `Any!` + """ + title: Any_Filter +} +""" +✨ Generated first-row input type for table 'Thing'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input Thing_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [Thing_Order!] + """ + Filters rows based on the specified conditions. + """ + where: Thing_Filter +} +""" +✨ Generated having input type for table 'Thing'. This input allows you to filter groups during aggregate queries using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input Thing_Having { + """ + Apply multiple Having conditions using `AND` logic. + """ + _and: [Thing_Having!] + """ + Whether to apply DISTINCT to the aggregate function. + """ + _distinct: Boolean + """ + Negate the result of the provided Having condition. + """ + _not: Thing_Having + """ + Apply multiple Having conditions using `OR` logic. + """ + _or: [Thing_Having!] + """ + ✨ Generated from Field `Thing`.`_count` of type `Int!` + """ + _count: Int_Filter + """ + ✨ Generated from Field `Thing`.`id_count` of type `Int!` + """ + id_count: Int_Filter + """ + ✨ Generated from Field `Thing`.`title_count` of type `Int!` + """ + title_count: Int_Filter + """ + ✨ Generated from Field `Thing`.`title_min` of type `Any` + """ + title_min: Any_Filter + """ + ✨ Generated from Field `Thing`.`title_max` of type `Any` + """ + title_max: Any_Filter +} +""" +✨ Generated key input type for table 'Thing'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input Thing_Key { + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `Thing`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated list filter input type for table 'Thing'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input Thing_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: Thing_Filter +} +""" +✨ Generated order input type for table 'Thing'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input Thing_Order { + """ + ✨ Generated from Field `Thing`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `Thing`.`title` of type `Any!` + """ + title: OrderDirection + """ + ✨ Generated from Field `Thing`.`_count` of type `Int!` + """ + _count: OrderDirection + """ + ✨ Generated from Field `Thing`.`id_count` of type `Int!` + """ + id_count: OrderDirection + """ + ✨ Generated from Field `Thing`.`title_count` of type `Int!` + """ + title_count: OrderDirection + """ + ✨ Generated from Field `Thing`.`title_min` of type `Any` + """ + title_min: OrderDirection + """ + ✨ Generated from Field `Thing`.`title_max` of type `Any` + """ + title_max: OrderDirection +} +""" +✨ Generated data input type for table 'TimestampHolder'. It includes all necessary fields for creating or upserting rows into table. +""" +input TimestampHolder_Data { + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `TimestampHolder`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables + """ + ✨ Generated from Field `TimestampHolder`.`date` of type `Date` + """ + date: Date + """ + ✨ `_date` server value variant of `date` (✨ Generated from Field `TimestampHolder`.`date` of type `Date`) + """ + date_date: Date_Relative @fdc_forbiddenInVariables + """ + ✨ `_expr` server value variant of `date` (✨ Generated from Field `TimestampHolder`.`date` of type `Date`) + """ + date_expr: Date_Expr @fdc_forbiddenInVariables + """ + ✨ `_update` server value variant of `date` (✨ Generated from Field `TimestampHolder`.`date` of type `Date`) + """ + date_update: [Date_Update!] @fdc_forbiddenInVariables + """ + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` + """ + timestamp: Timestamp + """ + ✨ `_expr` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) + """ + timestamp_expr: Timestamp_Expr @fdc_forbiddenInVariables + """ + ✨ `_time` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) + """ + timestamp_time: Timestamp_Relative @fdc_forbiddenInVariables + """ + ✨ `_update` server value variant of `timestamp` (✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!`) + """ + timestamp_update: [Timestamp_Update!] @fdc_forbiddenInVariables +} +""" +✨ Generated filter input type for table 'TimestampHolder'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input TimestampHolder_Filter { + """ + Apply multiple filter conditions using `AND` logic. + """ + _and: [TimestampHolder_Filter!] + """ + Negate the result of the provided filter condition. + """ + _not: TimestampHolder_Filter + """ + Apply multiple filter conditions using `OR` logic. + """ + _or: [TimestampHolder_Filter!] + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: UUID_Filter + """ + ✨ Generated from Field `TimestampHolder`.`date` of type `Date` + """ + date: Date_Filter + """ + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` + """ + timestamp: Timestamp_Filter +} +""" +✨ Generated first-row input type for table 'TimestampHolder'. This input selects the first row matching the filter criteria, ordered according to the specified conditions. +""" +input TimestampHolder_FirstRow { + """ + Order the result by the specified fields. + """ + orderBy: [TimestampHolder_Order!] + """ + Filters rows based on the specified conditions. + """ + where: TimestampHolder_Filter +} +""" +✨ Generated having input type for table 'TimestampHolder'. This input allows you to filter groups during aggregate queries using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. +""" +input TimestampHolder_Having { + """ + Apply multiple Having conditions using `AND` logic. + """ + _and: [TimestampHolder_Having!] + """ + Whether to apply DISTINCT to the aggregate function. + """ + _distinct: Boolean + """ + Negate the result of the provided Having condition. + """ + _not: TimestampHolder_Having + """ + Apply multiple Having conditions using `OR` logic. + """ + _or: [TimestampHolder_Having!] + """ + ✨ Generated from Field `TimestampHolder`.`_count` of type `Int!` + """ + _count: Int_Filter + """ + ✨ Generated from Field `TimestampHolder`.`date_count` of type `Int!` + """ + date_count: Int_Filter + """ + ✨ Generated from Field `TimestampHolder`.`id_count` of type `Int!` + """ + id_count: Int_Filter + """ + ✨ Generated from Field `TimestampHolder`.`timestamp_count` of type `Int!` + """ + timestamp_count: Int_Filter + """ + ✨ Generated from Field `TimestampHolder`.`date_min` of type `Date` + """ + date_min: Date_Filter + """ + ✨ Generated from Field `TimestampHolder`.`timestamp_min` of type `Timestamp` + """ + timestamp_min: Timestamp_Filter + """ + ✨ Generated from Field `TimestampHolder`.`date_max` of type `Date` + """ + date_max: Date_Filter + """ + ✨ Generated from Field `TimestampHolder`.`timestamp_max` of type `Timestamp` + """ + timestamp_max: Timestamp_Filter +} +""" +✨ Generated key input type for table 'TimestampHolder'. It represents the primary key fields used to uniquely identify a row in the table. +""" +input TimestampHolder_Key { + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: UUID + """ + ✨ `_expr` server value variant of `id` (✨ Generated from Field `TimestampHolder`.`id` of type `UUID!`) + """ + id_expr: UUID_Expr @fdc_forbiddenInVariables +} +""" +✨ Generated list filter input type for table 'TimestampHolder'. This input applies filtering logic based on the count or existence of related objects that matches certain criteria. +""" +input TimestampHolder_ListFilter { + """ + The desired number of objects that match the condition (defaults to at least one). + """ + count: Int_Filter = {gt:0} + """ + Condition of the related objects to filter for. + """ + exist: TimestampHolder_Filter +} +""" +✨ Generated order input type for table 'TimestampHolder'. This input defines the sorting order of rows in query results based on one or more fields. +""" +input TimestampHolder_Order { + """ + ✨ Generated from Field `TimestampHolder`.`id` of type `UUID!` + """ + id: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`date` of type `Date` + """ + date: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`timestamp` of type `Timestamp!` + """ + timestamp: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`_count` of type `Int!` + """ + _count: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`date_count` of type `Int!` + """ + date_count: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`id_count` of type `Int!` + """ + id_count: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`timestamp_count` of type `Int!` + """ + timestamp_count: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`date_min` of type `Date` + """ + date_min: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`timestamp_min` of type `Timestamp` + """ + timestamp_min: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`date_max` of type `Date` + """ + date_max: OrderDirection + """ + ✨ Generated from Field `TimestampHolder`.`timestamp_max` of type `Timestamp` + """ + timestamp_max: OrderDirection +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql new file mode 100644 index 000000000000..419cc0062903 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/mutation.gql @@ -0,0 +1,552 @@ +extend type Mutation { + """ + ✨ Insert a single `DirectedBy` into the table and return its key. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + directedBy_insert( + """ + Data object to insert into the table. + """ + data: DirectedBy_Data! + ): DirectedBy_KeyOutput! @fdc_generated(from: "DirectedBy", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `Movie` into the table and return its key. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + movie_insert( + """ + Data object to insert into the table. + """ + data: Movie_Data! + ): Movie_KeyOutput! @fdc_generated(from: "Movie", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `Person` into the table and return its key. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + person_insert( + """ + Data object to insert into the table. + """ + data: Person_Data! + ): Person_KeyOutput! @fdc_generated(from: "Person", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `Thing` into the table and return its key. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + thing_insert( + """ + Data object to insert into the table. + """ + data: Thing_Data! + ): Thing_KeyOutput! @fdc_generated(from: "Thing", purpose: INSERT_SINGLE) + """ + ✨ Insert a single `TimestampHolder` into the table and return its key. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + timestampHolder_insert( + """ + Data object to insert into the table. + """ + data: TimestampHolder_Data! + ): TimestampHolder_KeyOutput! @fdc_generated(from: "TimestampHolder", purpose: INSERT_SINGLE) + """ + ✨ Insert `DirectedBy` objects into the table and return their keys. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + directedBy_insertMany( + """ + List of data objects to insert into the table. + """ + data: [DirectedBy_Data!]! + ): [DirectedBy_KeyOutput!]! @fdc_generated(from: "DirectedBy", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `Movie` objects into the table and return their keys. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + movie_insertMany( + """ + List of data objects to insert into the table. + """ + data: [Movie_Data!]! + ): [Movie_KeyOutput!]! @fdc_generated(from: "Movie", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `Person` objects into the table and return their keys. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + person_insertMany( + """ + List of data objects to insert into the table. + """ + data: [Person_Data!]! + ): [Person_KeyOutput!]! @fdc_generated(from: "Person", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `Thing` objects into the table and return their keys. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + thing_insertMany( + """ + List of data objects to insert into the table. + """ + data: [Thing_Data!]! + ): [Thing_KeyOutput!]! @fdc_generated(from: "Thing", purpose: INSERT_MULTIPLE) + """ + ✨ Insert `TimestampHolder` objects into the table and return their keys. Columns not specified in `data` will receive defaults (e.g. `null`). + """ + timestampHolder_insertMany( + """ + List of data objects to insert into the table. + """ + data: [TimestampHolder_Data!]! + ): [TimestampHolder_KeyOutput!]! @fdc_generated(from: "TimestampHolder", purpose: INSERT_MULTIPLE) + """ + ✨ Insert or update a single `DirectedBy` into the table, based on the primary key. Returns the key of the newly inserted or existing updated `DirectedBy`. + """ + directedBy_upsert( + """ + Data object to insert or update if it already exists. + """ + data: DirectedBy_Data! + ): DirectedBy_KeyOutput! @fdc_generated(from: "DirectedBy", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `Movie` into the table, based on the primary key. Returns the key of the newly inserted or existing updated `Movie`. + """ + movie_upsert( + """ + Data object to insert or update if it already exists. + """ + data: Movie_Data! + ): Movie_KeyOutput! @fdc_generated(from: "Movie", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `Person` into the table, based on the primary key. Returns the key of the newly inserted or existing updated `Person`. + """ + person_upsert( + """ + Data object to insert or update if it already exists. + """ + data: Person_Data! + ): Person_KeyOutput! @fdc_generated(from: "Person", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `Thing` into the table, based on the primary key. Returns the key of the newly inserted or existing updated `Thing`. + """ + thing_upsert( + """ + Data object to insert or update if it already exists. + """ + data: Thing_Data! + ): Thing_KeyOutput! @fdc_generated(from: "Thing", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update a single `TimestampHolder` into the table, based on the primary key. Returns the key of the newly inserted or existing updated `TimestampHolder`. + """ + timestampHolder_upsert( + """ + Data object to insert or update if it already exists. + """ + data: TimestampHolder_Data! + ): TimestampHolder_KeyOutput! @fdc_generated(from: "TimestampHolder", purpose: UPSERT_SINGLE) + """ + ✨ Insert or update `DirectedBy` objects into the table, based on the primary key. Returns the key of the newly inserted or existing updated `DirectedBy`. + """ + directedBy_upsertMany( + """ + List of data objects to insert or update if it already exists. + """ + data: [DirectedBy_Data!]! + ): [DirectedBy_KeyOutput!]! @fdc_generated(from: "DirectedBy", purpose: UPSERT_MULTIPLE) + """ + ✨ Insert or update `Movie` objects into the table, based on the primary key. Returns the key of the newly inserted or existing updated `Movie`. + """ + movie_upsertMany( + """ + List of data objects to insert or update if it already exists. + """ + data: [Movie_Data!]! + ): [Movie_KeyOutput!]! @fdc_generated(from: "Movie", purpose: UPSERT_MULTIPLE) + """ + ✨ Insert or update `Person` objects into the table, based on the primary key. Returns the key of the newly inserted or existing updated `Person`. + """ + person_upsertMany( + """ + List of data objects to insert or update if it already exists. + """ + data: [Person_Data!]! + ): [Person_KeyOutput!]! @fdc_generated(from: "Person", purpose: UPSERT_MULTIPLE) + """ + ✨ Insert or update `Thing` objects into the table, based on the primary key. Returns the key of the newly inserted or existing updated `Thing`. + """ + thing_upsertMany( + """ + List of data objects to insert or update if it already exists. + """ + data: [Thing_Data!]! + ): [Thing_KeyOutput!]! @fdc_generated(from: "Thing", purpose: UPSERT_MULTIPLE) + """ + ✨ Insert or update `TimestampHolder` objects into the table, based on the primary key. Returns the key of the newly inserted or existing updated `TimestampHolder`. + """ + timestampHolder_upsertMany( + """ + List of data objects to insert or update if it already exists. + """ + data: [TimestampHolder_Data!]! + ): [TimestampHolder_KeyOutput!]! @fdc_generated(from: "TimestampHolder", purpose: UPSERT_MULTIPLE) + """ + ✨ Update a single `DirectedBy` based on `id`, `key` or `first`, setting columns specified in `data`. Returns the key of the updated `DirectedBy` or `null` if not found. + """ + directedBy_update( + """ + The key used to identify the object. + """ + key: DirectedBy_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: DirectedBy_FirstRow + + """ + Data object containing fields to be updated. + """ + data: DirectedBy_Data! + ): DirectedBy_KeyOutput @fdc_generated(from: "DirectedBy", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `Movie` based on `id`, `key` or `first`, setting columns specified in `data`. Returns the key of the updated `Movie` or `null` if not found. + """ + movie_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Movie_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Movie_FirstRow + + """ + Data object containing fields to be updated. + """ + data: Movie_Data! + ): Movie_KeyOutput @fdc_generated(from: "Movie", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `Person` based on `id`, `key` or `first`, setting columns specified in `data`. Returns the key of the updated `Person` or `null` if not found. + """ + person_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Person_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Person_FirstRow + + """ + Data object containing fields to be updated. + """ + data: Person_Data! + ): Person_KeyOutput @fdc_generated(from: "Person", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `Thing` based on `id`, `key` or `first`, setting columns specified in `data`. Returns the key of the updated `Thing` or `null` if not found. + """ + thing_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Thing_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Thing_FirstRow + + """ + Data object containing fields to be updated. + """ + data: Thing_Data! + ): Thing_KeyOutput @fdc_generated(from: "Thing", purpose: UPDATE_SINGLE) + """ + ✨ Update a single `TimestampHolder` based on `id`, `key` or `first`, setting columns specified in `data`. Returns the key of the updated `TimestampHolder` or `null` if not found. + """ + timestampHolder_update( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: TimestampHolder_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: TimestampHolder_FirstRow + + """ + Data object containing fields to be updated. + """ + data: TimestampHolder_Data! + ): TimestampHolder_KeyOutput @fdc_generated(from: "TimestampHolder", purpose: UPDATE_SINGLE) + """ + ✨ Update `DirectedBy` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + directedBy_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: DirectedBy_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: DirectedBy_Data! + ): Int! @fdc_generated(from: "DirectedBy", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `Movie` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + movie_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: Movie_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: Movie_Data! + ): Int! @fdc_generated(from: "Movie", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `Person` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + person_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: Person_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: Person_Data! + ): Int! @fdc_generated(from: "Person", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `Thing` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + thing_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: Thing_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: Thing_Data! + ): Int! @fdc_generated(from: "Thing", purpose: UPDATE_MULTIPLE) + """ + ✨ Update `TimestampHolder` objects matching `where` conditions (or `all`, if true) according to `data`. Returns the number of rows updated. + """ + timestampHolder_updateMany( + """ + Filter condition to specify which rows to update. + """ + where: TimestampHolder_Filter + + """ + Set to true to update all rows. + """ + all: Boolean = false + + """ + Data object containing fields to update. + """ + data: TimestampHolder_Data! + ): Int! @fdc_generated(from: "TimestampHolder", purpose: UPDATE_MULTIPLE) + """ + ✨ Delete a single `DirectedBy` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + directedBy_delete( + """ + The key used to identify the object. + """ + key: DirectedBy_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: DirectedBy_FirstRow + ): DirectedBy_KeyOutput @fdc_generated(from: "DirectedBy", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `Movie` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + movie_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Movie_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Movie_FirstRow + ): Movie_KeyOutput @fdc_generated(from: "Movie", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `Person` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + person_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Person_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Person_FirstRow + ): Person_KeyOutput @fdc_generated(from: "Person", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `Thing` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + thing_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Thing_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Thing_FirstRow + ): Thing_KeyOutput @fdc_generated(from: "Thing", purpose: DELETE_SINGLE) + """ + ✨ Delete a single `TimestampHolder` based on `id`, `key` or `first` and return its key (or `null` if not found). + """ + timestampHolder_delete( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: TimestampHolder_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: TimestampHolder_FirstRow + ): TimestampHolder_KeyOutput @fdc_generated(from: "TimestampHolder", purpose: DELETE_SINGLE) + """ + ✨ Delete `DirectedBy` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + directedBy_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: DirectedBy_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "DirectedBy", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `Movie` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + movie_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: Movie_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "Movie", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `Person` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + person_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: Person_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "Person", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `Thing` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + thing_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: Thing_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "Thing", purpose: DELETE_MULTIPLE) + """ + ✨ Delete `TimestampHolder` objects matching `where` conditions (or `all`, if true). Returns the number of rows deleted. + """ + timestampHolder_deleteMany( + """ + Filter condition to specify which rows to delete. + """ + where: TimestampHolder_Filter + + """ + Set to true to delete all rows. + """ + all: Boolean = false + ): Int! @fdc_generated(from: "TimestampHolder", purpose: DELETE_MULTIPLE) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql new file mode 100644 index 000000000000..f7fd3c460647 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/query.gql @@ -0,0 +1,262 @@ +extend type Query { + """ + ✨ Look up a single `DirectedBy` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + directedBy( + """ + The key used to identify the object. + """ + key: DirectedBy_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: DirectedBy_FirstRow + ): DirectedBy @fdc_generated(from: "DirectedBy", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `Movie` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + movie( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Movie_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Movie_FirstRow + ): Movie @fdc_generated(from: "Movie", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `Person` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + person( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Person_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Person_FirstRow + ): Person @fdc_generated(from: "Person", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `Thing` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + thing( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: Thing_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: Thing_FirstRow + ): Thing @fdc_generated(from: "Thing", purpose: QUERY_SINGLE) + """ + ✨ Look up a single `TimestampHolder` based on `id`, `key` or `first` and return selected fields (or `null` if not found). + """ + timestampHolder( + """ + The unique ID of the object. + """ + id: UUID + + """ + The key used to identify the object. + """ + key: TimestampHolder_Key + + """ + Fetch the first row based on the filters and ordering. + """ + first: TimestampHolder_FirstRow + ): TimestampHolder @fdc_generated(from: "TimestampHolder", purpose: QUERY_SINGLE) + """ + ✨ List `DirectedBy` objects in the table and return selected fields, optionally filtered by `where` conditions + """ + directedBies( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: DirectedBy_Having + ): [DirectedBy!]! @fdc_generated(from: "DirectedBy", purpose: QUERY_MULTIPLE) + """ + ✨ List `Movie` objects in the table and return selected fields, optionally filtered by `where` conditions + """ + movies( + """ + Filter condition to narrow down the query results. + """ + where: Movie_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [Movie_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: Movie_Having + ): [Movie!]! @fdc_generated(from: "Movie", purpose: QUERY_MULTIPLE) + """ + ✨ List `Person` objects in the table and return selected fields, optionally filtered by `where` conditions + """ + people( + """ + Filter condition to narrow down the query results. + """ + where: Person_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [Person_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: Person_Having + ): [Person!]! @fdc_generated(from: "Person", purpose: QUERY_MULTIPLE) + """ + ✨ List `Thing` objects in the table and return selected fields, optionally filtered by `where` conditions + """ + things( + """ + Filter condition to narrow down the query results. + """ + where: Thing_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [Thing_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: Thing_Having + ): [Thing!]! @fdc_generated(from: "Thing", purpose: QUERY_MULTIPLE) + """ + ✨ List `TimestampHolder` objects in the table and return selected fields, optionally filtered by `where` conditions + """ + timestampHolders( + """ + Filter condition to narrow down the query results. + """ + where: TimestampHolder_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [TimestampHolder_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: TimestampHolder_Having + ): [TimestampHolder!]! @fdc_generated(from: "TimestampHolder", purpose: QUERY_MULTIPLE) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/relation.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/relation.gql new file mode 100644 index 000000000000..97e64bd39091 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/main/relation.gql @@ -0,0 +1,417 @@ +extend type DirectedBy { + """ + Implicit metadata field that cannot be written. It provides extra information about query results. + """ + _metadata: _Metadata @fdc_generated(from: "DirectedBy", purpose: METADATA_FIELD) + """ + ✨ Count the number of rows in the `DirectedBy` table. + """ + _count: Int! @fdc_generated(from: "DirectedBy.", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `DirectedBy` table where the `directedbyId` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + directedbyId_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "DirectedBy.directedbyId", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `DirectedBy` table where the `movieId` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + movieId_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "DirectedBy.movieId", purpose: QUERY_COUNT) + """ + A generated field that is used for caching results in SDKs. + """ + _id: ID! @fdc_generated(from: "DirectedBy.movieId,directedbyId", purpose: ENTITY_ID) +} +extend type Movie { + """ + ✨ List `DirectedBy` objects in a one-to-many relationship (where `DirectedBy`.`movie` is this object). + """ + directedBies_on_movie( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: DirectedBy_Having + ): [DirectedBy!]! @fdc_generated(from: "DirectedBy.movie", purpose: QUERY_MULTIPLE_ONE_TO_MANY) + """ + ✨ List `Person` objects using `DirectedBy` as the join table (a `DirectedBy` object exists where its `movie` is this and its `directedby` is that). + """ + people_via_DirectedBy( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: DirectedBy_Having + ): [Person!]! @fdc_generated(from: "DirectedBy", purpose: QUERY_MULTIPLE_MANY_TO_MANY) + """ + Implicit metadata field that cannot be written. It provides extra information about query results. + """ + _metadata: _Metadata @fdc_generated(from: "Movie", purpose: METADATA_FIELD) + """ + ✨ Count the number of rows in the `Movie` table. + """ + _count: Int! @fdc_generated(from: "Movie.", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Movie` table where the `description` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + description_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Movie.description", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Movie` table where the `genre` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + genre_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Movie.genre", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Movie` table where the `id` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + id_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Movie.id", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Movie` table where the `rating` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + rating_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Movie.rating", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Movie` table where the `releaseYear` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + releaseYear_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Movie.releaseYear", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Movie` table where the `title` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + title_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Movie.title", purpose: QUERY_COUNT) + """ + ✨ Sum the `rating` field in the `Movie` table. + """ + rating_sum( + """ + Set to true to sum the distinct values. + """ + distinct: Boolean = false + ): Float @fdc_generated(from: "Movie.rating", purpose: QUERY_SUM) + """ + ✨ Sum the `releaseYear` field in the `Movie` table. + """ + releaseYear_sum( + """ + Set to true to sum the distinct values. + """ + distinct: Boolean = false + ): Int @fdc_generated(from: "Movie.releaseYear", purpose: QUERY_SUM) + """ + ✨ Average the `rating` field in the `Movie` table. + """ + rating_avg( + """ + Set to true to average the distinct values. + """ + distinct: Boolean = false + ): Float @fdc_generated(from: "Movie.rating", purpose: QUERY_AVG) + """ + ✨ Average the `releaseYear` field in the `Movie` table. + """ + releaseYear_avg( + """ + Set to true to average the distinct values. + """ + distinct: Boolean = false + ): Float @fdc_generated(from: "Movie.releaseYear", purpose: QUERY_AVG) + """ + ✨ Minimum of the `rating` field in the `Movie` table. + """ + rating_min: Float @fdc_generated(from: "Movie.rating", purpose: QUERY_MIN) + """ + ✨ Minimum of the `releaseYear` field in the `Movie` table. + """ + releaseYear_min: Int @fdc_generated(from: "Movie.releaseYear", purpose: QUERY_MIN) + """ + ✨ Maximum of the `rating` field in the `Movie` table. + """ + rating_max: Float @fdc_generated(from: "Movie.rating", purpose: QUERY_MAX) + """ + ✨ Maximum of the `releaseYear` field in the `Movie` table. + """ + releaseYear_max: Int @fdc_generated(from: "Movie.releaseYear", purpose: QUERY_MAX) + """ + A generated field that is used for caching results in SDKs. + """ + _id: ID! @fdc_generated(from: "Movie.id", purpose: ENTITY_ID) +} +extend type Person { + """ + ✨ List `DirectedBy` objects in a one-to-many relationship (where `DirectedBy`.`directedby` is this object). + """ + directedBies_on_directedby( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: DirectedBy_Having + ): [DirectedBy!]! @fdc_generated(from: "DirectedBy.directedby", purpose: QUERY_MULTIPLE_ONE_TO_MANY) + """ + ✨ List `Movie` objects using `DirectedBy` as the join table (a `DirectedBy` object exists where its `directedby` is this and its `movie` is that). + """ + movies_via_DirectedBy( + """ + Filter condition to narrow down the query results. + """ + where: DirectedBy_Filter + + """ + Order the query results by specific fields. + """ + orderBy: [DirectedBy_Order!] + + """ + Number of rows to skip before starting to return the results. + """ + offset: Int + + """ + Maximum number of rows to return (defaults to 100 rows). + """ + limit: Int = 100 + + """ + Set to true to return distinct results. + """ + distinct: Boolean = false + + """ + Filter condition to apply to the groups of aggregate queries. + """ + having: DirectedBy_Having + ): [Movie!]! @fdc_generated(from: "DirectedBy", purpose: QUERY_MULTIPLE_MANY_TO_MANY) + """ + Implicit metadata field that cannot be written. It provides extra information about query results. + """ + _metadata: _Metadata @fdc_generated(from: "Person", purpose: METADATA_FIELD) + """ + ✨ Count the number of rows in the `Person` table. + """ + _count: Int! @fdc_generated(from: "Person.", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Person` table where the `id` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + id_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Person.id", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Person` table where the `name` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + name_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Person.name", purpose: QUERY_COUNT) + """ + A generated field that is used for caching results in SDKs. + """ + _id: ID! @fdc_generated(from: "Person.id", purpose: ENTITY_ID) +} +extend type Thing { + """ + Implicit metadata field that cannot be written. It provides extra information about query results. + """ + _metadata: _Metadata @fdc_generated(from: "Thing", purpose: METADATA_FIELD) + """ + ✨ Count the number of rows in the `Thing` table. + """ + _count: Int! @fdc_generated(from: "Thing.", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Thing` table where the `id` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + id_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Thing.id", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `Thing` table where the `title` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + title_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "Thing.title", purpose: QUERY_COUNT) + """ + ✨ Minimum of the `title` field in the `Thing` table. + """ + title_min: Any @fdc_generated(from: "Thing.title", purpose: QUERY_MIN) + """ + ✨ Maximum of the `title` field in the `Thing` table. + """ + title_max: Any @fdc_generated(from: "Thing.title", purpose: QUERY_MAX) + """ + A generated field that is used for caching results in SDKs. + """ + _id: ID! @fdc_generated(from: "Thing.id", purpose: ENTITY_ID) +} +extend type TimestampHolder { + """ + Implicit metadata field that cannot be written. It provides extra information about query results. + """ + _metadata: _Metadata @fdc_generated(from: "TimestampHolder", purpose: METADATA_FIELD) + """ + ✨ Count the number of rows in the `TimestampHolder` table. + """ + _count: Int! @fdc_generated(from: "TimestampHolder.", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `TimestampHolder` table where the `date` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + date_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "TimestampHolder.date", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `TimestampHolder` table where the `id` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + id_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "TimestampHolder.id", purpose: QUERY_COUNT) + """ + ✨ Count the number of rows in the `TimestampHolder` table where the `timestamp` field is non-null. Pass the `distinct` argument to instead count the number of distinct values. + """ + timestamp_count( + """ + Set to true to count the number of distinct values. + """ + distinct: Boolean = false + ): Int! @fdc_generated(from: "TimestampHolder.timestamp", purpose: QUERY_COUNT) + """ + ✨ Minimum of the `date` field in the `TimestampHolder` table. + """ + date_min: Date @fdc_generated(from: "TimestampHolder.date", purpose: QUERY_MIN) + """ + ✨ Minimum of the `timestamp` field in the `TimestampHolder` table. + """ + timestamp_min: Timestamp @fdc_generated(from: "TimestampHolder.timestamp", purpose: QUERY_MIN) + """ + ✨ Maximum of the `date` field in the `TimestampHolder` table. + """ + date_max: Date @fdc_generated(from: "TimestampHolder.date", purpose: QUERY_MAX) + """ + ✨ Maximum of the `timestamp` field in the `TimestampHolder` table. + """ + timestamp_max: Timestamp @fdc_generated(from: "TimestampHolder.timestamp", purpose: QUERY_MAX) + """ + A generated field that is used for caching results in SDKs. + """ + _id: ID! @fdc_generated(from: "TimestampHolder.id", purpose: ENTITY_ID) +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/prelude.gql b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/prelude.gql new file mode 100644 index 000000000000..ebd062ea55f7 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/dataconnect/.dataconnect/schema/prelude.gql @@ -0,0 +1,2419 @@ +"AccessLevel specifies coarse access policies for common situations." +enum AccessLevel @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + """ + This operation is accessible to anyone, with or without authentication. + Equivalent to: `@auth(expr: "true")` + """ + PUBLIC + + """ + This operation can be executed only with a valid Firebase Auth ID token. + **Note:** This access level allows anonymous and unverified accounts, + which may present security and abuse risks. + Equivalent to: `@auth(expr: "auth.uid != nil")` + """ + USER_ANON + + """ + This operation is restricted to non-anonymous Firebase Auth accounts. + Equivalent to: `@auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")` + """ + USER + + """ + This operation is restricted to Firebase Auth accounts with verified email addresses. + Equivalent to: `@auth(expr: "auth.uid != nil && auth.token.email_verified")` + """ + USER_EMAIL_VERIFIED + + """ + This operation cannot be executed by anyone. The operation can only be performed + by using the Admin SDK from a privileged environment. + Equivalent to: `@auth(expr: "false")` + """ + NO_ACCESS +} + +""" +The `@auth` directive defines the authentication policy for a query or mutation. + +It must be added to any operation that you wish to be accessible from a client +application. If not specified, the operation defaults to `@auth(level: NO_ACCESS)`. + +Refer to [Data Connect Auth Guide](https://firebase.google.com/docs/data-connect/authorization-and-security) for the best practices. +""" +directive @auth( + """ + The minimal level of access required to perform this operation. + Exactly one of `level` and `expr` should be specified. + """ + level: AccessLevel @fdc_oneOf(required: true) + """ + A CEL expression that grants access to this operation if the expression + evaluates to `true`. + Exactly one of `level` and `expr` should be specified. + """ + expr: Boolean_Expr @fdc_oneOf(required: true) + """ + If the `@auth` on this operation is considered insecure, then developer + acknowledgement is required to deploy this operation, for new operations. + `@auth` is considered insecure if `level: PUBLIC`, or if + `level: USER/USER_ANON/USER_EMAIL_VERIFIED` and `auth.uid` is not referenced + in the operation. + If `insecureReason` is set, no further developer acknowledgement is needed. + """ + insecureReason: String +) on QUERY | MUTATION + +""" +Require that this mutation always run in a DB transaction. + +Mutations with `@transaction` are guaranteed to either fully succeed or fully +fail. Upon the first error in a transaction (either an execution error or failed +`@check`), the transaction will be rolled back. In the GraphQL response, all +fields within the transaction will be `null`, each with an error raised. + +- Fields that have been already evaluated will be nullified due to the rollback + and a "(rolled back)" error will be reported on each of them. +- The execution error or failed `@check` will be reported on the current field. +- Subsequent fields will not be executed. An `(aborted)` error will be reported + on each subsequent field. + +Mutations without `@transaction` would execute each root field one after +another in sequence. They surface any errors as partial +[field errors](https://spec.graphql.org/October2021/#sec-Errors.Field-errors), +but does not impact the execution of subsequent fields. However, failed +`@check`s still terminate the entire operation. + +The `@transaction` directive cannot be added to queries for now. +Currently, queries cannot fail partially, the response data is not guaranteed +to be a consistent snapshot. +""" +directive @transaction on MUTATION + +""" +Redact a part of the response from the client. + +Redacted fields are still evaluated for side effects (including data changes and +`@check`) and the results are still available to later steps in CEL expressions +(via `response.fieldName`). +""" +directive @redact on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +""" +Ensure this field is present and is not null or `[]`, or abort the request / transaction. + +A CEL expression, `expr` is used to test the field value. It defaults to +rejecting null and `[]` but a custom expression can be provided instead. + +If the field occurs multiple times (i.e. directly or indirectly nested under a +list), `expr` will be executed once for each occurrence and `@check` succeeds if +all values succeed. `@check` fails when the field is not present at all (i.e. +all ancestor paths contain `null` or `[]`), unless `optional` is true. + +If a `@check` fails in a mutation, the top-level field containing it will be +replaced with a partial error, whose message can be customzied via the `message` +argument. Each subsequent top-level fields will return an aborted error (i.e. +not executed). To rollback previous steps, see `@transaction`. +""" +directive @check( + """ + The CEL expression to test the field value (or values if nested under a list). + + Within the CEL expression, a special value `this` evaluates to the field that + this directive is attached to. If this field occurs multiple times because + any ancestor is a list, each occurrence is tested with `this` bound to each + value. When the field itself is a list or object, `this` follows the same + structure (including all descendants selected in case of objects). + + For any given path, if an ancestor is `null` or `[]`, the field will not be + reached and the CEL evaluation will be skipped for that path. In other words, + evaluation only takes place when `this` is `null` or non-null, but never + undefined. (See also the `optional` argument.) + """ + expr: Boolean_Expr! = "!(this in [null, []])" + """ + The error message to return to the client if the check fails. + + Defaults to "permission denied" if not specified. + """ + message: String! = "permission denied" + """ + Whether the check should pass or fail (default) when the field is not present. + + A field will not be reached at a given path if its parent or any ancestor is + `[]` or `null`. When this happens to all paths, the field will not be present + anywhere in the response tree. In other words, `expr` is evaluated 0 times. + By default, @check will automatically fail in this case. Set this argument to + `true` to make it pass even if no tests are run (a.k.a. "vacuously true"). + """ + optional: Boolean = false +) repeatable on QUERY | MUTATION | FIELD | FRAGMENT_DEFINITION | FRAGMENT_SPREAD | INLINE_FRAGMENT + +""" +Marks an element of a GraphQL operation as no longer supported for client use. +The Firebase Data Connect backend will continue supporting this element, +but it will no longer be visible in the generated SDKs. +""" +directive @retired( + "Provides the reason for retirement." + reason: String +) on QUERY | MUTATION | FIELD | VARIABLE_DEFINITION + +"Query filter criteria for `String` scalar fields." +input String_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: String @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. Currently only `auth.uid` is supported as an expression. + """ + eq_expr: String_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: String @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. Currently only `auth.uid` is supported as an expression. + """ + ne_expr: String_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [String!] + "Match if field value is not among the provided list of values." + nin: [String!] + "Match if field value is greater than the provided value." + gt: String + "Match if field value is greater than or equal to the provided value." + ge: String + "Match if field value is less than the provided value." + lt: String + "Match if field value is less than or equal to the provided value." + le: String + """ + Match if field value contains the provided value as a substring. Equivalent + to `LIKE '%value%'` + """ + contains: String + """ + Match if field value starts with the provided value. Equivalent to + `LIKE 'value%'` + """ + startsWith: String + """ + Match if field value ends with the provided value. Equivalent to + `LIKE '%value'` + """ + endsWith: String + """ + Match based on the provided pattern. + """ + pattern: String_Pattern +} + +input String_Pattern { + """ + Match using LIKE semantics (https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-LIKE) + """ + like: String @fdc_oneOf + """ + Match against a POSIX regular expression. + """ + regex: String @fdc_oneOf + """ + If true, match patterns case-insensitively. + """ + ignoreCase: Boolean +} + +"Query filter criteris for `[String!]` scalar fields." +input String_ListFilter { + "Match if list field contains the provided value as a member." + includes: String + "Match if list field does not contain the provided value as a member." + excludes: String + "Match if list field contains all of the provided values as members." + includesAll: [String!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [String!] +} + +"Query filter criteria for `UUID` scalar fields." +input UUID_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: UUID @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. + """ + eq_expr: UUID_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: UUID @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. + """ + ne_expr: UUID_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [UUID!] + "Match if field value is not among the provided list of values." + nin: [UUID!] +} + +"Query filter criteris for `[UUID!]` scalar fields." +input UUID_ListFilter { + "Match if list field contains the provided value as a member." + includes: UUID + "Match if list field does not contain the provided value as a member." + excludes: UUID + "Match if list field contains all of the provided values as members." + includesAll: [UUID!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [UUID!] +} + +"Query filter criteria for `Int` scalar fields." +input Int_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Int @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. + """ + eq_expr: Int_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: Int @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. + """ + ne_expr: Int_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [Int!] + "Match if field value is not among the provided list of values." + nin: [Int!] + "Match if field value is greater than the provided value." + gt: Int @fdc_oneOf(group: "gt") + """ + Match if field value is greater than the result of the provided server value + expression. + """ + gt_expr: Int_Expr @fdc_oneOf(group: "gt") + "Match if field value is greater than or equal to the provided value." + ge: Int @fdc_oneOf(group: "ge") + """ + Match if field value is greater than or equal to the result of the provided + server value expression. + """ + ge_expr: Int_Expr @fdc_oneOf(group: "ge") + "Match if field value is less than the provided value." + lt: Int @fdc_oneOf(group: "lt") + """ + Match if field value is less than the result of the provided server value + expression. + """ + lt_expr: Int_Expr @fdc_oneOf(group: "lt") + "Match if field value is less than or equal to the provided value." + le: Int @fdc_oneOf(group: "le") + """ + Match if field value is less than or equal to the result of the provided + server value expression. + """ + le_expr: Int_Expr @fdc_oneOf(group: "le") +} + +"Query filter criteris for `[Int!]` scalar fields." +input Int_ListFilter { + "Match if list field contains the provided value as a member." + includes: Int + "Match if list field does not contain the provided value as a member." + excludes: Int + "Match if list field contains all of the provided values as members." + includesAll: [Int!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Int!] +} + +"Query filter criteria for `Int64` scalar fields." +input Int64_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Int64 @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. + """ + eq_expr: Int64_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: Int64 @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. + """ + ne_expr: Int64_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [Int64!] + "Match if field value is not among the provided list of values." + nin: [Int64!] + "Match if field value is greater than the provided value." + gt: Int64 @fdc_oneOf(group: "gt") + """ + Match if field value is greater than the result of the provided server value + expression. + """ + gt_expr: Int64_Expr @fdc_oneOf(group: "gt") + "Match if field value is greater than or equal to the provided value." + ge: Int64 @fdc_oneOf(group: "ge") + """ + Match if field value is greater than or equal to the result of the provided + server value expression. + """ + ge_expr: Int64_Expr @fdc_oneOf(group: "ge") + "Match if field value is less than the provided value." + lt: Int64 @fdc_oneOf(group: "lt") + """ + Match if field value is less than the result of the provided server value + expression. + """ + lt_expr: Int64_Expr @fdc_oneOf(group: "lt") + "Match if field value is less than or equal to the provided value." + le: Int64 @fdc_oneOf(group: "le") + """ + Match if field value is less than or equal to the result of the provided + server value expression. + """ + le_expr: Int64_Expr @fdc_oneOf(group: "le") +} + +"Query filter criteria for `[Int64!]` scalar fields." +input Int64_ListFilter { + "Match if list field contains the provided value as a member." + includes: Int64 + "Match if list field does not contain the provided value as a member." + excludes: Int64 + "Match if list field contains all of the provided values as members." + includesAll: [Int64!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Int64!] +} + +"Query filter criteria for `Float` scalar fields." +input Float_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Float @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. + """ + eq_expr: Float_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: Float @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. + """ + ne_expr: Float_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [Float!] + "Match if field value is not among the provided list of values." + nin: [Float!] + "Match if field value is greater than the provided value." + gt: Float @fdc_oneOf(group: "gt") + """ + Match if field value is greater than the result of the provided server value + expression. + """ + gt_expr: Float_Expr @fdc_oneOf(group: "gt") + "Match if field value is greater than or equal to the provided value." + ge: Float @fdc_oneOf(group: "ge") + """ + Match if field value is greater than or equal to the result of the provided + server value expression. + """ + ge_expr: Float_Expr @fdc_oneOf(group: "ge") + "Match if field value is less than the provided value." + lt: Float @fdc_oneOf(group: "lt") + """ + Match if field value is less than the result of the provided server value + expression. + """ + lt_expr: Float_Expr @fdc_oneOf(group: "lt") + "Match if field value is less than or equal to the provided value." + le: Float @fdc_oneOf(group: "le") + """ + Match if field value is less than or equal to the result of the provided + server value expression. + """ + le_expr: Float_Expr @fdc_oneOf(group: "le") +} + +"Query filter criteria for `[Float!]` scalar fields." +input Float_ListFilter { + "Match if list field contains the provided value as a member." + includes: Float + "Match if list field does not contain the provided value as a member." + excludes: Float + "Match if list field contains all of the provided values as members." + includesAll: [Float!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Float!] +} + +"Query filter criteria for `Boolean` scalar fields." +input Boolean_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Boolean @fdc_oneOf(group: "eq") + "Match if field is equal to the result of the provided expression." + eq_expr: Boolean_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: Boolean @fdc_oneOf(group: "ne") + """ + Match if field does not match the result of the provided expression. + """ + ne_expr: Boolean_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [Boolean!] + "Match if field value is not among the provided list of values." + nin: [Boolean!] +} + +"Query filter criteria for `[Boolean!]` scalar fields." +input Boolean_ListFilter { + "Match if list field contains the provided value as a member." + includes: Boolean + "Match if list field does not contain the provided value as a member." + excludes: Boolean + "Match if list field contains all of the provided values as members." + includesAll: [Boolean!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Boolean!] +} + +"Query filter criteria for `Any` scalar fields." +input Any_Filter { + "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." + isNull: Boolean + "Match if field is exactly equal to provided value." + eq: Any @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. + """ + eq_expr: Any_Expr @fdc_oneOf(group: "eq") + "Match if field is not equal to provided value." + ne: Any @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. + """ + ne_expr: Any_Expr @fdc_oneOf(group: "ne") + "Match if field value is among the provided list of values." + in: [Any!] + "Match if field value is not among the provided list of values." + nin: [Any!] +} + +"Query filter criteria for `[Any!]` scalar fields." +input Any_ListFilter { + "Match if list field contains the provided value as a member." + includes: Any + "Match if list field does not contain the provided value as a member." + excludes: Any + "Match if list field contains all of the provided values as members." + includesAll: [Any!] + "Match if list field does not contain any of the provided values as members." + excludesAll: [Any!] +} + +"Conditions on a `Date` value." +input Date_Filter { + "Match if the field `IS NULL`." + isNull: Boolean + "Match if the field is exactly equal to the provided value." + eq: Date @fdc_oneOf(group: "eq") + "Match if the field equals the provided CEL expression." + eq_expr: Date_Expr @fdc_oneOf(group: "eq") + "Match if the field equals the provided relative date." + eq_date: Date_Relative @fdc_oneOf(group: "eq") + "Match if the field is not equal to the provided value." + ne: Date @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided CEL expression." + ne_expr: Date_Expr @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided relative date." + ne_date: Date_Relative @fdc_oneOf(group: "ne") + "Match if the field value is among the provided list of values." + in: [Date!] + "Match if the field value is not among the provided list of values." + nin: [Date!] + "Match if the field value is greater than the provided value." + gt: Date @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided CEL expression." + gt_expr: Date_Expr @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided relative date." + gt_date: Date_Relative @fdc_oneOf(group: "gt") + "Match if the field value is greater than or equal to the provided value." + ge: Date @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided CEL expression." + ge_expr: Date_Expr @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided relative date." + ge_date: Date_Relative @fdc_oneOf(group: "ge") + "Match if the field value is less than the provided value." + lt: Date @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided CEL expression." + lt_expr: Date_Expr @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided relative date." + lt_date: Date_Relative @fdc_oneOf(group: "lt") + "Match if the field value is less than or equal to the provided value." + le: Date @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided CEL expression." + le_expr: Date_Expr @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided relative date." + le_date: Date_Relative @fdc_oneOf(group: "le") +} + +"Conditions on a`Date` list." +input Date_ListFilter { + "Match if the list contains the provided date." + includes: Date @fdc_oneOf(group: "includes") + "Match if the list contains the provided date CEL expression." + includes_expr: Date_Expr @fdc_oneOf(group: "includes") + "Match if the list contains the provided relative date." + includes_date: Date_Relative @fdc_oneOf(group: "includes") + "Match if the list does not contain the provided date." + excludes: Date @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided date CEL expression." + excludes_expr: Date_Expr @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided relative date." + excludes_date: Date_Relative @fdc_oneOf(group: "excludes") + "Match if the list contains all the provided dates." + includesAll: [Date!] + "Match if the list contains none of the provided dates." + excludesAll: [Date!] +} + +"Conditions on a `Timestamp` value." +input Timestamp_Filter { + "Match if the field `IS NULL`." + isNull: Boolean + "Match if the field is exactly equal to the provided value." + eq: Timestamp @fdc_oneOf(group: "eq") + "Match if the field equals the provided CEL expression." + eq_expr: Timestamp_Expr @fdc_oneOf(group: "eq") + "Match if the field equals the provided relative time." + eq_time: Timestamp_Relative @fdc_oneOf(group: "eq") + "Match if the field is not equal to the provided value." + ne: Timestamp @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided CEL expression." + ne_expr: Timestamp_Expr @fdc_oneOf(group: "ne") + "Match if the field is not equal to the provided relative time." + ne_time: Timestamp_Relative @fdc_oneOf(group: "ne") + "Match if the field value is among the provided list of values." + in: [Timestamp!] + "Match if the field value is not among the provided list of values." + nin: [Timestamp!] + "Match if the field value is greater than the provided value." + gt: Timestamp @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided CEL expression." + gt_expr: Timestamp_Expr @fdc_oneOf(group: "gt") + "Match if the field value is greater than the provided relative time." + gt_time: Timestamp_Relative @fdc_oneOf(group: "gt") + "Match if the field value is greater than or equal to the provided value." + ge: Timestamp @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided CEL expression." + ge_expr: Timestamp_Expr @fdc_oneOf(group: "ge") + "Match if the field value is greater than or equal to the provided relative time." + ge_time: Timestamp_Relative @fdc_oneOf(group: "ge") + "Match if the field value is less than the provided value." + lt: Timestamp @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided CEL expression." + lt_expr: Timestamp_Expr @fdc_oneOf(group: "lt") + "Match if the field value is less than the provided relative time." + lt_time: Timestamp_Relative @fdc_oneOf(group: "lt") + "Match if the field value is less than or equal to the provided value." + le: Timestamp @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided CEL expression." + le_expr: Timestamp_Expr @fdc_oneOf(group: "le") + "Match if the field value is less than or equal to the provided relative time." + le_time: Timestamp_Relative @fdc_oneOf(group: "le") +} + +"Conditions on a `Timestamp` list." +input Timestamp_ListFilter { + "Match if the list contains the provided timestamp." + includes: Timestamp @fdc_oneOf(group: "includes") + "Match if the list contains the provided timestamp CEL expression." + includes_expr: Timestamp_Expr @fdc_oneOf(group: "includes") + "Match if the list contains the provided relative timestamp." + includes_time: Timestamp_Relative @fdc_oneOf(group: "includes") + "Match if the list does not contain the provided timestamp." + excludes: Timestamp @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided timestamp CEL expression." + excludes_expr: Timestamp_Expr @fdc_oneOf(group: "excludes") + "Match if the list does not contain the provided relative timestamp." + excludes_time: Timestamp_Relative @fdc_oneOf(group: "excludes") + "Match if the list contains all the provided timestamps." + includesAll: [Timestamp!] + "Match if the list contains none of the provided timestamps." + excludesAll: [Timestamp!] +} + +""" +Put on a `String` field to include it in the full-text search index. + +###### Example + +```graphql +type Post @table { + title: String @searchable + body: String @searchable +} +``` + +```graphql +query SearchPosts($query: String!) @auth(level: PUBLIC) { + posts_search(query: $query) { + id title body + } +} +``` + +""" +directive @searchable( + """ + Language of the string column to be indexed for full-text search. + (e.g. "french", "spanish", etc.) + Defaults to "english" if not specified. + """ + language: String = "english") on FIELD_DEFINITION + +extend type _Metadata { + """ + Only set for entities returned from a full text search. + The `ts_rank` relevance score of the row compared to the search query. + + You can use it to tune `relevanceThreshold`. + """ + relevance: Float +} + + +enum Search_QueryFormat @fdc_forbiddenAsFieldType { + """ + Allows search engine style semantics (e.g. quoted strings, AND and OR). + """ + QUERY, + """ + Splits the query into words and does ANDs between them. + """ + PLAIN, + """ + Matches an exact phrase. Requires the words to be in the same order (i.e. "brown + dog" will not match "brown and red dog"). + """ + PHRASE, + """ + Create complex queries using the full set of tsquery operators. + """ + ADVANCED, +} + +""" +(Internal) A string that uniquely identifies a type, field, and so on. + +The most common usage in FDC is `SomeType` or `SomeType.someField`. See the +linked page in the @specifiedBy directive for the GraphQL RFC with more details. +""" +scalar SchemaCoordinate + @specifiedBy(url: "https://github.com/graphql/graphql-wg/blob/6d02705dea034fb65ebc6799632adb7bd550d0aa/rfcs/SchemaCoordinates.md") + @fdc_forbiddenAsFieldType + @fdc_forbiddenAsVariableType + +"(Internal) The purpose of a generated type or field." +enum GeneratedPurpose @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + # Implicit fields added to the table types as columns. + IMPLICIT_KEY_FIELD + IMPLICIT_REF_FIELD + + # Generated static fields extended to table types. + METADATA_FIELD + + # Relational non-column fields extended to table types. + QUERY_MULTIPLE_ONE_TO_MANY + QUERY_MULTIPLE_MANY_TO_MANY + + # Generated fields for aggregates + QUERY_COUNT + QUERY_SUM + QUERY_AVG + QUERY_MIN + QUERY_MAX + + # Generated field for full text search + QUERY_MULTIPLE_BY_FULL_TEXT_SEARCH + + # Top-level Query fields. + QUERY_SINGLE + QUERY_MULTIPLE + QUERY_MULTIPLE_BY_SIMILARITY + + # Top-level Mutation fields. + INSERT_SINGLE + INSERT_MULTIPLE + UPSERT_SINGLE + UPSERT_MULTIPLE + UPDATE_SINGLE + UPDATE_MULTIPLE + DELETE_SINGLE + DELETE_MULTIPLE +} + +"(Internal) Added to definitions generated by FDC." +directive @fdc_generated( + "The source type or field that causes this definition to be generated." + from: SchemaCoordinate + "The reason why this definition is generated, such as the intended use case." + purpose: GeneratedPurpose! +) on + | SCALAR + | OBJECT + | FIELD_DEFINITION + | ARGUMENT_DEFINITION + | INTERFACE + | UNION + | ENUM + | ENUM_VALUE + | INPUT_OBJECT + | INPUT_FIELD_DEFINITION + +type _Service { + "Full Service Definition Language of the Frebase Data Connect Schema, including normalized schema, predefined and generated types." + sdl( + """ + Whether or not to omit Data Connect builtin GraphQL preludes. + They are static GraphQL publically available in the docsite. + """ + omitBuiltin: Boolean = false + """ + Whether or not to omit GQL description in the SDL. + We generate description to document generated schema. + It may bloat the size of SDL. + """ + omitDescription: Boolean = false + ): String! + "All GraphQL Schema Sources in the service." + schema: String! + "GraphQL Schema Sources in the service for each schema_id." + schemas: [_Schema!]! + "Generated documentation from the schema of the Firebase Data Connect Service." + docs: [_Doc!]! +} + +type _Schema { + """ + The schema id of the schema. + The `main` schema can define SQL `@table` and `@view` based by Cloud SQL PostgreSQL. + Other schemas are secondary schemas backed by GraphQL services in Cloud Run. + """ + id: String! + "The GraphQL Schema in this particular schema." + source: String! +} + +type _Doc { + "Name of the Doc Page." + page: String! + "The markdown content of the doc page." + markdown: String! +} + +"(Internal) Added to scalars representing quoted CEL expressions." +directive @fdc_celExpression( + "The expected CEL type that the expression should evaluate to." + returnType: String +) on SCALAR + +"(Internal) Added to scalars representing quoted SQL expressions." +directive @fdc_sqlExpression( + "The expected SQL type that the expression should evaluate to." + dataType: String +) on SCALAR + +"(Internal) Added to types that may not be used as variables." +directive @fdc_forbiddenAsVariableType on SCALAR | OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT + +"(Internal) Added to input field definitions that may not be present when used as variables." +directive @fdc_forbiddenInVariables on INPUT_FIELD_DEFINITION + +"(Internal) Added to types that may not be used as fields in schema." +directive @fdc_forbiddenAsFieldType on SCALAR | OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT + +"Provides a frequently used example for this type / field / argument." +directive @fdc_example( + "A GraphQL literal value (verbatim) whose type matches the target." + value: Any + "A human-readable text description of what `value` means in this context." + description: String +) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | INPUT_OBJECT | INPUT_FIELD_DEFINITION + +"(Internal) Marks this field / argument as conflicting with others in the same group." +directive @fdc_oneOf( + "The group name where fields / arguments conflict with each other." + group: String! = "" + "If true, exactly one field / argument in the group must be specified." + required: Boolean! = false +) repeatable on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION + +""" +The `_Metadata` type is used to return metadata about a field in a response. +""" +type _Metadata { + # During vector similarity search, the distance between the query vector and + # this row's vector. In other cases, this field is not set. + distance: Float +} + +""" +**SQL_Query**: A scalar representing a PostgreSQL SQL Data Query Language (DQL) statement. + +To guard against SQL injection, the SQL statement must be a **string literal** embedded directly within the GraphQL operation. It **cannot** be provided as a GraphQL variable. + +**Constraints:** + +* Only Data Query Language (DQL) statements (e.g., `SELECT`, `TABLE`) are permitted. +* Data Definition Language (DDL) statements (e.g., `CREATE`, `ALTER`, `DROP`) are **not** allowed. +* Data Manipulation Language (DML) statements (e.g., `INSERT`, `UPDATE`, `DELETE`) are **not** allowed. +""" +scalar SQL_Query + @fdc_sqlExpression + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "SELECT id, content FROM items WHERE id = $1", description: "Select items by ID.") + @fdc_example(value: "TABLE items", description: "Select all from items.") + +""" +**SQL_Mutation**: A scalar representing a PostgreSQL SQL Data Manipulation Language (DML) statement. + +To guard against SQL injection, the SQL statement must be a **string literal** embedded directly within the GraphQL operation. It **cannot** be provided as a GraphQL variable. + +**Constraints:** + +* Only Data Manipulation Language (DML) statements (e.g., `INSERT`, `UPDATE`, `DELETE`) are permitted. +* Data Definition Language (DDL) statements (e.g., `CREATE`, `ALTER`, `DROP`) are **not** allowed. +""" +scalar SQL_Mutation + @fdc_sqlExpression + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "UPDATE my_table SET status = $1 WHERE id = $2", description: "Update status by ID.") + @fdc_example(value: "INSERT INTO new_table (col1, col2) VALUES ($1, $2) RETURNING *", description: "Insert and return all columns.") + +""" +A list of values to bind to the `$1`, `$2`, etc. placeholders in the SQL statement, in order. +""" +scalar SQL_Params + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: ["user123", "active"], description: "List of values for $1, $2, etc.") + +extend type Query { + """ + Executes a SQL statement expected to return zero or more rows. + Intended for read-only operations (e.g., SELECT, TABLE). + Deploy-time checks will warn against DML statements in this field. + + **Usage:** + + ```graphql + query GetRestaurantStats { + _select( + sql: "SELECT id, column from my_table WHERE user_id = $1 AND status = $2", + params: [{_expr: "auth.uid"}, "active"] + ) + } + ``` + + **Returns:** A JSON array of objects. The structure is derived from the `SELECT` statement's columns. + """ + _select( + """ + The SQL DQL statement to execute. This must be a **string literal** defined directly in the GraphQL operation. Use `$1`, `$2`, etc. as placeholders for values provided in the `params` argument. + """ + sql: SQL_Query!, + """ + A list of values to bind to the `$1`, `$2`, etc. placeholders in the `sql`, in order. Values can also be expressions using `_expr` (e.g., `{_expr: "auth.uid"}`) to inject dynamic values. + """ + params: SQL_Params + ): [Any] + + """ + Executes a SQL statement expected to return exactly one row. + Returns null if no row is returned. + Intended for read-only operations. + Deploy-time checks will warn against DML statements in this field. + + **Usage:** + + ```graphql + query MyCustomSelect { + _selectFirst( + sql: "SELECT id, column from my_table WHERE user_id = $1 LIMIT 1", + params: [{_expr: "auth.uid"}] + ) + } + ``` + + **Returns:** A single JSON object or `null`. The structure is derived from the `SELECT` statement's columns. + """ + _selectFirst( + """ + The SQL DQL statement to execute. This must be a **string literal** defined directly in the GraphQL operation. Use `$1`, `$2`, etc. as placeholders for values provided in the `params` argument. + """ + sql: SQL_Query!, + """ + A list of values to bind to the `$1`, `$2`, etc. placeholders in the `sql`, in order. Values can also be expressions using `_expr` (e.g., `{_expr: "auth.uid"}`) to inject dynamic values. + """ + params: SQL_Params + ): Any +} + +extend type Mutation { + """ + Executes a SQL statement, potentially with side effects, expected to return zero or more rows. + Useful for DML statements with a RETURNING clause (e.g., INSERT/UPDATE/DELETE ... RETURNING *). + + **Usage:** + + ```graphql + mutation MyCustomInsert { + _executeReturning( + sql: "INSERT INTO new_table (col1, col2) VALUES ($1, $2) RETURNING *", + params: ["value1", 456] + ) + } + ``` + + **Returns:** A JSON array of objects, where each object represents a row from the `RETURNING` clause. + """ + _executeReturning( + """ + The SQL statement to execute. This must be a **string literal** defined directly in the GraphQL operation. Use `$1`, `$2`, etc. as placeholders for values provided in the `params` argument. + """ + sql: SQL_Mutation!, + """ + A list of values to bind to the `$1`, `$2`, etc. placeholders in the `sql`, in order. Values can also be expressions using `_expr` (e.g., `{_expr: "auth.uid"}`) to inject dynamic values. + """ + params: SQL_Params + ): [Any] + + """ + Executes a SQL statement, potentially with side effects, expected to return exactly one row. + Useful for DML statements on a single row with a RETURNING clause. + Returns null if no row is returned. + + **Usage:** + + ```graphql + mutation MyCustomUpdate { + _executeReturningFirst( + sql: "UPDATE my_table SET status = $1 WHERE id = $2 AND user_id = $3 RETURNING *", + params: ["inactive", 123, {_expr: "auth.uid"}] + ) + } + ``` + + **Returns:** A single JSON object or `null`, representing a row from the `RETURNING` clause. + """ + _executeReturningFirst( + """ + The SQL DML statement with a `RETURNING *` clause. This must be a **string literal** defined directly in the GraphQL operation. Use `$1`, `$2`, etc. as placeholders for values provided in the `params` argument. + """ + sql: SQL_Mutation!, + """ + A list of values to bind to the `$1`, `$2`, etc. placeholders in the `sql`, in order. Values can also be expressions using `_expr` (e.g., `{_expr: "auth.uid"}`) to inject dynamic values. + """ + params: SQL_Params + ): Any + + """ + Executes a SQL DML statement (e.g., INSERT, UPDATE, DELETE) where the primary result is the number of affected rows. + RETURNING clauses in the SQL will be ignored in the output of this field. + + **Usage:** + + ```graphql + mutation MyCustomExecute { + _execute( + sql: "DELETE FROM my_table WHERE id = $1", + params: [123] + ) + } + ``` + + **Returns:** The number of rows affected. + """ + _execute( + """ + The SQL statement to execute. This must be a **string literal** defined directly in the GraphQL operation. Use `$1`, `$2`, etc. as placeholders for values provided in the `params` argument. + """ + sql: SQL_Mutation!, + """ + A list of values to bind to the `$1`, `$2`, etc. placeholders in the `sql`, in order. Values can also be expressions using `_expr` (e.g., `{_expr: "auth.uid"}`) to inject dynamic values. + """ + params: SQL_Params + ): Int +} + +type Mutation { + """ + Run a query during the mutation and add fields into the response. + + Example: foo: query { users { id } } will add a field foo: {users: [{id: "..."}, …]} into the response JSON. + + Note: Data fetched this way can be handy for permission checks. See @check. + """ + query: Query +} + +""" +`UUID` is a string of hexadecimal digits representing an RFC4122-compliant UUID. + +UUIDs are always output as 32 lowercase hexadecimal digits without delimiters or +curly braces. +Inputs in the following formats are also accepted (case insensitive): + +- `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +- `urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +- `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}` + +In the PostgreSQL table, it's stored as [`uuid`](https://www.postgresql.org/docs/current/datatype-uuid.html). +""" +scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") + +""" +`Int64` is a scalar that represents a 64-bit signed integer. + +In the PostgreSQL table, it's stored as [`bigint`](https://www.postgresql.org/docs/current/datatype-numeric.html). + +On the wire, it's encoded as string because 64-bit integer exceeds the range of JSON number. +""" +scalar Int64 + +""" +The `Any` scalar type accommodates any valid [JSON value](https://www.json.org/json-en.html) +(e.g., numbers, strings, booleans, arrays, objects). PostgreSQL efficiently +stores this data as jsonb, providing flexibility for schemas with evolving structures. + +Caution: JSON doesn't distinguish Int and Float. + +##### Example: + +#### Schema + +```graphql +type Movie @table { + name: String! + metadata: Any! +} +``` + +#### Mutation + +Insert a movie with name and metadata from JSON literal. + +```graphql +mutation InsertMovie { + movie_insert( + data: { + name: "The Dark Knight" + metadata: { + release_year: 2008 + genre: ["Action", "Adventure", "Superhero"] + cast: [ + { name: "Christopher Bale", age: 31 } + { name: "Heath Ledger", age: 28 } + ] + director: "Christopher Nolan" + } + } + ) +} +``` + +Insert a movie with name and metadata that's constructed from a few GQL variables. + +```graphql +mutation InsertMovie($name: String!, $releaseDate: Date!, $genre: [String], $cast: [Any], $director: String!, $boxOfficeInUSD: Int) { + movie_insert(data: { + name: $name, + release_date: $releaseDate, + genre: $genre, + cast: $cast, + director: $director, + box_office: $boxOfficeInUSD + }) +} +``` +**Note**: + + - A mix of non-null and nullable variables can be provided. + + - `Date!` can be passed into scalar `Any` as well! It's stored as string. + + - `$cast` is a nested array. `[Any]` can represent an array of arbitrary types, but it won't enforce the input shape. + +#### Query + +Since `metadata` field has scalar `Any` type, it would return the full JSON in the response. + +**Note**: You can't define selection set to scalar based on [GraphQL spec](https://spec.graphql.org/October2021/#sec-Field-Selections). + +```graphql +query GetAllMovies { + movies { + name + metadata + } +} +``` + +""" +scalar Any @specifiedBy(url: "https://www.json.org/json-en.html") + +""" +The `Void` scalar type represents the absence of any value. It is typically used +in operations where no value is expected in return. +""" +scalar Void @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType + +""" +The `True` scalar type only accepts the boolean value `true`. + +An optional field/argument typed as `True` may either be set +to `true` or omitted (not provided at all). The values `false` or `null` are not +accepted. +""" +scalar True + @fdc_forbiddenAsFieldType + @fdc_forbiddenAsVariableType + @fdc_example(value: true, description: "The only allowed value.") + +""" +Date is a string in the YYYY-MM-DD format representing a local-only date. + +See the description for Timestamp for range and limitations. + +As a FDC-specific extension, inputs that includes time portions (as specified by +the Timestamp scalar) are accepted but only the date portion is used. In other +words, only the part before "T" is used and the rest discarded. This effectively +truncates it to the local date in the specified time-zone. + +Outputs will always be in the canonical YYYY-MM-DD format. + +In the PostgreSQL table, it's stored as [`date`](https://www.postgresql.org/docs/current/datatype-datetime.html). +""" +scalar Date @specifiedBy(url: "https://scalars.graphql.org/andimarek/local-date.html") + +""" +Timestamp is a RFC 3339 string that represents an exact point in time. + +The serialization format follows https://scalars.graphql.org/andimarek/date-time +except the "Non-optional exact milliseconds" Section. As a FDC-specific +extension, inputs and outputs may contain 0, 3, 6, or 9 fractional digits. + +Specifically, output precision varies by server-side factors such as data source +support and clients must not rely on an exact number of digits. Clients may +truncate extra digits as fit, with the caveat that there may be information loss +if the truncated value is subsequently sent back to the server. + +FDC only supports year 1583 to 9999 (inclusive) and uses the ISO-8601 calendar +system for all date-time calculations. Notably, the expanded year representation +(+/-YYYYY) is rejected and Year 1582 and before may either be rejected or cause +undefined behavior. + +In the PostgreSQL table, it's stored as [`timestamptz`](https://www.postgresql.org/docs/current/datatype-datetime.html). +""" +scalar Timestamp @specifiedBy(url: "https://scalars.graphql.org/andimarek/date-time") + + +""" +A Common Expression Language (CEL) expression that returns a boolean at runtime. + +This expression can reference the `auth` variable, which is null when Firebase +Auth is not used. When Firebase Auth is used, the following fields are available: + + - `auth.uid`: The current user ID. + - `auth.token`: A map containing all token fields (e.g., claims). + +""" +scalar Boolean_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "bool") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "auth != null", description: "Allow only if a Firebase Auth user is present.") + +""" +A Common Expression Language (CEL) expression that returns a string at runtime. + +**Limitation**: Currently, only a limited set of expressions are supported. +""" +scalar String_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "string") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "auth.uid", description: "The ID of the currently logged in user in Firebase Auth. (Errors if not logged in.)") + @fdc_example(value: "uuidV4()", description: "Generates a new random UUID (version 4) string, formatted as 32 lower-case hex digits without delimiters.") + +""" +A Common Expression Language (CEL) expression that returns a UUID string at runtime. + +**Limitation**: Currently, only a limited set of expressions are supported. +""" +scalar UUID_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "string") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "uuidV4()", description: "Generates a new random UUID (version 4) every time.") + +""" +A Common Expression Language (CEL) expression that returns a Int at runtime. +""" +scalar Int_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "int") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "2 * 4", description: "Evaluates to 8.") + @fdc_example(value: "vars.foo.size()", description: "Assuming `vars.foo` is a string, it will evaluate to the length of the string.") + + +""" +A Common Expression Language (CEL) expression that returns a Int64 at runtime. +""" +scalar Int64_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "int64") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "5000*1000*1000", description: "Evaluates to 5e9.") + +""" +A Common Expression Language (CEL) expression that returns a Float at runtime. +""" +scalar Float_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "float") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "2.0 * 4.0", description: "Evaluates to 8.0.") + +""" +A Common Expression Language (CEL) expression whose return type is valid JSON. + +Examples: + - `{'A' : 'B'}` (Evaluates to a JSON object.) + - `['A', 'B']` (Evaluates to a JSON array.) + - `{'A' 1, 'B': [1, 2, {'foo': 'bar'}]}` (Nested JSON objects and arrays.) +""" +scalar Any_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + +""" +A PostgreSQL value expression whose return type is unspecified. +""" +scalar Any_SQL + @specifiedBy(url: "https://www.postgresql.org/docs/current/sql-expressions.html") + @fdc_sqlExpression + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + +""" +A Common Expression Language (CEL) expression that returns a Timestamp at runtime. + +Limitation: Right now, only a few expressions are supported. +""" +scalar Timestamp_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "google.protobuf.Timestamp") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "request.time", description: "The timestamp when the request is received (with microseconds precision).") + +""" +A Common Expression Language (CEL) expression that returns a Timestamp at runtime, +which is then truncated to UTC date only. The time-of-day parts are discarded. + +Limitation: Right now, only a few expressions are supported. +""" +scalar Date_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "google.protobuf.Timestamp") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "request.time", description: "The UTC date on which the request is received.") + + +""" +Defines a relational database table. + +In this example, we defined one table with a field named `myField`. + +```graphql +type TableName @table { + myField: String +} +``` +Data Connect adds an implicit `id` primary key column. So the above schema is equivalent to: + +```graphql +type TableName @table(key: "id") { + id: String @default(expr: "uuidV4()") + myField: String +} +``` + +Data Connect generates the following SQL table and CRUD operations to use it. + +```sql +CREATE TABLE "public"."table_name" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "my_field" text NULL, + PRIMARY KEY ("id") +) +``` + + * You can lookup a row: `query ($id: UUID!) { tableName(id: $id) { myField } } ` + * You can find rows using: `query tableNames(limit: 20) { myField }` + * You can insert a row: `mutation { tableName_insert(data: {myField: "foo"}) }` + * You can update a row: `mutation ($id: UUID!) { tableName_update(id: $id, data: {myField: "bar"}) }` + * You can delete a row: `mutation ($id: UUID!) { tableName_delete(id: $id) }` + +##### Customizations + +- `@table(singular)` and `@table(plural)` can customize the singular and plural name. +- `@table(name)` can customize the Postgres table name. +- `@table(key)` can customize the primary key field name and type. + +For example, the `User` table often has a `uid` as its primary key. + +```graphql +type User @table(key: "uid") { + uid: String! + name: String +} +``` + + * You can securely lookup a row: `query { user(key: {uid_expr: "auth.uid"}) { name } } ` + * You can securely insert a row: `mutation { user_insert(data: {uid_expr: "auth.uid" name: "Fred"}) }` + * You can securely update a row: `mutation { user_update(key: {uid_expr: "auth.uid"}, data: {name: "New Name"}) }` + * You can securely delete a row: `mutation { user_delete(key: {uid_expr: "auth.uid"}) }` + +`@table` type can be configured further with: + + - Custom SQL data types for columns. See `@col`. + - Add SQL indexes. See `@index`. + - Add SQL unique constraints. See `@unique`. + - Add foreign key constraints to define relations. See `@ref`. + +""" +directive @table( + """ + Configures the SQL database table name. Defaults to snake_case like `table_name`. + """ + name: String + """ + Configures the singular name. Defaults to the camelCase like `tableName`. + """ + singular: String + """ + Configures the plural name. Defaults to infer based on English plural pattern like `tableNames`. + """ + plural: String + """ + Defines the primary key of the table. Defaults to a single field named `id`. + If not present already, Data Connect adds an implicit field `id: UUID! @default(expr: "uuidV4()")`. + """ + key: [String!] +) on OBJECT + +""" +Defines a relational database SQL view. + +Data Connect generates GraphQL queries with WHERE and ORDER BY clauses. +However, not all SQL features have a native GraphQL equivalent. + +With `@view`, you can write **arbitrary SQL SELECT statements** and Data Connect +maps GraphQL fields on `@view` type to columns in your SELECT statement. + +* Scalar GQL fields (camelCase) should match SQL columns (snake_case) + in the SQL SELECT statement. +* Reference GQL field can point to another `@table` type. Similar to foreign key + defined with `@ref` on a `@table` type, a `@view` type establishes a relation + when `@ref(fields)` match `@ref(references)` on the target table. + +In this example, you can use `@view(sql)` to define an aggregation view on existing +table. + +```graphql +type User @table { + name: String + score: Int +} +type UserAggregation @view(sql: ''' + SELECT + COUNT(*) as count, + SUM(score) as sum, + AVG(score) as average, + PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score) AS median, + (SELECT id FROM "user" LIMIT 1) as example_id + FROM "user" +''') { + count: Int + sum: Int + average: Float + median: Float + example: User + exampleId: UUID +} +``` + +###### Example: Query SQL View + +```graphql +query { + userAggregations { + count sum average median + exampleId example { id } + } +} +``` + +##### One-to-One View + +An one-to-one companion `@view` can be handy if you want to argument a `@table` +with additional implied content. + +```graphql +type Restaurant @table { + name: String! +} +type Review @table { + restaurant: Restaurant! + rating: Int! +} +type RestaurantStats @view(sql: ''' + SELECT + restaurant_id, + COUNT(*) AS review_count, + AVG(rating) AS average_rating + FROM review + GROUP BY restaurant_id +''') { + restaurant: Restaurant @unique + reviewCount: Int + averageRating: Float +} +``` + +In this example, `@unique` convey the assumption that each `Restaurant` should +have only one `RestaurantStats` object. + +###### Example: Query One-to-One View + +```graphql +query ListRestaurants { + restaurants { + name + stats: restaurantStats_on_restaurant { + reviewCount + averageRating + } + } +} +``` + +###### Example: Filter based on One-to-One View + +```graphql +query BestRestaurants($minAvgRating: Float, $minReviewCount: Int) { + restaurants(where: { + restaurantStats_on_restaurant: { + averageRating: {ge: $minAvgRating} + reviewCount: {ge: $minReviewCount} + } + }) { name } +} +``` + +##### Customizations + +- One of `@view(sql)` or `@view(name)` should be defined. + `@view(name)` can refer to a persisted SQL view in the Postgres schema. +- `@view(singular)` and `@view(plural)` can customize the singular and plural name. + +`@view` type can be configured further: + + - `@unique` lets you define one-to-one relation. + - `@col` lets you customize SQL column mapping. For example, `@col(name: "column_in_select")`. + +##### Limitations + +SQL views doesn't have a primary key, so they don't support lookup. Other +`@table` or `@view` cannot have `@ref` to a view either. + +A view cannot be mutated. You can perform CRUD operations on the underlying +table to alter its content. + +**Important: Data Connect doesn't parse and validate SQL** + +- If the SQL view is invalid or undefined, related requests may fail. +- If the SQL view return incompatible types. Firebase Data Connect may surface + errors. +- If a field doesn't have a corresponding column in the SQL SELECT statement, + it will always be `null`. +- There is no way to ensure VIEW to TABLE `@ref` constraint. +- All fields must be nullable in case they aren't found in the SELECT statement + or in the referenced table. + +**Important: You should always test `@view`!** + +""" +directive @view( + """ + The SQL view name. If neither `name` nor `sql` are provided, defaults to the + snake_case of the singular type name. + `name` and `sql` cannot be specified at the same time. + """ + name: String @fdc_oneOf + """ + SQL `SELECT` statement used as the basis for this type. + SQL SELECT columns should use snake_case. GraphQL fields should use camelCase. + `name` and `sql` cannot be specified at the same time. + """ + sql: String @fdc_oneOf + """ + Configures the singular name. Defaults to the camelCase like `viewName`. + """ + singular: String + """ + Configures the plural name. Defaults to infer based on English plural pattern like `viewNames`. + """ + plural: String +) on OBJECT + +""" +Customizes a field that represents a SQL database table column. + +Data Connect maps scalar Fields on `@table` type to a SQL column of +corresponding data type. + +- scalar `UUID` maps to [`uuid`](https://www.postgresql.org/docs/current/datatype-uuid.html). +- scalar `String` maps to [`text`](https://www.postgresql.org/docs/current/datatype-character.html). +- scalar `Int` maps to [`int`](https://www.postgresql.org/docs/current/datatype-numeric.html). +- scalar `Int64` maps to [`bigint`](https://www.postgresql.org/docs/current/datatype-numeric.html). +- scalar `Float` maps to [`double precision`](https://www.postgresql.org/docs/current/datatype-numeric.html). +- scalar `Boolean` maps to [`boolean`](https://www.postgresql.org/docs/current/datatype-boolean.html). +- scalar `Date` maps to [`date`](https://www.postgresql.org/docs/current/datatype-datetime.html). +- scalar `Timestamp` maps to [`timestamptz`](https://www.postgresql.org/docs/current/datatype-datetime.html). +- scalar `Any` maps to [`jsonb`](https://www.postgresql.org/docs/current/datatype-json.html). +- scalar `Vector` maps to [`pgvector`](https://github.com/pgvector/pgvector). + +Array scalar fields are mapped to [Postgres arrays](https://www.postgresql.org/docs/current/arrays.html). + +###### Example: Serial Primary Key + +For example, you can define auto-increment primary key. + +```graphql +type Post @table { + id: Int! @col(name: "post_id", dataType: "serial") +} +``` + +Data Connect converts it to the following SQL table schema. + +```sql +CREATE TABLE "public"."post" ( + "post_id" serial NOT NULL, + PRIMARY KEY ("id") +) +``` + +###### Example: Vector + +```graphql +type Post @table { + content: String! @col(name: "post_content") + contentEmbedding: Vector! @col(size:768) +} +``` + +""" +directive @col( + """ + The SQL database column name. Defaults to snake_case of the field name. + """ + name: String + """ + Configures the custom SQL data type. + + Each GraphQL type can map to multiple SQL data types. + Refer to [Postgres supported data types](https://www.postgresql.org/docs/current/datatype.html). + + Incompatible SQL data type will lead to undefined behavior. + """ + dataType: String + """ + Required on `Vector` columns. It specifies the length of the Vector. + `textembedding-gecko@003` model generates `Vector` of `@col(size:768)`. + """ + size: Int +) on FIELD_DEFINITION + + +""" +Defines a foreign key reference to another table. + +For example, we can define a many-to-one relation. + +```graphql +type ManyTable @table { + refField: OneTable! +} +type OneTable @table { + someField: String! +} +``` +Data Connect adds implicit foreign key column and relation query field. So the +above schema is equivalent to the following schema. + +```graphql +type ManyTable @table { + id: UUID! @default(expr: "uuidV4()") + refField: OneTable! @ref(fields: "refFieldId", references: "id") + refFieldId: UUID! +} +type OneTable @table { + id: UUID! @default(expr: "uuidV4()") + someField: UUID! + # Generated Fields: + # manyTables_on_refField: [ManyTable!]! +} +``` +Data Connect generates the necessary foreign key constraint. + +```sql +CREATE TABLE "public"."many_table" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "ref_field_id" uuid NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "many_table_ref_field_id_fkey" FOREIGN KEY ("ref_field_id") REFERENCES "public"."one_table" ("id") ON DELETE CASCADE +) +``` + +###### Example: Traverse the Reference Field + +```graphql +query ($id: UUID!) { + manyTable(id: $id) { + refField { id } + } +} +``` + +###### Example: Reverse Traverse the Reference field + +```graphql +query ($id: UUID!) { + oneTable(id: $id) { + manyTables_on_refField { id } + } +} +``` + +##### Optional Many-to-One Relation + +An optional foreign key reference will be set to null if the referenced row is deleted. + +In this example, if a `User` is deleted, the `assignee` and `reporter` +references will be set to null. + +```graphql +type Bug @table { + title: String! + assignee: User + reproter: User +} + +type User @table { name: String! } +``` + +##### Required Many-to-One Relation + +A required foreign key reference will cascade delete if the referenced row is +deleted. + +In this example, if a `Post` is deleted, associated comments will also be +deleted. + +```graphql +type Comment @table { + post: Post! + content: String! +} + +type Post @table { title: String! } +``` + +##### Many To Many Relation + +You can define a many-to-many relation with a join table. + +```graphql +type Membership @table(key: ["group", "user"]) { + group: Group! + user: User! + role: String! @default(value: "member") +} + +type Group @table { name: String! } +type User @table { name: String! } +``` + +When Data Connect sees a table with two reference field as its primary key, it +knows this is a join table, so expands the many-to-many query field. + +```graphql +type Group @table { + name: String! + # Generated Fields: + # users_via_Membership: [User!]! + # memberships_on_group: [Membership!]! +} +type User @table { + name: String! + # Generated Fields: + # groups_via_Membership: [Group!]! + # memberships_on_user: [Membership!]! +} +``` + +###### Example: Traverse the Many-To-Many Relation + +```graphql +query ($id: UUID!) { + group(id: $id) { + users: users_via_Membership { + name + } + } +} +``` + +###### Example: Traverse to the Join Table + +```graphql +query ($id: UUID!) { + group(id: $id) { + memberships: memberships_on_group { + user { name } + role + } + } +} +``` + +##### One To One Relation + +You can even define a one-to-one relation with the help of `@unique` or `@table(key)`. + +```graphql +type User @table { + name: String +} +type Account @table { + user: User! @unique +} +# Alternatively, use primary key constraint. +# type Account @table(key: "user") { +# user: User! +# } +``` + +###### Example: Transerse the Reference Field + +```graphql +query ($id: UUID!) { + account(id: $id) { + user { id } + } +} +``` + +###### Example: Reverse Traverse the Reference field + +```graphql +query ($id: UUID!) { + user(id: $id) { + account_on_user { id } + } +} +``` + +##### Customizations + +- `@ref(constraintName)` can customize the SQL foreign key constraint name (`table_name_ref_field_fkey` above). +- `@ref(fields)` can customize the foreign key field names. +- `@ref(references)` can customize the constraint to reference other columns. + By default, `@ref(references)` is the primary key of the `@ref` table. + Other fields with `@unique` may also be referred in the foreign key constraint. + +""" +directive @ref( + "The SQL database foreign key constraint name. Defaults to snake_case `{table_name}_{field_name}_fkey`." + constraintName: String + """ + Foreign key fields. Defaults to `{tableName}{PrimaryIdName}`. + """ + fields: [String!] + "The fields that the foreign key references in the other table. Defaults to its primary key." + references: [String!] +) on FIELD_DEFINITION + +"Defines the orderBy direction in a query." +enum OrderDirection @fdc_forbiddenAsFieldType { +"Results are ordered in ascending order." + ASC +"Results are ordered in descending order." + DESC +} + +""" +Specifies the default value for a column field. + +For example: + +```graphql +type User @table(key: "uid") { + uid: String! @default(expr: "auth.uid") + number: Int! @col(dataType: "serial") + createdAt: Date! @default(expr: "request.time") + role: String! @default(value: "Member") + credit: Int! @default(value: 100) +} +``` + +The supported arguments vary based on the field type. +""" +directive @default( + "A constant value validated against the field's GraphQL type during compilation." + value: Any @fdc_oneOf(required: true) + "A CEL expression whose return value must match the field's data type." + expr: Any_Expr @fdc_oneOf(required: true) + """ + An SQL expression, whose SQL data type must match the underlying column. + + The value is any variable-free expression (in particular, cross-references to + other columns in the current table are not allowed). Subqueries are not allowed either. + See [PostgreSQL defaults](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-DEFAULT) + for more details. + """ + sql: Any_SQL @fdc_oneOf(required: true) +) on FIELD_DEFINITION + +""" +Defines a database index to optimize query performance. + +```graphql +type User @table @index(fields: ["name", "phoneNumber"], order: [ASC, DESC]) { + name: String @index + phoneNumber: Int64 @index + tags: [String] @index # GIN Index +} +``` + +##### Single Field Index + +You can put `@index` on a `@col` field to create a SQL index. + +`@index(order)` matters little for single field indexes, as they can be scanned +in both directions. + +##### Composite Index + +You can put `@index(fields: [...])` on `@table` type to define composite indexes. + +`@index(order: [...])` can customize the index order to satisfy particular +filter and order requirement. + +""" +directive @index( + """ + Configure the SQL database index id. + + If not overridden, Data Connect generates the index name: + - `{table_name}_{first_field}_{second_field}_aa_idx` + - `{table_name}_{field_name}_idx` + """ + name: String + """ + Only allowed and required when used on a `@table` type. + Specifies the fields to create the index on. + """ + fields: [String!] + """ + Only allowed for `BTREE` `@index` on `@table` type. + Specifies the order for each indexed column. Defaults to all `ASC`. + """ + order: [IndexFieldOrder!] + """ + Customize the index type. + + For most index, it defaults to `BTREE`. + For array fields, only allowed `IndexType` is `GIN`. + For `Vector` fields, defaults to `HNSW`, may configure to `IVFFLAT`. + """ + type: IndexType + """ + Only allowed when used on vector field. + Defines the vector similarity method. Defaults to `INNER_PRODUCT`. + """ + vector_method: VectorSimilarityMethod +) repeatable on FIELD_DEFINITION | OBJECT + +"Specifies the sorting order for database indexes." +enum IndexFieldOrder @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "Sorts the field in ascending order (from lowest to highest)." + ASC + "Sorts the field in descending order (from highest to lowest)." + DESC +} + +"Defines the type of index to be used in the database." +enum IndexType @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "A general-purpose index type commonly used for sorting and searching." + BTREE + "Generalized Inverted Index, optimized for indexing composite values such as arrays." + GIN + "Hierarchical Navigable Small World graph, used for nearest-neighbor searches on vector fields." + HNSW + "Inverted File Index, optimized for approximate nearest-neighbor searches in vector databases." + IVFFLAT +} + +""" +Defines unique constraints on `@table`. + +For example, + +```graphql +type User @table { + phoneNumber: Int64 @unique +} +type UserProfile @table { + user: User! @unique + address: String @unique +} +``` + +- `@unique` on a `@col` field adds a single-column unique constraint. +- `@unique` on a `@table` type adds a composite unique constraint. +- `@unique` on a `@ref` defines a one-to-one relation. It adds unique constraint + on `@ref(fields)`. + +`@unique` ensures those fields can uniquely identify a row, so other `@table` +type may define `@ref(references)` to refer to fields that has a unique constraint. + +""" +directive @unique( + """ + Configures the SQL database unique constraint name. + + If not overridden, Data Connect generates the unique constraint name: + - `table_name_first_field_second_field_uidx` + - `table_name_only_field_name_uidx` + """ + indexName: String + """ + Only allowed and required when used on OBJECT, + this specifies the fields to create a unique constraint on. + """ + fields: [String!] +) repeatable on FIELD_DEFINITION | OBJECT + +"A runtime-calculated `Timestamp` value relative to `now` or `at`." +input Timestamp_Relative @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "Match for the current time." + now: True @fdc_oneOf(group: "from", required: true) + "A specific timestamp for matching." + at: Timestamp @fdc_oneOf(group: "from", required: true) + "Add the provided duration to the base timestamp." + add: Timestamp_Duration + "Subtract the provided duration from the base timestamp." + sub: Timestamp_Duration + "Truncate the timestamp to the provided interval." + truncateTo: Timestamp_Interval +} + +input Timestamp_Duration @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "The number of milliseconds for the duration." + milliseconds: Int! = 0 + "The number of seconds for the duration." + seconds: Int! = 0 + "The number of minutes for the duration." + minutes: Int! = 0 + "The number of hours for the duration." + hours: Int! = 0 + "The number of days for the duration." + days: Int! = 0 + "The number of weeks for the duration." + weeks: Int! = 0 + "The number of months for the duration." + months: Int! = 0 + "The number of years for the duration." + years: Int! = 0 +} + +enum Timestamp_Interval @fdc_forbiddenAsFieldType { + "Represents a time interval of one second." + SECOND + "Represents a time interval of one minute." + MINUTE + "Represents a time interval of one hour." + HOUR + "Represents a time interval of one day." + DAY + "Represents a time interval of one week." + WEEK + "Represents a time interval of one month." + MONTH + "Represents a time interval of one year." + YEAR +} + +"A runtime-calculated Date value relative to `today` or `on`." +input Date_Relative @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "Match for today’s date." + today: True @fdc_oneOf(group: "from", required: true) + "A specific date for matching." + on: Date @fdc_oneOf(group: "from", required: true) + "Add the provided duration to the base date." + add: Date_Duration + "Subtract the provided duration from the base date." + sub: Date_Duration + "Truncate the date to the provided interval." + truncateTo: Date_Interval +} + +input Date_Duration @fdc_forbiddenAsVariableType @fdc_forbiddenAsFieldType { + "The number of days for the duration." + days: Int! = 0 + "The number of weeks for the duration." + weeks: Int! = 0 + "The number of months for the duration." + months: Int! = 0 + "The number of years for the duration." + years: Int! = 0 +} + +enum Date_Interval @fdc_forbiddenAsFieldType { + "Represents a time interval of one week." + WEEK + "Represents a time interval of one month." + MONTH + "Represents a time interval of one year." + YEAR +} + +"Update input of a `String` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input String_ListUpdate { + "Append the provided values to the existing list." + append: [String!] @fdc_oneOf + "Prepend the provided values to the existing list." + prepend: [String!] @fdc_oneOf + "Append values that do not already exist to the list." + add: [String!] @fdc_oneOf + "Remove all occurrences of each value from the list." + remove: [String!] @fdc_oneOf +} + +"Update input of an `ID` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input UUID_ListUpdate { + "Append the provided UUIDs to the existing list." + append: [UUID!] @fdc_oneOf + "Prepend the provided UUIDs to the existing list." + prepend: [UUID!] @fdc_oneOf + "Append values that do not already exist to the list." + add: [UUID!] @fdc_oneOf + "Remove all occurrences of each value from the list." + remove: [UUID!] @fdc_oneOf +} + +"Update input of an `Int` value. Only one of `inc` or `dec` may be specified." +input Int_Update { + "Increment the field by a provided value." + inc: Int @fdc_oneOf + "Decrement the field by a provided value." + dec: Int @fdc_oneOf +} + +"Update input of an `Int` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input Int_ListUpdate { + "Append the provided list of values to the existing list." + append: [Int!] @fdc_oneOf + "Prepend the provided list of values to the existing list." + prepend: [Int!] @fdc_oneOf + "Append values that do not already exist to the list." + add: [Int!] @fdc_oneOf + "Remove all occurrences of each value from the list." + remove: [Int!] @fdc_oneOf +} + +"Update input of an `Int64` value. Only one of `inc` or `dec` may be specified." +input Int64_Update { + "Increment the field by a provided value." + inc: Int64 @fdc_oneOf + "Decrement the field by a provided value." + dec: Int64 @fdc_oneOf +} + +"Update input of an `Int64` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input Int64_ListUpdate { + "Append the provided list of values to the existing list." + append: [Int64!] @fdc_oneOf + "Prepend the provided list of values to the existing list." + prepend: [Int64!] @fdc_oneOf + "Append values that do not already exist to the list." + add: [Int64!] @fdc_oneOf + "Remove all occurrences of each value from the list." + remove: [Int64!] @fdc_oneOf +} + +"Update input of a `Float` value. Only one of `inc` or `dec` may be specified." +input Float_Update { + "Increment the field by a provided value." + inc: Float @fdc_oneOf + "Decrement the field by a provided value." + dec: Float @fdc_oneOf +} + +"Update input of a `Float` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input Float_ListUpdate { + "Append the provided list of values to the existing list." + append: [Float!] @fdc_oneOf + "Prepend the provided list of values to the existing list." + prepend: [Float!] @fdc_oneOf + "Append values that do not already exist to the list." + add: [Float!] @fdc_oneOf + "Remove all occurrences of each value from the list." + remove: [Float!] @fdc_oneOf +} + +"Update input of a `Boolean` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input Boolean_ListUpdate { + "Append the provided list of values to the existing list." + append: [Boolean!] @fdc_oneOf + "Prepend the provided list of values to the existing list." + prepend: [Boolean!] @fdc_oneOf + "Append values that do not already exist to the list." + add: [Boolean!] @fdc_oneOf + "Remove all occurrences of each value from the list." + remove: [Boolean!] @fdc_oneOf +} + +"Update input of an `Any` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input Any_ListUpdate { + "Append the provided list of values to the existing list." + append: [Any!] @fdc_oneOf + "Prepend the provided list of values to the existing list." + prepend: [Any!] @fdc_oneOf + "Append values that do not already exist to the list." + add: [Any!] @fdc_oneOf + "Remove all occurrences of each value from the list." + remove: [Any!] @fdc_oneOf +} + +"Update input of a `Date` value. Only one of `inc` or `dec` may be specified." +input Date_Update { + "Increment the field by a provided duration." + inc: Date_Duration @fdc_oneOf + "Decrement the field by a provided duration." + dec: Date_Duration @fdc_oneOf +} + +"Update input of a `Date` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input Date_ListUpdate { + "Append the provided `Date` values to the existing list." + append: [Date!] @fdc_oneOf + "Prepend the provided `Date` values to the existing list." + prepend: [Date!] @fdc_oneOf + "Append any `Date` values that do not already exist to the list." + add: [Date!] @fdc_oneOf + "Remove all occurrences of each `Date` from the list." + remove: [Date!] @fdc_oneOf +} + +"Update input of a `Timestamp` value. Only one of `inc` or `dec` may be specified." +input Timestamp_Update { + "Increment the field by a provided duration." + inc: Timestamp_Duration @fdc_oneOf + "Decrement the field by a provided duration." + dec: Timestamp_Duration @fdc_oneOf +} + +"Update input of an `Timestamp` list value. Only one of `append`, `prepend`, `add`, or `remove` may be specified." +input Timestamp_ListUpdate { + "Append the provided `Timestamp` values to the existing list." + append: [Timestamp!] @fdc_oneOf + "Prepend the provided `Timestamp` values to the existing list." + prepend: [Timestamp!] @fdc_oneOf + "Append any `Timestamp` values that do not already exist to the list." + add: [Timestamp!] @fdc_oneOf + "Remove all occurrences of each `Timestamp` from the list." + remove: [Timestamp!] @fdc_oneOf +} + +type Query { + """ + _service provides customized introspection on Firebase Data Connect Sevice. + """ + _service: _Service! +} + +""" +Vector is an array of single-precision floating-point numbers, serialized +as a JSON array. All elements must be finite (no NaN, Infinity or -Infinity). + +Example: [1.1, 2, 3.3] + +In the PostgreSQL table, it's stored as [`pgvector`](https://github.com/pgvector/pgvector). + +See `Vector_Embed` for how to generate text embeddings in query and mutations. +""" +scalar Vector + +""" +Defines the similarity function to use when comparing vectors in queries. + +Defaults to `INNER_PRODUCT`. + +View [all vector functions](https://github.com/pgvector/pgvector?tab=readme-ov-file#vector-functions). +""" +enum VectorSimilarityMethod @fdc_forbiddenAsFieldType { + "Measures the Euclidean (L2) distance between two vectors." + L2 + "Measures the cosine similarity between two vectors." + COSINE + "Measures the inner product(dot product) between two vectors." + INNER_PRODUCT +} + +"Conditions on a Vector value." +input Vector_Filter { + "Match if the field is exactly equal to the provided vector." + eq: Vector + "Match if the field is not equal to the provided vector." + ne: Vector + "Match if the field value is among the provided list of vectors." + in: [Vector!] + "Match if the field value is not among the provided list of vectors." + nin: [Vector!] + "Match if the field is `NULL`." + isNull: Boolean +} + +input Vector_ListFilter { + "Match if the list includes the supplied vector." + includes: Vector + "Match if the list does not include the supplied vector." + excludes: Vector + "Match if the list contains all the provided vectors." + includesAll: [Vector!] + "Match if the list contains none of the provided vectors." + excludesAll: [Vector!] +} + +""" +Create a vector embedding of text using the given model on Vertex AI. + +Cloud SQL for Postgresql natively integrates with [Vertex AI Text embeddings API](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api) +to effectively generate text embeddings. + +If you uses [`Vector`](scalar.md#Vector) in your schema, Firebase Data Connect automatically installs +[`pgvector`](https://github.com/pgvector/pgvector) and [`google_ml_integration`](https://cloud.google.com/sql/docs/postgres/integrate-cloud-sql-with-vertex-ai) +Postgres extensions in your Cloud SQL database. + +Given a Post table with a `Vector` embedding field. + +```graphql +type Post @table { + content: String! + contentEmbedding: Vector @col(size:768) +} +``` + +NOTE: All natively supported `Vector_Embed_Model` generates vector of length `768`. + +###### Example: Insert embedding + +```graphql +mutation CreatePost($content: String!) { + post_insert(data: { + content: $content, + contentEmbedding_embed: {model: "textembedding-gecko@003", text: $content}, + }) +} +``` + +###### Example: Vector similarity Search + +```graphql +query SearchPost($query: String!) { + posts_contentEmbedding_similarity(compare_embed: {model: "textembedding-gecko@003", text: $query}) { + id + content + } +} +``` +""" +input Vector_Embed @fdc_forbiddenAsVariableType { + """ + The model to use for vector embedding. + Recommend the latest stable model: `textembedding-gecko@003`. + """ + model: Vector_Embed_Model! + "The text to generate the vector embedding from." + text: String! +} + +""" +The Vertex AI model version that is required in input `Vector_Embed`. + +It is recommended to use the latest stable model version: `textembedding-gecko@003`. + +View all supported [Vertex AI Text embeddings APIs](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api). +""" +scalar Vector_Embed_Model + @specifiedBy(url: "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/model-versioning") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "textembedding-gecko@003", description: "A stable version of the textembedding-gecko model") + @fdc_example(value: "textembedding-gecko@001", description: "An older version of the textembedding-gecko model") + @fdc_example(value: "text-embedding-004", description: "Another text embedding model") + +# Intentionally left blank. + diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart index 1ea81e9e0aa3..78e604c432cf 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart @@ -14,6 +14,37 @@ import 'generation_e2e.dart'; import 'instance_e2e.dart'; import 'listen_e2e.dart'; import 'query_e2e.dart'; +import 'websocket_e2e.dart'; + +Future _signInTestUser() async { + final auth = FirebaseAuth.instance; + const password = 'password'; + final email = 'fdc-test-${DateTime.now().microsecondsSinceEpoch}@mail.com'; + + for (var attempt = 0; attempt < 5; attempt++) { + try { + await auth.createUserWithEmailAndPassword( + email: email, + password: password, + ); + return; + } on FirebaseAuthException catch (e) { + if (e.code == 'email-already-in-use') { + await auth.signInWithEmailAndPassword( + email: email, + password: password, + ); + return; + } + + if (attempt == 4) { + rethrow; + } + } + + await Future.delayed(Duration(seconds: attempt + 1)); + } +} void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -27,16 +58,16 @@ void main() { final connector = MoviesConnector.connectorConfig; FirebaseDataConnect.instanceFor(connectorConfig: connector) - .useDataConnectEmulator('localhost', 9399); - await FirebaseAuth.instance.useAuthEmulator('localhost', 9099); + .useDataConnectEmulator('127.0.0.1', 9399); + await FirebaseAuth.instance.useAuthEmulator('127.0.0.1', 9099); - await FirebaseAuth.instance.createUserWithEmailAndPassword( - email: 'test@mail.com', password: 'password'); + await _signInTestUser(); }); runInstanceTests(); runQueryTests(); runGenerationTest(); runListenTests(); + runWebSocketTests(); }); } diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart new file mode 100644 index 000000000000..a105b4783184 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart @@ -0,0 +1,250 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; + +import 'package:firebase_data_connect/firebase_data_connect.dart'; +import 'package:firebase_data_connect_example/generated/movies.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'query_e2e.dart'; // For deleteAllMovies + +const _streamTimeout = Duration(seconds: 30); + +Future _waitForStreamEvent(Future future, String description) { + return future.timeout( + _streamTimeout, + onTimeout: () => throw TimeoutException( + 'Timed out waiting for $description', + _streamTimeout, + ), + ); +} + +void runWebSocketTests() { + group( + '$FirebaseDataConnect WebSocketTransport', + () { + setUp(() async { + await deleteAllMovies(); + }); + + testWidgets('should support multiplexing multiple subscriptions', + (WidgetTester tester) async { + final Completer ready1 = Completer(); + final Completer ready2 = Completer(); + final Completer update1 = Completer(); + final Completer update2 = Completer(); + + int count1 = 0; + int count2 = 0; + + final sub1 = MoviesConnector.instance + .listMoviesByPartialTitle(input: 'Matrix') + .ref() + .subscribe() + .listen((value) { + if (count1 == 0) { + if (!ready1.isCompleted) ready1.complete(); + } else { + if (!update1.isCompleted) update1.complete(); + } + count1++; + }); + + final sub2 = MoviesConnector.instance + .listMoviesByPartialTitle(input: 'Titan') + .ref() + .subscribe() + .listen((value) { + if (count2 == 0) { + if (!ready2.isCompleted) ready2.complete(); + } else { + if (!update2.isCompleted) update2.complete(); + } + count2++; + }); + + try { + // Wait for both to be ready + await _waitForStreamEvent(ready1.future, 'Matrix subscription'); + await _waitForStreamEvent(ready2.future, 'Titan subscription'); + + // Create movies + await MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'The Matrix', + releaseYear: 1999, + ) + .rating(4.5) + .ref() + .execute(); + + await MoviesConnector.instance + .createMovie( + genre: 'Drama', + title: 'Titanic', + releaseYear: 1997, + ) + .rating(4.8) + .ref() + .execute(); + + // Explicitly resume each active query so this test does not depend on + // emulator-side push timing. + await MoviesConnector.instance + .listMoviesByPartialTitle(input: 'Matrix') + .ref() + .execute(fetchPolicy: QueryFetchPolicy.serverOnly); + await MoviesConnector.instance + .listMoviesByPartialTitle(input: 'Titan') + .ref() + .execute(fetchPolicy: QueryFetchPolicy.serverOnly); + + // Wait for updates + await _waitForStreamEvent(update1.future, 'Matrix update'); + await _waitForStreamEvent(update2.future, 'Titan update'); + } finally { + await sub1.cancel(); + await sub2.cancel(); + } + }); + + testWidgets( + 'should support unary operations over WebSocket when connected', + (WidgetTester tester) async { + final Completer isReady = Completer(); + int count = 0; + + // Start a subscription to ensure WebSocket is connected + final sub = MoviesConnector.instance + .listMovies() + .ref() + .subscribe() + .listen((value) { + if (count == 0) { + if (!isReady.isCompleted) isReady.complete(); + } + count++; + }); + + try { + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); + + // Now perform a query, which should go over WebSocket if connected + final result = + await MoviesConnector.instance.listMovies().ref().execute(); + expect(result.data.movies.length, 0); + + // Perform a mutation + await MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'Inception', + releaseYear: 2010, + ) + .rating(4.9) + .ref() + .execute(); + + // Verify update via query + final result2 = + await MoviesConnector.instance.listMovies().ref().execute(); + expect(result2.data.movies.length, 1); + expect(result2.data.movies[0].title, 'Inception'); + } finally { + await sub.cancel(); + } + }); + + testWidgets('should stop receiving events after cancel', + (WidgetTester tester) async { + final Completer isReady = Completer(); + final Completer receivedUpdate = Completer(); + int count = 0; + + final sub = MoviesConnector.instance + .listMovies() + .ref() + .subscribe() + .listen((value) { + if (count == 0) { + if (!isReady.isCompleted) isReady.complete(); + } else { + if (!receivedUpdate.isCompleted) receivedUpdate.complete(); + } + count++; + }); + + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); + + // Cancel the subscription + await sub.cancel(); + + // Create a movie + await MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'Avatar', + releaseYear: 2009, + ) + .rating(4.7) + .ref() + .execute(); + + // Wait a bit to ensure no event is received + bool received = true; + try { + await receivedUpdate.future.timeout(const Duration(seconds: 2)); + } on TimeoutException { + received = false; + } + expect(received, isFalse, + reason: 'Should not receive events after cancel'); + }); + + testWidgets( + 'should disconnect the websocket channel when all subscriptions are closed', + (WidgetTester tester) async { + final Completer isReady = Completer(); + int count = 0; + + final sub = MoviesConnector.instance + .listMovies() + .ref() + .subscribe() + .listen((value) { + if (count == 0) { + if (!isReady.isCompleted) isReady.complete(); + } + count++; + }); + + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); + + final dataConnect = MoviesConnector.instance.dataConnect; + final transport = (dataConnect as dynamic).transport; + final ws = (transport as dynamic).websocket; + + expect(ws.isConnected, isTrue); + + // Cancel the subscription + await sub.cancel(); + + expect(ws.isConnected, isFalse); + }); + }, + ); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/config.json b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/config.json new file mode 100644 index 000000000000..e37ed06f8a66 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/config.json @@ -0,0 +1,9 @@ +{ + "description": "A set of guides for interacting with the generated firebase dataconnect sdk", + "mcpServers": { + "firebase": { + "command": "npx", + "args": ["-y", "firebase-tools@latest", "experimental:mcp"] + } + } +} diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/setup.md b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/setup.md new file mode 100644 index 000000000000..4a3737fe81bf --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/setup.md @@ -0,0 +1,15 @@ +# Setup + +This guide will walk you through setting up your environment to use the Firebase Data Connect SDK. Mostly using +documentation listed [here](https://firebase.google.com/docs/flutter/setup?platform=ios#install-cli-tools). + +1. Make sure you have the latest Firebase CLI tools installed. Follow the instructions [here](https://firebase.google.com/docs/cli#setup_update_cli) to install. +2. Log into your Firebase account: +```sh +firebase login +``` +3. Install the FlutterFire CLI by running the following command from any directory: +```sh +dart pub global activate flutterfire_cli +``` +4. Make sure the user has initialized Firebase already based on the instructions [here](https://firebase.google.com/docs/flutter/setup?platform=ios#initialize-firebase). diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/usage.md b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/usage.md new file mode 100644 index 000000000000..9006a3657ea6 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/.guides/usage.md @@ -0,0 +1,32 @@ +# Basic Usage + +```dart +MoviesConnector.instance.addPerson(addPersonVariables).execute(); +MoviesConnector.instance.addDirectorToMovie(addDirectorToMovieVariables).execute(); +MoviesConnector.instance.addTimestamp(addTimestampVariables).execute(); +MoviesConnector.instance.addDateAndTimestamp(addDateAndTimestampVariables).execute(); +MoviesConnector.instance.seedMovies().execute(); +MoviesConnector.instance.createMovie(createMovieVariables).execute(); +MoviesConnector.instance.deleteMovie(deleteMovieVariables).execute(); +MoviesConnector.instance.thing(thingVariables).execute(); +MoviesConnector.instance.seedData().execute(); +MoviesConnector.instance.ListMovies().execute(); + +``` + +## Optional Fields + +Some operations may have optional fields. In these cases, the Flutter SDK exposes a builder method, and will have to be set separately. + +Optional fields can be discovered based on classes that have `Optional` object types. + +This is an example of a mutation with an optional field: + +```dart +await MoviesConnector.instance.ListThing({ ... }) +.data(...) +.execute(); +``` + +Note: the above example is a mutation, but the same logic applies to query operations as well. Additionally, `createMovie` is an example, and may not be available to the user. + diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart index 8f221792ec16..8f7680ea851e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_date_and_timestamp.dart @@ -29,10 +29,27 @@ class AddDateAndTimestampVariablesBuilder { } } +@immutable class AddDateAndTimestampTimestampHolderInsert { - String id; + final String id; AddDateAndTimestampTimestampHolderInsert.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddDateAndTimestampTimestampHolderInsert otherTyped = + other as AddDateAndTimestampTimestampHolderInsert; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -45,12 +62,28 @@ class AddDateAndTimestampTimestampHolderInsert { }); } +@immutable class AddDateAndTimestampData { - AddDateAndTimestampTimestampHolderInsert timestampHolder_insert; + final AddDateAndTimestampTimestampHolderInsert timestampHolder_insert; AddDateAndTimestampData.fromJson(dynamic json) : timestampHolder_insert = AddDateAndTimestampTimestampHolderInsert.fromJson( json['timestampHolder_insert']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddDateAndTimestampData otherTyped = other as AddDateAndTimestampData; + return timestampHolder_insert == otherTyped.timestampHolder_insert; + } + + @override + int get hashCode => timestampHolder_insert.hashCode; Map toJson() { Map json = {}; @@ -63,14 +96,31 @@ class AddDateAndTimestampData { }); } +@immutable class AddDateAndTimestampVariables { - DateTime date; - Timestamp timestamp; + final DateTime date; + final Timestamp timestamp; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') AddDateAndTimestampVariables.fromJson(Map json) : date = nativeFromJson(json['date']), timestamp = Timestamp.fromJson(json['timestamp']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddDateAndTimestampVariables otherTyped = + other as AddDateAndTimestampVariables; + return date == otherTyped.date && timestamp == otherTyped.timestamp; + } + + @override + int get hashCode => Object.hashAll([date.hashCode, timestamp.hashCode]); Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart index f70b56719550..235d4c31ae78 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_director_to_movie.dart @@ -39,12 +39,30 @@ class AddDirectorToMovieVariablesBuilder { } } +@immutable class AddDirectorToMovieDirectedByInsert { - String directedbyId; - String movieId; + final String directedbyId; + final String movieId; AddDirectorToMovieDirectedByInsert.fromJson(dynamic json) : directedbyId = nativeFromJson(json['directedbyId']), movieId = nativeFromJson(json['movieId']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddDirectorToMovieDirectedByInsert otherTyped = + other as AddDirectorToMovieDirectedByInsert; + return directedbyId == otherTyped.directedbyId && + movieId == otherTyped.movieId; + } + + @override + int get hashCode => Object.hashAll([directedbyId.hashCode, movieId.hashCode]); Map toJson() { Map json = {}; @@ -59,11 +77,27 @@ class AddDirectorToMovieDirectedByInsert { }); } +@immutable class AddDirectorToMovieData { - AddDirectorToMovieDirectedByInsert directedBy_insert; + final AddDirectorToMovieDirectedByInsert directedBy_insert; AddDirectorToMovieData.fromJson(dynamic json) : directedBy_insert = AddDirectorToMovieDirectedByInsert.fromJson( json['directedBy_insert']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddDirectorToMovieData otherTyped = other as AddDirectorToMovieData; + return directedBy_insert == otherTyped.directedBy_insert; + } + + @override + int get hashCode => directedBy_insert.hashCode; Map toJson() { Map json = {}; @@ -76,10 +110,27 @@ class AddDirectorToMovieData { }); } +@immutable class AddDirectorToMovieVariablesPersonId { - String id; + final String id; AddDirectorToMovieVariablesPersonId.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddDirectorToMovieVariablesPersonId otherTyped = + other as AddDirectorToMovieVariablesPersonId; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -92,9 +143,10 @@ class AddDirectorToMovieVariablesPersonId { }); } +@immutable class AddDirectorToMovieVariables { - late Optional personId; - late Optional movieId; + late final Optional personId; + late final Optional movieId; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') AddDirectorToMovieVariables.fromJson(Map json) { @@ -109,6 +161,22 @@ class AddDirectorToMovieVariables { ? null : nativeFromJson(json['movieId']); } + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddDirectorToMovieVariables otherTyped = + other as AddDirectorToMovieVariables; + return personId == otherTyped.personId && movieId == otherTyped.movieId; + } + + @override + int get hashCode => Object.hashAll([personId.hashCode, movieId.hashCode]); Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart index ecd924a47ba0..359cfb32768c 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_person.dart @@ -29,10 +29,26 @@ class AddPersonVariablesBuilder { } } +@immutable class AddPersonPersonInsert { - String id; + final String id; AddPersonPersonInsert.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddPersonPersonInsert otherTyped = other as AddPersonPersonInsert; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -45,10 +61,26 @@ class AddPersonPersonInsert { }); } +@immutable class AddPersonData { - AddPersonPersonInsert person_insert; + final AddPersonPersonInsert person_insert; AddPersonData.fromJson(dynamic json) : person_insert = AddPersonPersonInsert.fromJson(json['person_insert']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddPersonData otherTyped = other as AddPersonData; + return person_insert == otherTyped.person_insert; + } + + @override + int get hashCode => person_insert.hashCode; Map toJson() { Map json = {}; @@ -61,8 +93,9 @@ class AddPersonData { }); } +@immutable class AddPersonVariables { - late Optional name; + late final Optional name; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') AddPersonVariables.fromJson(Map json) { @@ -70,6 +103,21 @@ class AddPersonVariables { name.value = json['name'] == null ? null : nativeFromJson(json['name']); } + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddPersonVariables otherTyped = other as AddPersonVariables; + return name == otherTyped.name; + } + + @override + int get hashCode => name.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart index 67d2a26649b4..19ce28607185 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/add_timestamp.dart @@ -25,10 +25,27 @@ class AddTimestampVariablesBuilder { } } +@immutable class AddTimestampTimestampHolderInsert { - String id; + final String id; AddTimestampTimestampHolderInsert.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddTimestampTimestampHolderInsert otherTyped = + other as AddTimestampTimestampHolderInsert; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -41,11 +58,27 @@ class AddTimestampTimestampHolderInsert { }); } +@immutable class AddTimestampData { - AddTimestampTimestampHolderInsert timestampHolder_insert; + final AddTimestampTimestampHolderInsert timestampHolder_insert; AddTimestampData.fromJson(dynamic json) : timestampHolder_insert = AddTimestampTimestampHolderInsert.fromJson( json['timestampHolder_insert']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddTimestampData otherTyped = other as AddTimestampData; + return timestampHolder_insert == otherTyped.timestampHolder_insert; + } + + @override + int get hashCode => timestampHolder_insert.hashCode; Map toJson() { Map json = {}; @@ -58,12 +91,28 @@ class AddTimestampData { }); } +@immutable class AddTimestampVariables { - Timestamp timestamp; + final Timestamp timestamp; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') AddTimestampVariables.fromJson(Map json) : timestamp = Timestamp.fromJson(json['timestamp']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final AddTimestampVariables otherTyped = other as AddTimestampVariables; + return timestamp == otherTyped.timestamp; + } + + @override + int get hashCode => timestamp.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart index 33d6cfcac2bd..14ba5ce8a7a0 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/create_movie.dart @@ -46,10 +46,26 @@ class CreateMovieVariablesBuilder { } } +@immutable class CreateMovieMovieInsert { - String id; + final String id; CreateMovieMovieInsert.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final CreateMovieMovieInsert otherTyped = other as CreateMovieMovieInsert; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -62,10 +78,26 @@ class CreateMovieMovieInsert { }); } +@immutable class CreateMovieData { - CreateMovieMovieInsert movie_insert; + final CreateMovieMovieInsert movie_insert; CreateMovieData.fromJson(dynamic json) : movie_insert = CreateMovieMovieInsert.fromJson(json['movie_insert']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final CreateMovieData otherTyped = other as CreateMovieData; + return movie_insert == otherTyped.movie_insert; + } + + @override + int get hashCode => movie_insert.hashCode; Map toJson() { Map json = {}; @@ -78,12 +110,13 @@ class CreateMovieData { }); } +@immutable class CreateMovieVariables { - String title; - int releaseYear; - String genre; - late Optional rating; - late Optional description; + final String title; + final int releaseYear; + final String genre; + late final Optional rating; + late final Optional description; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') CreateMovieVariables.fromJson(Map json) @@ -99,6 +132,31 @@ class CreateMovieVariables { ? null : nativeFromJson(json['description']); } + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final CreateMovieVariables otherTyped = other as CreateMovieVariables; + return title == otherTyped.title && + releaseYear == otherTyped.releaseYear && + genre == otherTyped.genre && + rating == otherTyped.rating && + description == otherTyped.description; + } + + @override + int get hashCode => Object.hashAll([ + title.hashCode, + releaseYear.hashCode, + genre.hashCode, + rating.hashCode, + description.hashCode + ]); Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart index b5cfa8ecd3b1..9050d5c5d707 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/delete_movie.dart @@ -25,10 +25,26 @@ class DeleteMovieVariablesBuilder { } } +@immutable class DeleteMovieMovieDelete { - String id; + final String id; DeleteMovieMovieDelete.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final DeleteMovieMovieDelete otherTyped = other as DeleteMovieMovieDelete; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -41,12 +57,28 @@ class DeleteMovieMovieDelete { }); } +@immutable class DeleteMovieData { - DeleteMovieMovieDelete? movie_delete; + final DeleteMovieMovieDelete? movie_delete; DeleteMovieData.fromJson(dynamic json) : movie_delete = json['movie_delete'] == null ? null : DeleteMovieMovieDelete.fromJson(json['movie_delete']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final DeleteMovieData otherTyped = other as DeleteMovieData; + return movie_delete == otherTyped.movie_delete; + } + + @override + int get hashCode => movie_delete.hashCode; Map toJson() { Map json = {}; @@ -61,12 +93,28 @@ class DeleteMovieData { }); } +@immutable class DeleteMovieVariables { - String id; + final String id; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') DeleteMovieVariables.fromJson(Map json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final DeleteMovieVariables otherTyped = other as DeleteMovieVariables; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/get_movie.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/get_movie.dart index 5e09901aef92..4fe64439c88a 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/get_movie.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/get_movie.dart @@ -25,12 +25,28 @@ class GetMovieVariablesBuilder { } } +@immutable class GetMovieMovie { - String id; - String title; + final String id; + final String title; GetMovieMovie.fromJson(dynamic json) : id = nativeFromJson(json['id']), title = nativeFromJson(json['title']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final GetMovieMovie otherTyped = other as GetMovieMovie; + return id == otherTyped.id && title == otherTyped.title; + } + + @override + int get hashCode => Object.hashAll([id.hashCode, title.hashCode]); Map toJson() { Map json = {}; @@ -45,12 +61,28 @@ class GetMovieMovie { }); } +@immutable class GetMovieData { - GetMovieMovie? movie; + final GetMovieMovie? movie; GetMovieData.fromJson(dynamic json) : movie = json['movie'] == null ? null : GetMovieMovie.fromJson(json['movie']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final GetMovieData otherTyped = other as GetMovieData; + return movie == otherTyped.movie; + } + + @override + int get hashCode => movie.hashCode; Map toJson() { Map json = {}; @@ -65,10 +97,26 @@ class GetMovieData { }); } +@immutable class GetMovieVariablesKey { - String id; + final String id; GetMovieVariablesKey.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final GetMovieVariablesKey otherTyped = other as GetMovieVariablesKey; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -81,12 +129,28 @@ class GetMovieVariablesKey { }); } +@immutable class GetMovieVariables { - GetMovieVariablesKey key; + final GetMovieVariablesKey key; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') GetMovieVariables.fromJson(Map json) : key = GetMovieVariablesKey.fromJson(json['key']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final GetMovieVariables otherTyped = other as GetMovieVariables; + return key == otherTyped.key; + } + + @override + int get hashCode => key.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart index 3647007b8328..7b9faaeb08dd 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies.dart @@ -18,11 +18,12 @@ class ListMoviesVariablesBuilder { } } +@immutable class ListMoviesMovies { - String id; - String title; - List directed_by; - double? rating; + final String id; + final String title; + final List directed_by; + final double? rating; ListMoviesMovies.fromJson(dynamic json) : id = nativeFromJson(json['id']), title = nativeFromJson(json['title']), @@ -32,6 +33,25 @@ class ListMoviesMovies { rating = json['rating'] == null ? null : nativeFromJson(json['rating']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListMoviesMovies otherTyped = other as ListMoviesMovies; + return id == otherTyped.id && + title == otherTyped.title && + directed_by == otherTyped.directed_by && + rating == otherTyped.rating; + } + + @override + int get hashCode => Object.hashAll( + [id.hashCode, title.hashCode, directed_by.hashCode, rating.hashCode]); Map toJson() { Map json = {}; @@ -52,10 +72,27 @@ class ListMoviesMovies { }); } +@immutable class ListMoviesMoviesDirectedBy { - String name; + final String name; ListMoviesMoviesDirectedBy.fromJson(dynamic json) : name = nativeFromJson(json['name']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListMoviesMoviesDirectedBy otherTyped = + other as ListMoviesMoviesDirectedBy; + return name == otherTyped.name; + } + + @override + int get hashCode => name.hashCode; Map toJson() { Map json = {}; @@ -68,12 +105,28 @@ class ListMoviesMoviesDirectedBy { }); } +@immutable class ListMoviesData { - List movies; + final List movies; ListMoviesData.fromJson(dynamic json) : movies = (json['movies'] as List) .map((e) => ListMoviesMovies.fromJson(e)) .toList(); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListMoviesData otherTyped = other as ListMoviesData; + return movies == otherTyped.movies; + } + + @override + int get hashCode => movies.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart index c4f013c77dc3..bc895bf95238 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_movies_by_partial_title.dart @@ -28,11 +28,12 @@ class ListMoviesByPartialTitleVariablesBuilder { } } +@immutable class ListMoviesByPartialTitleMovies { - String id; - String title; - String genre; - double? rating; + final String id; + final String title; + final String genre; + final double? rating; ListMoviesByPartialTitleMovies.fromJson(dynamic json) : id = nativeFromJson(json['id']), title = nativeFromJson(json['title']), @@ -40,6 +41,26 @@ class ListMoviesByPartialTitleMovies { rating = json['rating'] == null ? null : nativeFromJson(json['rating']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListMoviesByPartialTitleMovies otherTyped = + other as ListMoviesByPartialTitleMovies; + return id == otherTyped.id && + title == otherTyped.title && + genre == otherTyped.genre && + rating == otherTyped.rating; + } + + @override + int get hashCode => Object.hashAll( + [id.hashCode, title.hashCode, genre.hashCode, rating.hashCode]); Map toJson() { Map json = {}; @@ -60,12 +81,29 @@ class ListMoviesByPartialTitleMovies { }); } +@immutable class ListMoviesByPartialTitleData { - List movies; + final List movies; ListMoviesByPartialTitleData.fromJson(dynamic json) : movies = (json['movies'] as List) .map((e) => ListMoviesByPartialTitleMovies.fromJson(e)) .toList(); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListMoviesByPartialTitleData otherTyped = + other as ListMoviesByPartialTitleData; + return movies == otherTyped.movies; + } + + @override + int get hashCode => movies.hashCode; Map toJson() { Map json = {}; @@ -78,12 +116,29 @@ class ListMoviesByPartialTitleData { }); } +@immutable class ListMoviesByPartialTitleVariables { - String input; + final String input; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') ListMoviesByPartialTitleVariables.fromJson(Map json) : input = nativeFromJson(json['input']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListMoviesByPartialTitleVariables otherTyped = + other as ListMoviesByPartialTitleVariables; + return input == otherTyped.input; + } + + @override + int get hashCode => input.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart index 5bdb83d6f7f0..a8629c79585a 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_persons.dart @@ -18,12 +18,28 @@ class ListPersonsVariablesBuilder { } } +@immutable class ListPersonsPeople { - String id; - String name; + final String id; + final String name; ListPersonsPeople.fromJson(dynamic json) : id = nativeFromJson(json['id']), name = nativeFromJson(json['name']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListPersonsPeople otherTyped = other as ListPersonsPeople; + return id == otherTyped.id && name == otherTyped.name; + } + + @override + int get hashCode => Object.hashAll([id.hashCode, name.hashCode]); Map toJson() { Map json = {}; @@ -38,12 +54,28 @@ class ListPersonsPeople { }); } +@immutable class ListPersonsData { - List people; + final List people; ListPersonsData.fromJson(dynamic json) : people = (json['people'] as List) .map((e) => ListPersonsPeople.fromJson(e)) .toList(); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListPersonsData otherTyped = other as ListPersonsData; + return people == otherTyped.people; + } + + @override + int get hashCode => people.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart index 4d6c7d6f1d5a..3f63233fae95 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_thing.dart @@ -30,10 +30,26 @@ class ListThingVariablesBuilder { } } +@immutable class ListThingThings { - AnyValue title; + final AnyValue title; ListThingThings.fromJson(dynamic json) : title = AnyValue.fromJson(json['title']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListThingThings otherTyped = other as ListThingThings; + return title == otherTyped.title; + } + + @override + int get hashCode => title.hashCode; Map toJson() { Map json = {}; @@ -46,12 +62,28 @@ class ListThingThings { }); } +@immutable class ListThingData { - List things; + final List things; ListThingData.fromJson(dynamic json) : things = (json['things'] as List) .map((e) => ListThingThings.fromJson(e)) .toList(); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListThingData otherTyped = other as ListThingData; + return things == otherTyped.things; + } + + @override + int get hashCode => things.hashCode; Map toJson() { Map json = {}; @@ -64,14 +96,30 @@ class ListThingData { }); } +@immutable class ListThingVariables { - late Optional data; + late final Optional data; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') ListThingVariables.fromJson(Map json) { data = Optional.optional(AnyValue.fromJson, defaultSerializer); data.value = json['data'] == null ? null : AnyValue.fromJson(json['data']); } + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListThingVariables otherTyped = other as ListThingVariables; + return data == otherTyped.data; + } + + @override + int get hashCode => data.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart index 70bf3615d06f..f2b6b64ade80 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/list_timestamps.dart @@ -18,14 +18,31 @@ class ListTimestampsVariablesBuilder { } } +@immutable class ListTimestampsTimestampHolders { - Timestamp timestamp; - DateTime? date; + final Timestamp timestamp; + final DateTime? date; ListTimestampsTimestampHolders.fromJson(dynamic json) : timestamp = Timestamp.fromJson(json['timestamp']), date = json['date'] == null ? null : nativeFromJson(json['date']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListTimestampsTimestampHolders otherTyped = + other as ListTimestampsTimestampHolders; + return timestamp == otherTyped.timestamp && date == otherTyped.date; + } + + @override + int get hashCode => Object.hashAll([timestamp.hashCode, date.hashCode]); Map toJson() { Map json = {}; @@ -42,12 +59,28 @@ class ListTimestampsTimestampHolders { }); } +@immutable class ListTimestampsData { - List timestampHolders; + final List timestampHolders; ListTimestampsData.fromJson(dynamic json) : timestampHolders = (json['timestampHolders'] as List) .map((e) => ListTimestampsTimestampHolders.fromJson(e)) .toList(); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ListTimestampsData otherTyped = other as ListTimestampsData; + return timestampHolders == otherTyped.timestampHolders; + } + + @override + int get hashCode => timestampHolders.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart index ef27424855d7..7ad7e907c59a 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/movies.dart @@ -1,6 +1,7 @@ library movies; import 'package:firebase_data_connect/firebase_data_connect.dart'; +import 'package:flutter/foundation.dart'; import 'dart:convert'; part 'add_person.dart'; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart index 8efc6ed7863a..ac4ade4472e2 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_data.dart @@ -18,10 +18,26 @@ class SeedDataVariablesBuilder { } } +@immutable class SeedDataTheMatrix { - String id; + final String id; SeedDataTheMatrix.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final SeedDataTheMatrix otherTyped = other as SeedDataTheMatrix; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -34,10 +50,26 @@ class SeedDataTheMatrix { }); } +@immutable class SeedDataData { - SeedDataTheMatrix the_matrix; + final SeedDataTheMatrix the_matrix; SeedDataData.fromJson(dynamic json) : the_matrix = SeedDataTheMatrix.fromJson(json['the_matrix']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final SeedDataData otherTyped = other as SeedDataData; + return the_matrix == otherTyped.the_matrix; + } + + @override + int get hashCode => the_matrix.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart index 370d83681e44..2805c05d308e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/seed_movies.dart @@ -18,10 +18,26 @@ class SeedMoviesVariablesBuilder { } } +@immutable class SeedMoviesTheMatrix { - String id; + final String id; SeedMoviesTheMatrix.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final SeedMoviesTheMatrix otherTyped = other as SeedMoviesTheMatrix; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -34,10 +50,26 @@ class SeedMoviesTheMatrix { }); } +@immutable class SeedMoviesJurassicPark { - String id; + final String id; SeedMoviesJurassicPark.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final SeedMoviesJurassicPark otherTyped = other as SeedMoviesJurassicPark; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -50,12 +82,30 @@ class SeedMoviesJurassicPark { }); } +@immutable class SeedMoviesData { - SeedMoviesTheMatrix the_matrix; - SeedMoviesJurassicPark jurassic_park; + final SeedMoviesTheMatrix the_matrix; + final SeedMoviesJurassicPark jurassic_park; SeedMoviesData.fromJson(dynamic json) : the_matrix = SeedMoviesTheMatrix.fromJson(json['the_matrix']), jurassic_park = SeedMoviesJurassicPark.fromJson(json['jurassic_park']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final SeedMoviesData otherTyped = other as SeedMoviesData; + return the_matrix == otherTyped.the_matrix && + jurassic_park == otherTyped.jurassic_park; + } + + @override + int get hashCode => + Object.hashAll([the_matrix.hashCode, jurassic_park.hashCode]); Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart index 49230260b438..167d997a25e5 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/generated/thing.dart @@ -30,9 +30,25 @@ class ThingVariablesBuilder { } } +@immutable class ThingAbc { - String id; + final String id; ThingAbc.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ThingAbc otherTyped = other as ThingAbc; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -45,9 +61,25 @@ class ThingAbc { }); } +@immutable class ThingDef { - String id; + final String id; ThingDef.fromJson(dynamic json) : id = nativeFromJson(json['id']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ThingDef otherTyped = other as ThingDef; + return id == otherTyped.id; + } + + @override + int get hashCode => id.hashCode; Map toJson() { Map json = {}; @@ -60,12 +92,28 @@ class ThingDef { }); } +@immutable class ThingData { - ThingAbc abc; - ThingDef def; + final ThingAbc abc; + final ThingDef def; ThingData.fromJson(dynamic json) : abc = ThingAbc.fromJson(json['abc']), def = ThingDef.fromJson(json['def']); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ThingData otherTyped = other as ThingData; + return abc == otherTyped.abc && def == otherTyped.def; + } + + @override + int get hashCode => Object.hashAll([abc.hashCode, def.hashCode]); Map toJson() { Map json = {}; @@ -80,8 +128,9 @@ class ThingData { }); } +@immutable class ThingVariables { - late Optional title; + late final Optional title; @Deprecated( 'fromJson is deprecated for Variable classes as they are no longer required for deserialization.') ThingVariables.fromJson(Map json) { @@ -89,6 +138,21 @@ class ThingVariables { title.value = json['title'] == null ? null : AnyValue.fromJson(json['title']); } + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + final ThingVariables otherTyped = other as ThingVariables; + return title == otherTyped.title; + } + + @override + int get hashCode => title.hashCode; Map toJson() { Map json = {}; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart b/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart index da63eec3afbd..98f684749ffa 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/lib/main.dart @@ -248,7 +248,7 @@ class _DataConnectWidgetState extends State { ])); } - _showError(String message) { + void _showError(String message) { showDialog( context: context, builder: (context) { diff --git a/packages/firebase_data_connect/firebase_data_connect/example/macos/Podfile b/packages/firebase_data_connect/firebase_data_connect/example/macos/Podfile index c795730db8ed..b52666a10389 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/macos/Podfile +++ b/packages/firebase_data_connect/firebase_data_connect/example/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.14' +platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/project.pbxproj b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/project.pbxproj index ac658b11a81e..b8abb5878e44 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,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 */; }; + 5A0EBAC5CA5020E53D9F5FB9 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68259C80087E2CE600CA9E3E /* Pods_RunnerTests.framework */; }; + CDE09EBC5F87EFADA38F8F59 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F81E46173A5FB7C231B32AE1 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -76,8 +78,16 @@ 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 = ""; }; + 367BE93E3B25556D72E19BE6 /* 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 = ""; }; + 57638D81F785C506C897EA30 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 58F3E215A6EE6C70A757F8D4 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 603D07CF30CE7F11FFBF6764 /* 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 = ""; }; + 68259C80087E2CE600CA9E3E /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 704B294FA8C9834EEDCCF961 /* 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 = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D093DBBD5E141E58AB2CE079 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + F81E46173A5FB7C231B32AE1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,6 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5A0EBAC5CA5020E53D9F5FB9 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,6 +103,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CDE09EBC5F87EFADA38F8F59 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -125,6 +137,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + FB0AE6DB54A0C7F6C5B5F8B5 /* Pods */, ); sourceTree = ""; }; @@ -175,10 +188,26 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + F81E46173A5FB7C231B32AE1 /* Pods_Runner.framework */, + 68259C80087E2CE600CA9E3E /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; }; + FB0AE6DB54A0C7F6C5B5F8B5 /* Pods */ = { + isa = PBXGroup; + children = ( + 704B294FA8C9834EEDCCF961 /* Pods-Runner.debug.xcconfig */, + 367BE93E3B25556D72E19BE6 /* Pods-Runner.release.xcconfig */, + 603D07CF30CE7F11FFBF6764 /* Pods-Runner.profile.xcconfig */, + D093DBBD5E141E58AB2CE079 /* Pods-RunnerTests.debug.xcconfig */, + 57638D81F785C506C897EA30 /* Pods-RunnerTests.release.xcconfig */, + 58F3E215A6EE6C70A757F8D4 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -186,6 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 30E7763C8BF85951A4034B2F /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -204,11 +234,14 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 92B2B246524EF31F3D357243 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 9D22A98DF478583AFE4C87D0 /* [CP] Embed Pods Frameworks */, + 7D0A7AE5F7B788418083225F /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -291,6 +324,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 30E7763C8BF85951A4034B2F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -329,6 +384,62 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 7D0A7AE5F7B788418083225F /* [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; + }; + 92B2B246524EF31F3D357243 /* [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; + }; + 9D22A98DF478583AFE4C87D0 /* [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 */ @@ -380,6 +491,7 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D093DBBD5E141E58AB2CE079 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -394,6 +506,7 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 57638D81F785C506C897EA30 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -408,6 +521,7 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 58F3E215A6EE6C70A757F8D4 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -461,7 +575,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -543,7 +657,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -593,7 +707,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 15368eccb25a..ac78810cdd2c 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -59,6 +59,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16ed0f..21a3cc14c74e 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner/AppDelegate.swift b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner/AppDelegate.swift index d53ef6437726..b3c176141221 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner/AppDelegate.swift +++ b/packages/firebase_data_connect/firebase_data_connect/example/macos/Runner/AppDelegate.swift @@ -1,9 +1,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/firebase_data_connect/firebase_data_connect/example/pubspec.yaml b/packages/firebase_data_connect/firebase_data_connect/example/pubspec.yaml index fc91e9c9f3b9..637a411ad07f 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/pubspec.yaml +++ b/packages/firebase_data_connect/firebase_data_connect/example/pubspec.yaml @@ -4,23 +4,25 @@ description: 'Firebase Data Connect example app' publish_to: 'none' version: 1.0.0+1 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: flutter: sdk: flutter - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 google_sign_in: ^6.1.0 - firebase_auth: ^6.3.0 + firebase_auth: ^6.5.4 firebase_data_connect: path: ../ cupertino_icons: ^1.0.6 flutter_rating_bar: ^4.0.1 protobuf: ^3.1.0 - firebase_app_check: ^0.4.2 + firebase_app_check: ^0.4.5 dev_dependencies: build_runner: ^2.3.3 @@ -28,9 +30,11 @@ dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 + flutter_lints: ^6.0.0 integration_test: sdk: flutter flutter: uses-material-design: true + config: + enable-swift-package-manager: false diff --git a/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh b/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh index 3821f46e402a..ddf9dde87cf0 100755 --- a/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh +++ b/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh @@ -1,5 +1,44 @@ #!/bin/bash -firebase emulators:start --project flutterfire-e2e-tests & -# Added below to fix the e2e tests -# npx "firebase/firebase-tools#mtewani/dart-bugbash" emulators:start --project flutterfire-e2e-tests & -sleep 30 \ No newline at end of file +set -euo pipefail + +LOG_FILE="${TMPDIR:-/tmp}/flutterfire-fdc-emulators.log" +rm -f "$LOG_FILE" + +print_emulator_logs() { + cat "$LOG_FILE" + + if [ -f firebase-debug.log ]; then + echo + echo "firebase-debug.log:" + cat firebase-debug.log + fi + + if [ -f dataconnect-debug.log ]; then + echo + echo "dataconnect-debug.log:" + cat dataconnect-debug.log + fi +} + +firebase emulators:start --project flutterfire-e2e-tests >"$LOG_FILE" 2>&1 & +FIREBASE_PID=$! + +for _ in {1..90}; do + if ! kill -0 "$FIREBASE_PID" 2>/dev/null; then + echo "Firebase emulators exited before becoming ready." + print_emulator_logs + wait "$FIREBASE_PID" + exit 1 + fi + + if grep -q "All emulators ready" "$LOG_FILE"; then + print_emulator_logs + exit 0 + fi + + sleep 1 +done + +echo "Timed out waiting for Firebase emulators to become ready." +print_emulator_logs +exit 1 diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache.dart index 3983df6c5842..6b26001a8ac3 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache.dart @@ -51,7 +51,7 @@ class Cache { String _constructCacheIdentifier() { final rawPrefix = - '${_settings.storage}-${dataConnect.app.options.projectId}-${dataConnect.app.name}-${dataConnect.connectorConfig.serviceId}-${dataConnect.connectorConfig.connector}-${dataConnect.connectorConfig.location}-${dataConnect.transport.transportOptions.host}'; + '${_settings.storage}-${dataConnect.app.options.projectId}-${dataConnect.app.name}-${dataConnect.connectorConfig.serviceId}-${dataConnect.connectorConfig.connector}-${dataConnect.connectorConfig.location}-${dataConnect.transport?.transportOptions.host}'; final prefixSha = convertToSha256(rawPrefix); final rawSuffix = dataConnect.auth?.currentUser?.uid ?? 'anon'; final suffixSha = convertToSha256(rawSuffix); diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache_data_types.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache_data_types.dart index 0768da15232c..9b991a946a79 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache_data_types.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/cache/cache_data_types.dart @@ -332,11 +332,11 @@ class EntityNode { srcListMap.forEach((key, value) { List enodeList = []; List jsonList = value as List; - jsonList.forEach((jsonObj) { + for (var jsonObj in jsonList) { Map jmap = jsonObj as Map; EntityNode en = EntityNode.fromJson(jmap, cacheProvider); enodeList.add(en); - }); + } objLists?[key] = enodeList; }); } @@ -367,9 +367,9 @@ class EntityNode { if (nestedObjectLists != null) { nestedObjectLists!.forEach((key, edoList) { List> jsonList = []; - edoList.forEach((edo) { + for (var edo in edoList) { jsonList.add(edo.toJson(mode: mode)); - }); + } jsonData[key] = jsonList; }); } @@ -396,9 +396,9 @@ class EntityNode { Map nestedObjectListsJson = {}; nestedObjectLists!.forEach((key, edoList) { List> jsonList = []; - edoList.forEach((edo) { + for (var edo in edoList) { jsonList.add(edo.toJson(mode: mode)); - }); + } nestedObjectListsJson[key] = jsonList; }); jsonData[listsKey] = nestedObjectListsJson; diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/common/common_library.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/common/common_library.dart index 9247287f5adf..df7b405ab788 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/common/common_library.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/common/common_library.dart @@ -102,6 +102,7 @@ abstract class DataConnectTransport { /// Invokes corresponding query endpoint. Future invokeQuery( + String operationId, String queryName, Deserializer deserializer, Serializer serializer, @@ -111,6 +112,17 @@ abstract class DataConnectTransport { /// Invokes corresponding mutation endpoint. Future invokeMutation( + String operationId, + String queryName, + Deserializer deserializer, + Serializer serializer, + Variables? vars, + String? token, + ); + + /// Invokes corresponding stream query endpoint. + Stream invokeStreamQuery( + String operationId, String queryName, Deserializer deserializer, Serializer serializer, diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/common/dataconnect_error.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/common/dataconnect_error.dart index 43b7fd964418..32641baafa85 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/common/dataconnect_error.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/common/dataconnect_error.dart @@ -36,9 +36,7 @@ class DataConnectError extends FirebaseException { /// Error thrown when an operation is partially successful. class DataConnectOperationError extends DataConnectError { - DataConnectOperationError( - DataConnectErrorCode code, String message, this.response) - : super(code, message); + DataConnectOperationError(super.code, super.message, this.response); final DataConnectOperationFailureResponse response; } diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart index 5b359f6a15b3..e118e439e7cc 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/core/ref.dart @@ -51,17 +51,51 @@ abstract class OperationRef { this.serializer, this.variables, ); - Variables? variables; - String operationName; - DataConnectTransport _transport; - Deserializer deserializer; - Serializer serializer; + final Variables? variables; + final String operationName; + final DataConnectTransport _transport; + final Deserializer deserializer; + final Serializer serializer; String? _lastToken; - FirebaseDataConnect dataConnect; + final FirebaseDataConnect dataConnect; + + late final String operationId = + createOperationId(operationName, variables, serializer); - Future> execute( - {QueryFetchPolicy fetchPolicy = QueryFetchPolicy.preferCache}); + static dynamic _sortKeys(dynamic value) { + if (value is Map) { + final sortedMap = {}; + final sortedKeys = value.keys.toList()..sort(); + for (final key in sortedKeys) { + sortedMap[key.toString()] = _sortKeys(value[key]); + } + return sortedMap; + } else if (value is List) { + return value.map(_sortKeys).toList(); + } + return value; + } + + static String createOperationId(String operationName, + Variables? vars, Serializer? serializer) { + if (vars != null && serializer != null) { + try { + final decoded = jsonDecode(serializer(vars)); + final sortedStr = jsonEncode(_sortKeys(decoded)); + final hashVars = convertToSha256(sortedStr); + return '$operationName::$hashVars'; + } catch (_) { + final rawVars = serializer(vars); + final hashVars = convertToSha256(rawVars); + return '$operationName::$hashVars'; + } + } else { + return operationName; + } + } + + Future> execute(); Future _shouldRetry() async { String? newToken; @@ -152,7 +186,7 @@ class QueryManager { try { await queryRef.execute(fetchPolicy: QueryFetchPolicy.cacheOnly); } catch (e) { - log('Error executing impacted query $e'); + log('Error executing impacted query $queryId $e'); } } } @@ -175,24 +209,20 @@ class QueryManager { StreamController> addQuery( QueryRef ref, ) { - final queryId = ref._queryId; + final queryId = ref.operationId; trackedQueries[queryId] = ref; final streamController = - StreamController>.broadcast(); + StreamController>.broadcast( + onCancel: () { + trackedQueries.remove(queryId); + ref._onAllSubscribersCancelled(); + }, + ); return streamController; } - static String createQueryId(String queryName, - QueryVariables? vars, Serializer varSerializer) { - if (vars != null) { - return '$queryName::${varSerializer(vars)}'; - } else { - return queryName; - } - } - void dispose() { _impactedQueriesSubscription?.cancel(); } @@ -216,7 +246,7 @@ class QueryRef extends OperationRef { variables, ); - QueryManager _queryManager; + final QueryManager _queryManager; @override Future> execute( @@ -239,9 +269,6 @@ class QueryRef extends OperationRef { } } - String get _queryId => - QueryManager.createQueryId(operationName, variables, serializer); - Future> _executeFromCache( QueryFetchPolicy fetchPolicy) async { if (dataConnect.cacheManager == null) { @@ -251,7 +278,7 @@ class QueryRef extends OperationRef { final cacheManager = dataConnect.cacheManager!; bool allowStale = fetchPolicy == QueryFetchPolicy.cacheOnly; //if its cache only, we always allow stale - final cachedData = await cacheManager.resultTree(_queryId, allowStale); + final cachedData = await cacheManager.resultTree(operationId, allowStale); if (cachedData != null) { try { @@ -280,6 +307,7 @@ class QueryRef extends OperationRef { try { ServerResponse serverResponse = await _transport.invokeQuery( + operationId, operationName, deserializer, serializer, @@ -288,7 +316,7 @@ class QueryRef extends OperationRef { ); if (dataConnect.cacheManager != null) { - await dataConnect.cacheManager!.update(_queryId, serverResponse); + await dataConnect.cacheManager!.update(operationId, serverResponse); } Data typedData = _convertBodyJsonToData(serverResponse.data); @@ -307,13 +335,95 @@ class QueryRef extends OperationRef { } StreamController>? _streamController; + Stream? _serverStream; + StreamSubscription? _serverStreamSubscription; + + void _onAllSubscribersCancelled() { + _serverStreamSubscription?.cancel(); + _serverStreamSubscription = null; + _serverStream = null; + } Stream> subscribe() { _streamController ??= _queryManager.addQuery(this); - execute(); + final stream = + _streamController!.stream.cast>(); + + // Return the stream to the caller, then execute fetches + Future.microtask(() async { + if (dataConnect.cacheManager != null) { + try { + await _executeFromCache(QueryFetchPolicy.cacheOnly); + } catch (err) { + log("Error fetching from cache during subscribe $err"); + // Ignore cache misses here, server stream will provide latest data + } + } + + // Initiate Web Socket stream only if not already streaming + if (_serverStream == null) { + _streamFromServer(); + } + }); + + return stream; + } + + void _streamFromServer() async { + bool shouldRetry = await _shouldRetry(); + try { + _serverStream = _transport.invokeStreamQuery( + operationId, + operationName, + deserializer, + serializer, + variables, + _lastToken, + ); - return _streamController!.stream.cast>(); + _serverStreamSubscription = _serverStream!.listen( + (serverResponse) async { + if (dataConnect.cacheManager != null) { + try { + await dataConnect.cacheManager! + .update(operationId, serverResponse); + } catch (e) { + log("QueryRef $operationId _streamFromServer loop cache update failed: $e"); + } + } + Data typedData = _convertBodyJsonToData(serverResponse.data); + + QueryResult res = + QueryResult(dataConnect, typedData, DataSource.server, this); + publishResultToStream(res); + }, + onError: (e) { + _serverStreamSubscription?.cancel(); + _serverStreamSubscription = null; + _serverStream = null; + + if (shouldRetry && + e is DataConnectError && + e.code == DataConnectErrorCode.unauthorized.toString()) { + _streamFromServer(); + } else { + publishErrorToStream(e); + } + }, + onDone: () { + _serverStreamSubscription?.cancel(); + _serverStreamSubscription = null; + _serverStream = null; + }, + ); + } catch (e) { + _serverStreamSubscription?.cancel(); + _serverStreamSubscription = null; + _serverStream = null; + log("QueryRef $operationId _streamFromServer loop Unknown loop failure: $e"); + publishErrorToStream(e); + } } void publishResultToStream(QueryResult result) { @@ -322,7 +432,7 @@ class QueryRef extends OperationRef { } } - void publishErrorToStream(Error err) { + void publishErrorToStream(Object err) { if (_streamController != null) { _streamController?.addError(err); } @@ -331,24 +441,16 @@ class QueryRef extends OperationRef { class MutationRef extends OperationRef { MutationRef( - FirebaseDataConnect dataConnect, - String operationName, - DataConnectTransport transport, - Deserializer deserializer, - Serializer serializer, - Variables? variables, - ) : super( - dataConnect, - operationName, - transport, - deserializer, - serializer, - variables, - ); + super.dataConnect, + super.operationName, + super.transport, + super.deserializer, + super.serializer, + super.variables, + ); @override - Future> execute( - {QueryFetchPolicy fetchPolicy = QueryFetchPolicy.serverOnly}) async { + Future> execute() async { bool shouldRetry = await _shouldRetry(); try { // Logic below is duplicated due to the fact that `executeOperation` returns @@ -370,6 +472,7 @@ class MutationRef extends OperationRef { ) async { ServerResponse serverResponse = await _transport.invokeMutation( + operationId, operationName, deserializer, serializer, diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/dataconnect_version.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/dataconnect_version.dart index a62e6b0ea102..e5a8a4417906 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/dataconnect_version.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/dataconnect_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// version number for the package, should be align with pubspec.yaml. -const packageVersion = '0.2.4'; +const packageVersion = '0.3.0+5'; diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/firebase_data_connect.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/firebase_data_connect.dart index d5813f94dc89..165b6f5d62fb 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/firebase_data_connect.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/firebase_data_connect.dart @@ -20,22 +20,24 @@ import 'package:firebase_data_connect/src/common/common_library.dart'; import 'package:firebase_data_connect/src/core/ref.dart'; import 'package:flutter/foundation.dart'; -import './network/transport_library.dart' - if (dart.library.io) './network/grpc_library.dart' - if (dart.library.js_interop) './network/rest_library.dart' - if (dart.library.html) './network/rest_library.dart'; +import './network/rest_library.dart'; +import './network/transport_library.dart'; import 'cache/cache_data_types.dart'; import 'cache/cache.dart'; /// DataConnect class -class FirebaseDataConnect extends FirebasePluginPlatform { +class FirebaseDataConnect extends FirebasePlugin { /// Constructor for initializing Data Connect @visibleForTesting FirebaseDataConnect( {required this.app, required this.connectorConfig, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') this.auth, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') this.appCheck, CallerSDKType? sdkType, this.cacheSettings}) @@ -67,9 +69,9 @@ class FirebaseDataConnect extends FirebasePluginPlatform { /// FirebaseAppCheck FirebaseAppCheck? appCheck; - /// Due to compatibility issues with grpc-web, we swap out the transport based on what platform the user is using. - /// For web, we use RestTransport. For mobile, we use GRPCTransport. - late DataConnectTransport transport; + /// Transport for connecting to the Data Connect service. + /// Routes between RestTransport and WebSocketTransport based on subscription status + DataConnectTransport? transport; /// FirebaseAuth FirebaseAuth? auth; @@ -91,15 +93,30 @@ class FirebaseDataConnect extends FirebasePluginPlatform { /// Checks whether the transport has been properly initialized. @visibleForTesting void checkTransport() { + if (transport != null) { + return; + } transportOptions ??= TransportOptions('firebasedataconnect.googleapis.com', null, true); - transport = getTransport( + auth ??= app.getService(); + appCheck ??= app.getService(); + + final rest = RestTransport( + transportOptions!, + options, + app.options.appId, + _sdkType, + appCheck, + ); + final ws = WebSocketTransport( transportOptions!, options, app.options.appId, _sdkType, appCheck, + auth, ); + transport = _RoutingTransport(rest, ws); } @visibleForTesting @@ -120,7 +137,7 @@ class FirebaseDataConnect extends FirebasePluginPlatform { checkTransport(); checkAndInitializeCache(); String queryId = - QueryManager.createQueryId(operationName, vars, varsSerializer); + OperationRef.createOperationId(operationName, vars, varsSerializer); QueryRef? ref = _queryManager.trackedQueries[queryId] as QueryRef?; @@ -130,7 +147,7 @@ class FirebaseDataConnect extends FirebasePluginPlatform { return QueryRef( this, operationName, - transport, + transport!, dataDeserializer, _queryManager, varsSerializer, @@ -147,10 +164,12 @@ class FirebaseDataConnect extends FirebasePluginPlatform { Variables? vars, ) { checkTransport(); + //initialize cache since mutations on a stream could result in subscribed query updates + checkAndInitializeCache(); return MutationRef( this, operationName, - transport, + transport!, dataDeserializer, varsSerializer, vars, @@ -167,11 +186,12 @@ class FirebaseDataConnect extends FirebasePluginPlatform { String mappedHost = automaticHostMapping ? getMappedHost(host) : host; transportOptions = TransportOptions(mappedHost, port, isSecure); - if (cacheManager != null) { - // dispose and clean this up. it will get reinitialized for newer QueryRefs that target the emulator. - cacheManager?.dispose(); - cacheManager = null; - } + // dispose and clean this up. it will get reinitialized for newer QueryRefs that target the emulator. + cacheManager?.dispose(); + cacheManager = null; + + // transport will get reinitialized for newer QueryRefs that target the emulator. + transport = null; } /// Currently cached DataConnect instances. Maps from app name to ConnectorConfigStr, DataConnect. @@ -185,7 +205,11 @@ class FirebaseDataConnect extends FirebasePluginPlatform { /// If pass in [appCheck], request session will get protected from abusing. static FirebaseDataConnect instanceFor( {FirebaseApp? app, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') FirebaseAuth? auth, + @Deprecated( + 'Passing an explicit instance is deprecated, internal handling is now automatic.') FirebaseAppCheck? appCheck, CallerSDKType? sdkType, required ConnectorConfig connectorConfig, @@ -199,16 +223,13 @@ class FirebaseDataConnect extends FirebasePluginPlatform { return cachedInstances[app.name]![connectorConfig.toJson()]!; } - //TODO remove after testing since CS should be null by default - final resolvedCacheSettings = cacheSettings ?? CacheSettings(); - FirebaseDataConnect newInstance = FirebaseDataConnect( app: app, auth: auth, appCheck: appCheck, connectorConfig: connectorConfig, sdkType: sdkType, - cacheSettings: resolvedCacheSettings, + cacheSettings: cacheSettings, ); if (cachedInstances[app.name] == null) { cachedInstances[app.name] = {}; @@ -218,3 +239,96 @@ class FirebaseDataConnect extends FirebasePluginPlatform { return newInstance; } } + +class _RoutingTransport implements DataConnectTransport { + _RoutingTransport(this.rest, this.websocket); + final RestTransport rest; + final WebSocketTransport websocket; + + @override + FirebaseAppCheck? get appCheck => rest.appCheck; + @override + set appCheck(FirebaseAppCheck? val) { + rest.appCheck = val; + websocket.appCheck = val; + } + + @override + CallerSDKType get sdkType => rest.sdkType; + @override + set sdkType(CallerSDKType val) { + rest.sdkType = val; + websocket.sdkType = val; + } + + @override + TransportOptions get transportOptions => rest.transportOptions; + @override + set transportOptions(TransportOptions val) { + rest.transportOptions = val; + websocket.transportOptions = val; + } + + @override + DataConnectOptions get options => rest.options; + @override + set options(DataConnectOptions val) { + rest.options = val; + websocket.options = val; + } + + @override + String get appId => rest.appId; + @override + set appId(String val) { + rest.appId = val; + websocket.appId = val; + } + + @override + Future invokeMutation( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? token, + ) { + if (websocket.isConnected) { + return websocket.invokeMutation( + operationId, queryName, deserializer, serializer, vars, token); + } + return rest.invokeMutation( + operationId, queryName, deserializer, serializer, vars, token); + } + + @override + Future invokeQuery( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serialize, + Variables? vars, + String? token, + ) { + if (websocket.isConnected) { + return websocket.invokeQuery( + operationId, queryName, deserializer, serialize, vars, token); + } + return rest.invokeQuery( + operationId, queryName, deserializer, serialize, vars, token); + } + + @override + Stream invokeStreamQuery( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? token, + ) { + return websocket.invokeStreamQuery( + operationId, queryName, deserializer, serializer, vars, token); + } +} diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/duration.pb.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/duration.pb.dart index 4bcbcd32a4c2..6c32fb50221c 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/duration.pb.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/duration.pb.dart @@ -1,3 +1,4 @@ +// ignore_for_file: implementation_imports // // Generated code. Do not modify. // source: google/protobuf/duration.proto diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/struct.pb.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/struct.pb.dart index 42d55e426602..42164fbc928f 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/struct.pb.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/generated/google/protobuf/struct.pb.dart @@ -1,3 +1,4 @@ +// ignore_for_file: implementation_imports // // Generated code. Do not modify. // source: google/protobuf/struct.proto diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_library.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_library.dart deleted file mode 100644 index d46b30815133..000000000000 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_library.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:convert'; -import 'dart:developer'; - -import 'package:firebase_app_check/firebase_app_check.dart'; -import 'package:firebase_data_connect/src/generated/graphql_error.pb.dart'; -import 'package:grpc/grpc.dart'; - -import '../common/common_library.dart'; -import '../dataconnect_version.dart'; -import '../generated/connector_service.pbgrpc.dart'; -import '../generated/google/protobuf/struct.pb.dart'; - -part 'grpc_transport.dart'; diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart deleted file mode 100644 index 180bb209168b..000000000000 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/grpc_transport.dart +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -part of 'grpc_library.dart'; - -/// Transport used for Android/iOS. Uses a GRPC transport instead of REST. -class GRPCTransport implements DataConnectTransport { - /// GRPCTransport creates a new channel - GRPCTransport( - this.transportOptions, - this.options, - this.appId, - this.sdkType, - this.appCheck, - ) { - bool isSecure = transportOptions.isSecure ?? true; - channel = ClientChannel( - transportOptions.host, - port: transportOptions.port ?? 443, - options: ChannelOptions( - credentials: (isSecure - ? const ChannelCredentials.secure() - : const ChannelCredentials.insecure()), - ), - ); - stub = ConnectorServiceClient(channel); - name = - 'projects/${options.projectId}/locations/${options.location}/services/${options.serviceId}/connectors/${options.connector}'; - } - - /// FirebaseAppCheck - @override - FirebaseAppCheck? appCheck; - - @override - CallerSDKType sdkType; - - /// Name of the endpoint. - late String name; - - /// ConnectorServiceClient used to execute the query/mutation. - late ConnectorServiceClient stub; - - /// ClientChannel used to configure connection to the GRPC server. - late ClientChannel channel; - - /// Current host configuration. - @override - TransportOptions transportOptions; - - /// Data Connect backend configuration options. - @override - DataConnectOptions options; - - /// Application ID - @override - String appId; - - Future> getMetadata(String? authToken) async { - String? appCheckToken; - try { - appCheckToken = await appCheck?.getToken(); - } catch (e) { - log('Unable to get app check token: $e'); - } - Map metadata = { - 'x-goog-request-params': 'location=${options.location}&frontend=data', - 'x-goog-api-client': getGoogApiVal(sdkType, packageVersion), - 'x-firebase-client': getFirebaseClientVal(packageVersion) - }; - - if (authToken != null) { - metadata['x-firebase-auth-token'] = authToken; - } - if (appCheckToken != null) { - metadata['X-Firebase-AppCheck'] = appCheckToken; - } - metadata['x-firebase-gmpid'] = appId; - return metadata; - } - - /// Invokes GPRC query endpoint. - @override - Future invokeQuery( - String queryName, - Deserializer deserializer, - Serializer? serializer, - Variables? vars, - String? authToken, - ) async { - ExecuteQueryResponse response; - - ExecuteQueryRequest request = - ExecuteQueryRequest(name: name, operationName: queryName); - if (vars != null && serializer != null) { - request.variables = getStruct(vars, serializer); - } - try { - response = await stub.executeQuery( - request, - options: CallOptions(metadata: await getMetadata(authToken)), - ); - return handleResponse( - CommonResponse.fromExecuteQuery(deserializer, response)); - } on Exception catch (e) { - if (e.toString().contains('invalid Firebase Auth Credentials')) { - throw DataConnectError( - DataConnectErrorCode.unauthorized, - 'Failed to invoke operation: $e', - ); - } - rethrow; - } - } - - /// Converts the variables into a proto Struct. - Struct getStruct( - Variables vars, - Serializer serializer, - ) { - Struct struct = Struct.create(); - struct.mergeFromProto3Json(jsonDecode(serializer(vars))); - return struct; - } - - /// Invokes GPRC mutation endpoint. - @override - Future invokeMutation( - String queryName, - Deserializer deserializer, - Serializer? serializer, - Variables? vars, - String? authToken, - ) async { - ExecuteMutationResponse response; - ExecuteMutationRequest request = - ExecuteMutationRequest(name: name, operationName: queryName); - if (vars != null && serializer != null) { - request.variables = getStruct(vars, serializer); - } - - try { - response = await stub.executeMutation( - request, - options: CallOptions(metadata: await getMetadata(authToken)), - ); - return handleResponse( - CommonResponse.fromExecuteMutation(deserializer, response)); - } on Exception catch (e) { - if (e.toString().contains('invalid Firebase Auth Credentials')) { - throw DataConnectError( - DataConnectErrorCode.unauthorized, - 'Failed to invoke operation: $e', - ); - } - rethrow; - } - } -} - -ServerResponse handleResponse(CommonResponse commonResponse) { - Map? jsond = commonResponse.data as Map?; - String jsonEncoded = jsonEncode(commonResponse.data); - - Map? jsonExt = - commonResponse.extensions as Map?; - - if (commonResponse.errors.isNotEmpty) { - Map? data = - jsonDecode(jsonEncoded) as Map?; - Data? decodedData; - List errors = commonResponse - .errors - .map((e) => DataConnectOperationFailureResponseErrorInfo( - e.path.values - .map((val) => val.hasStringValue() - ? DataConnectFieldPathSegment(val.stringValue) - : DataConnectListIndexPathSegment(val.numberValue.toInt())) - .toList(), - e.message)) - .toList(); - if (data != null) { - try { - decodedData = commonResponse.deserializer(jsonEncoded); - } catch (e) { - // nothing required - } - } - final response = - DataConnectOperationFailureResponse(errors, data, decodedData); - throw DataConnectOperationError(DataConnectErrorCode.other, - 'failed to invoke operation: ${response.errors}', response); - } - - // no errors - return a standard response - if (jsond != null) { - return ServerResponse(jsond, extensions: jsonExt); - } else { - return ServerResponse({}); - } -} - -/// Initializes GRPC transport for Data Connect. -DataConnectTransport getTransport( - TransportOptions transportOptions, - DataConnectOptions options, - String appId, - CallerSDKType sdkType, - FirebaseAppCheck? appCheck, -) => - GRPCTransport(transportOptions, options, appId, sdkType, appCheck); - -class CommonResponse { - CommonResponse(this.deserializer, this.data, this.errors, this.extensions); - static CommonResponse fromExecuteMutation( - Deserializer deserializer, ExecuteMutationResponse response) { - return CommonResponse( - deserializer, response.data.toProto3Json(), response.errors, null); - } - - static CommonResponse fromExecuteQuery( - Deserializer deserializer, ExecuteQueryResponse response) { - return CommonResponse(deserializer, response.data.toProto3Json(), - response.errors, response.extensions.toProto3Json()); - } - - final Deserializer deserializer; - final Object? data; - final List errors; - final Object? extensions; -} diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart index 2680f692e350..4480096e49a3 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart @@ -150,6 +150,7 @@ class RestTransport implements DataConnectTransport { /// Invokes query REST endpoint. @override Future invokeQuery( + String operationId, String queryName, Deserializer deserializer, Serializer? serializer, @@ -169,6 +170,7 @@ class RestTransport implements DataConnectTransport { /// Invokes mutation REST endpoint. @override Future invokeMutation( + String operationId, String queryName, Deserializer deserializer, Serializer? serializer, @@ -184,6 +186,20 @@ class RestTransport implements DataConnectTransport { token, ); } + + /// WebSockets are now handled by WebSocketTransport in FirebaseDataConnect. + @override + Stream invokeStreamQuery( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? token, + ) { + throw UnsupportedError( + 'Streaming should be routed through WebSocketTransport'); + } } /// Initializes Rest transport for Data Connect. diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/stream_protocol.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/stream_protocol.dart new file mode 100644 index 000000000000..a0478e6e1e0c --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/stream_protocol.dart @@ -0,0 +1,160 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// The kind of streaming request. +enum RequestKind { + subscribe, + execute, + resume, + cancel, +} + +/// Request to execute or subscribe to a Data Connect query or mutation. +class ExecuteRequest { + ExecuteRequest(this.operationName, this.variables); + + final String operationName; + final Map? variables; + + Map toJson() { + final Map data = {}; + data['operationName'] = operationName; + if (variables != null) { + data['variables'] = variables; + } + return data; + } +} + +/// Request to resume a query. +class ResumeRequest { + ResumeRequest(); + + Map toJson() { + return {}; + } +} + +/// StreamRequest defines the request of Data Connect's bi-directional streaming API. +class StreamRequest { + StreamRequest({ + this.name, + this.headers, + this.requestId, + this.requestKind, + this.subscribe, + this.execute, + this.resume, + this.cancel, + this.dataEtag, + }); + + /// The resource name of the connector. + final String? name; + + /// Optional headers. + final Map? headers; + + /// The request id used to identify a request within the stream. + final String? requestId; + + /// Kind of the request. + final RequestKind? requestKind; + + /// Subscribe to a Data Connect query. + final ExecuteRequest? subscribe; + + /// Execute a Data Connect query or mutation. + final ExecuteRequest? execute; + + /// Resume a query. + final ResumeRequest? resume; + + /// Signal that the client is no longer interested. + final bool? cancel; + + /// Etag for caching. + final String? dataEtag; + + Map toJson() { + final Map data = {}; + if (name != null) { + data['name'] = name; + } + if (headers != null) { + data['headers'] = headers; + } + if (requestId != null) { + data['requestId'] = requestId; + } + if (dataEtag != null) { + data['dataEtag'] = dataEtag; + } + + if (subscribe != null) { + data['subscribe'] = subscribe!.toJson(); + } else if (execute != null) { + data['execute'] = execute!.toJson(); + } else if (resume != null) { + data['resume'] = resume!.toJson(); + } else if (cancel == true) { + data['cancel'] = {}; + } + + return data; + } +} + +/// StreamResponse defines the response of Data Connect's bi-directional streaming API. +class StreamResponse { + StreamResponse({ + this.requestId, + this.data, + this.dataEtag, + this.errors, + this.cancelled, + this.extensions, + }); + + factory StreamResponse.fromJson(Map json) { + if (json.containsKey('result')) { + json = json['result'] as Map; + } else if (json.containsKey('error')) { + final errObj = json['error'] as Map; + json = { + 'errors': [ + {'message': errObj['message']} + ] + }; + } + + List? errorsList = json['errors'] as List?; + + return StreamResponse( + requestId: json['requestId'] as String?, + data: json['data'] as Map?, + dataEtag: json['dataEtag'] as String?, + errors: errorsList, + cancelled: json['cancelled'] as bool?, + extensions: json['extensions'] as Map?, + ); + } + + final String? requestId; + final Map? data; + final String? dataEtag; + final List? errors; + final bool? cancelled; + final Map? extensions; +} diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_library.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_library.dart index 7da28d92b812..44e0391dd6c7 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_library.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_library.dart @@ -12,8 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer' as developer; +import 'dart:math'; import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; import '../common/common_library.dart'; +import '../dataconnect_version.dart'; +import 'stream_protocol.dart'; part 'transport_stub.dart'; +part 'websocket_transport.dart'; diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_stub.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_stub.dart index e00c53b5bac1..87b8445f3abe 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_stub.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/transport_stub.dart @@ -50,6 +50,7 @@ class TransportStub implements DataConnectTransport { /// Stub for invoking a mutation. @override Future invokeMutation( + String operationId, String queryName, Deserializer deserializer, Serializer? serializer, @@ -60,9 +61,24 @@ class TransportStub implements DataConnectTransport { throw UnimplementedError(); } + /// Stub for subscribing to a query. + @override + Stream invokeStreamQuery( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? token, + ) { + // TODO: implement invokeStreamQuery + throw UnimplementedError(); + } + /// Stub for invoking a query. @override Future invokeQuery( + String operationId, String queryName, Deserializer deserializer, Serializer? serialize, diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart new file mode 100644 index 000000000000..c2514dbb69c1 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart @@ -0,0 +1,638 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +part of 'transport_library.dart'; + +/// WebSocketTransport makes requests out to the streaming endpoints of the configured backend, +/// multiplexing multiple subscriptions and unary operations over a single WebSocket connection. + +class _PendingUnary { + final Completer completer; + final String operationName; + final Map? variables; + final bool isMutation; + + _PendingUnary( + this.completer, this.operationName, this.variables, this.isMutation); +} + +class _PendingSubscription { + final String operationId; + final String queryName; + final Map? variables; + + _PendingSubscription(this.operationId, this.queryName, this.variables); +} + +class WebSocketTransport implements DataConnectTransport { + static const int _maxReconnectAttempts = 10; + static const int _maxReconnectDelayMs = 30000; + static const int _initialReconnectDelayMs = 1000; + + /// Initializes necessary protocol and port. + WebSocketTransport( + this.transportOptions, + this.options, + this.appId, + this.sdkType, + this.appCheck, [ + this.auth, + ]) { + final protocol = (transportOptions.isSecure ?? true) ? 'wss' : 'ws'; + final host = transportOptions.host; + final port = transportOptions.port ?? 443; + final location = options.location; + + _url = Uri( + scheme: protocol, + host: host, + port: port, + path: + '/ws/google.firebase.dataconnect.v1.ConnectorStreamService/Connect/locations/$location', + ).toString(); + + _currentUid = auth?.currentUser?.uid; + _authSubscription = auth?.idTokenChanges().listen((user) async { + final newUid = user?.uid; + // Disconnect and reconnect on any fundamental user change (login, logout, switch). + if (_currentUid != newUid) { + _disconnect(); + _scheduleReconnect(); + } else if (newUid != null && isConnected) { + // Token refreshed for the same user, push the new token natively down the socket. + try { + final token = await user?.getIdToken(); + final request = StreamRequest( + requestId: _generateRequestId('auth'), + headers: _buildHeaders(token, null), + ); + _send(request.toJson()); + } catch (_) { + // Ignored + } + } + _currentUid = newUid; + }); + } + + FirebaseAuth? auth; + String? _currentUid; + // ignore: unused_field + StreamSubscription? _authSubscription; //required to hold reference + + @override + FirebaseAppCheck? appCheck; + + @override + CallerSDKType sdkType; + + late String _url; + + @override + TransportOptions transportOptions; + + @override + DataConnectOptions options; + + @override + String appId; + + WebSocketChannel? _channel; + // ignore: unused_field + StreamSubscription? _channelSubscription; + + // Active listeners for stream subscriptions mapped by requestId. + final Map>> _streamListeners = + {}; + + // Pending information for subscriptions mapped by requestId. + final Map _pendingSubscriptions = {}; + + // Active completers for unary operations mapped by requestId. + final Map> _unaryListeners = {}; + + // Active subscriptions mapped by operationId => requestId. + final Map _activeSubscriptions = {}; + + bool _isReconnecting = false; + int _reconnectAttempts = 0; + bool _isExpectedDisconnect = false; + + void _checkIdleAndDisconnect() { + if (_streamListeners.isEmpty && _unaryListeners.isEmpty) { + _isExpectedDisconnect = true; + _disconnect(); + _clearState(); + } + } + + final Random _random = Random(); + static const String _chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; + + String _generateRequestId(String operationName) { + final randStr = String.fromCharCodes(Iterable.generate( + 15, (_) => _chars.codeUnitAt(_random.nextInt(_chars.length)))); + return '${operationName}_$randStr'; + } + + void _send(Map json) { + if (_channel == null) return; + final encoded = jsonEncode(json); + if (encoded.isNotEmpty) { + _channel!.sink.add(encoded); + } + } + + bool get isConnected => _channel != null; + + Map _buildHeaders(String? authToken, String? appCheckToken) { + Map headers = { + 'x-goog-api-client': getGoogApiVal(sdkType, packageVersion), + 'x-firebase-client': getFirebaseClientVal(packageVersion) + }; + if (authToken != null) { + headers['X-Firebase-Auth-Token'] = authToken; + } + if (appCheckToken != null) { + headers['X-Firebase-AppCheck'] = appCheckToken; + } + headers['x-firebase-gmpid'] = appId; + return headers; + } + + Future? _connectionFuture; + + Future _ensureConnected(String? authToken) { + if (_channel != null) return Future.value(); + if (_connectionFuture != null) return _connectionFuture!; + _connectionFuture = _doConnect(authToken).whenComplete(() { + _connectionFuture = null; + }); + return _connectionFuture!; + } + + Future _doConnect(String? authToken) async { + String? appCheckToken; + try { + appCheckToken = await appCheck?.getToken(); + } catch (_) { + // Ignored + } + + final headers = _buildHeaders(authToken, appCheckToken); + + _channel = WebSocketChannel.connect(Uri.parse(_url)); + _channelSubscription = _channel?.stream.listen( + _onMessage, + onError: _onError, + onDone: _onDone, + ); + + // reset this since an explicit connect was requested + _isExpectedDisconnect = false; + + try { + await _channel?.ready; + } catch (e) { + developer.log('WebSocket connection failed to become ready: $e'); + _channel = null; + throw DataConnectError( + DataConnectErrorCode.other, 'WebSocket connection failed: $e'); + } + + final initRequest = StreamRequest( + name: + 'projects/${options.projectId}/locations/${options.location}/services/${options.serviceId}/connectors/${options.connector}', + headers: headers, + ); + _send(initRequest.toJson()); + } + + // called when a message is received from the stream + void _onMessage(dynamic message) { + try { + var bodyString = ''; + if (message is List) { + bodyString = utf8.decode(message); + } else { + bodyString = message as String; + } + + final bodyJson = jsonDecode(bodyString) as Map; + final response = StreamResponse.fromJson(bodyJson); + + final requestId = response.requestId; + if (requestId == null) return; + + final serverResponse = ServerResponse( + response.data ?? {}, + extensions: response.extensions, + ); + + // Append errors if any exist on the stream payload + if (response.errors != null && response.errors!.isNotEmpty) { + // We simulate a DataConnectOperationError payload structure + // so that ref.dart can parse it correctly + serverResponse.data['errors'] = response.errors; + } + + if (_unaryListeners.containsKey(requestId)) { + final pendings = _unaryListeners.remove(requestId) ?? []; + for (final p in pendings) { + if (!p.completer.isCompleted) { + p.completer.complete(serverResponse); + } + } + _checkIdleAndDisconnect(); + } + + if (_streamListeners.containsKey(requestId)) { + final controllers = _streamListeners[requestId] ?? []; + if (response.cancelled == true) { + for (final controller in controllers) { + controller.close(); + } + _streamListeners.remove(requestId); + _activeSubscriptions.removeWhere((key, value) => value == requestId); + _pendingSubscriptions.remove(requestId); + _checkIdleAndDisconnect(); + } else { + for (final controller in controllers) { + controller.add(serverResponse); + } + } + } + } catch (e) { + // JSON decoding error or unknown format + developer.log('error decoding server response $e'); + } + } + + void _clearState([DataConnectError? error]) { + final e = error ?? + DataConnectError( + DataConnectErrorCode.other, 'WebSocket connection closed.'); + for (final pendings in _unaryListeners.values) { + for (final p in pendings) { + if (!p.completer.isCompleted) { + p.completer.completeError(e); + } + } + } + for (final controllers in _streamListeners.values) { + for (final controller in controllers) { + controller.addError(e); + controller.close(); + } + } + _unaryListeners.clear(); + _streamListeners.clear(); + _activeSubscriptions.clear(); + _pendingSubscriptions.clear(); + _isReconnecting = false; + _reconnectAttempts = 0; + } + + Timer? _reconnectTimer; + + void _scheduleReconnect() { + if (_isReconnecting || _isExpectedDisconnect) return; + if (_streamListeners.isEmpty && _unaryListeners.isEmpty) return; + _isReconnecting = true; + + if (_reconnectAttempts >= _maxReconnectAttempts) { + _clearState(DataConnectError(DataConnectErrorCode.other, + 'Network disconnected after max attempts.')); + return; + } + + final delay = min( + _initialReconnectDelayMs * pow(2, _reconnectAttempts).toInt(), + _maxReconnectDelayMs); + + _reconnectTimer?.cancel(); + _reconnectTimer = Timer(Duration(milliseconds: delay), () async { + _performReconnect(); + }); + } + + Future _refreshAuthToken() async { + try { + return await auth?.currentUser?.getIdToken(); + } catch (_) { + // If fetching token fails, continue unauthenticated. + return null; + } + } + + Future _refreshAppCheckToken() async { + try { + if (appCheck != null) { + return await appCheck!.getToken(); + } + } catch (_) { + // Ignored: continue without AppCheck token if it fails. + } + return null; + } + + void _resubscribeActive(String? authToken, String? appCheckToken) { + for (final sub in _pendingSubscriptions.values) { + final reqId = _activeSubscriptions[sub.operationId]; + if (reqId == null) continue; + final headers = _buildHeaders(authToken, appCheckToken); + final request = StreamRequest( + requestId: reqId, + requestKind: RequestKind.subscribe, + subscribe: ExecuteRequest(sub.queryName, sub.variables), + headers: headers, + ); + _send(request.toJson()); + } + } + + void _replayQueriesAndFailMutations( + String? authToken, String? appCheckToken) { + final unariesToReplay = >{}; + for (final entry in _unaryListeners.entries) { + final reqId = entry.key; + final kept = <_PendingUnary>[]; + for (final p in entry.value) { + if (p.isMutation) { + p.completer.completeError(DataConnectError(DataConnectErrorCode.other, + 'Network reconnected; mutations cannot be safely retried.')); + } else { + kept.add(p); + final headers = _buildHeaders(authToken, appCheckToken); + final request = StreamRequest( + requestId: reqId, + requestKind: RequestKind.execute, + execute: ExecuteRequest(p.operationName, p.variables), + headers: headers, + ); + _send(request.toJson()); + } + } + if (kept.isNotEmpty) { + unariesToReplay[reqId] = kept; + } + } + _unaryListeners.clear(); + _unaryListeners.addAll(unariesToReplay); + } + + Future _performReconnect() async { + _channel?.sink.close(); + _channel = null; + _reconnectAttempts++; + + final authToken = await _refreshAuthToken(); + final appCheckToken = await _refreshAppCheckToken(); + + try { + await _ensureConnected(authToken); + + _reconnectAttempts = 0; + _isReconnecting = false; + + _resubscribeActive(authToken, appCheckToken); + _replayQueriesAndFailMutations(authToken, appCheckToken); + } catch (e) { + _isReconnecting = false; + _scheduleReconnect(); + } + } + + void _onError(dynamic error) { + if (_channel == null) return; + developer.log('WebSocket error: $error'); + _channel = null; + _isReconnecting = false; + _scheduleReconnect(); + } + + void _disconnect() { + _reconnectTimer?.cancel(); + _reconnectTimer = null; + _channel?.sink.close(); + _channel = null; + } + + void disconnect() { + _isExpectedDisconnect = true; + _disconnect(); + } + + void _onDone() { + if (_channel == null) return; + _channel = null; + _isReconnecting = false; + if (!_isExpectedDisconnect) { + _scheduleReconnect(); + } + } + + @override + Future invokeQuery( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? authToken, + ) async { + return _invokeUnary(operationId, queryName, deserializer, serializer, vars, + authToken, RequestKind.execute, false); + } + + @override + Future invokeMutation( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? authToken, + ) async { + return _invokeUnary(operationId, queryName, deserializer, serializer, vars, + authToken, RequestKind.execute, true); + } + + Future _invokeUnary( + String operationId, + String operationName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? authToken, + RequestKind requestKind, + bool isMutation, + ) async { + await _ensureConnected(authToken); + + final completer = Completer(); + + if (_activeSubscriptions.containsKey(operationId)) { + final existingRequestId = _activeSubscriptions[operationId]!; + Map? variablesMap; + if (vars != null && serializer != null) { + variablesMap = jsonDecode(serializer(vars)); + } + _unaryListeners.putIfAbsent(existingRequestId, () => []).add( + _PendingUnary(completer, operationName, variablesMap, isMutation)); + + String? appCheckToken; + try { + appCheckToken = await appCheck?.getToken(); + } catch (_) { + // Ignored + } + + final headers = _buildHeaders(authToken, appCheckToken); + + final request = StreamRequest( + requestId: existingRequestId, + requestKind: RequestKind.resume, + resume: ResumeRequest(), + headers: headers, + ); + _send(request.toJson()); + + return completer.future; + } + + final requestId = _generateRequestId(operationId); + + Map? variables; + if (vars != null && serializer != null) { + variables = jsonDecode(serializer(vars)); + } + _unaryListeners + .putIfAbsent(requestId, () => []) + .add(_PendingUnary(completer, operationName, variables, isMutation)); + + String? appCheckToken; + try { + appCheckToken = await appCheck?.getToken(); + } catch (_) { + // Ignored + } + + final headers = _buildHeaders(authToken, appCheckToken); + + final request = StreamRequest( + requestId: requestId, + requestKind: requestKind, + execute: ExecuteRequest(operationName, variables), + headers: headers, + ); + + _send(request.toJson()); + + return completer.future; + } + + @override + Stream invokeStreamQuery( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? authToken, + ) { + late StreamController controller; + + controller = StreamController( + onListen: () async { + try { + await _ensureConnected(authToken); + } catch (e) { + developer.log("Error subscribing - setting up stream $e"); + // Do NOT add error to sink here. The stream is designed to quietly + // add the query to `_pendingSubscriptions` below and silently + // retry when the network reconnects via `_scheduleReconnect`. + } + + if (_activeSubscriptions.containsKey(operationId)) { + final existingRequestId = _activeSubscriptions[operationId]!; + _streamListeners + .putIfAbsent(existingRequestId, () => []) + .add(controller); + return; + } + + final requestId = _generateRequestId(operationId); + _activeSubscriptions[operationId] = requestId; + _streamListeners.putIfAbsent(requestId, () => []).add(controller); + + Map? variables; + if (vars != null && serializer != null) { + variables = json.decode(serializer(vars)); + } + _pendingSubscriptions[requestId] = + _PendingSubscription(operationId, queryName, variables); + + if (!isConnected) { + // we are not connected - + // keep pending sub to use for retry + _scheduleReconnect(); + return; + } + + String? appCheckToken; + try { + appCheckToken = await appCheck?.getToken(); + } catch (_) { + // Ignored + } + + final headers = _buildHeaders(authToken, appCheckToken); + + final request = StreamRequest( + requestId: requestId, + requestKind: RequestKind.subscribe, + subscribe: ExecuteRequest(queryName, variables), + headers: headers, + ); + + _send(request.toJson()); + }, + onCancel: () { + if (!_activeSubscriptions.containsKey(operationId)) return; + final requestId = _activeSubscriptions[operationId]!; + + final listeners = _streamListeners[requestId]; + if (listeners != null) { + listeners.remove(controller); + if (listeners.isEmpty) { + _streamListeners.remove(requestId); + _activeSubscriptions.remove(operationId); + _pendingSubscriptions.remove(requestId); + + final cancelReq = StreamRequest( + requestId: requestId, + requestKind: RequestKind.cancel, + cancel: true, + ); + _send(cancelReq.toJson()); + _checkIdleAndDisconnect(); + } + } + }, + ); + + return controller.stream; + } +} diff --git a/packages/firebase_data_connect/firebase_data_connect/pubspec.yaml b/packages/firebase_data_connect/firebase_data_connect/pubspec.yaml index d0145d0290ac..bdc841a51a73 100644 --- a/packages/firebase_data_connect/firebase_data_connect/pubspec.yaml +++ b/packages/firebase_data_connect/firebase_data_connect/pubspec.yaml @@ -1,38 +1,39 @@ name: firebase_data_connect description: 'Flutter plugin for Firebase Data Connect, a relational database service that lets you build and scale using a fully-managed PostgreSQL database powered by Cloud SQL.' -version: 0.2.4 +version: 0.3.0+5 +resolution: workspace homepage: https://firebase.google.com/docs/data-connect/quickstart?platform=flutter false_secrets: - example/** - dartpad/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: crypto: ^3.0.6 - firebase_app_check: ^0.4.2 - firebase_auth: ^6.3.0 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 + firebase_app_check: ^0.4.5 + firebase_auth: ^6.5.4 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 fixnum: ^1.1.1 flutter: sdk: flutter - grpc: ^3.2.4 + grpc: ^4.0.1 http: ^1.2.1 intl: ^0.20.2 path: ^1.9.0 path_provider: ^2.0.0 protobuf: ^3.1.0 - sqlite3: ^2.9.0 - sqlite3_flutter_libs: ^0.5.40 + sqlite3: ^3.1.0 + web_socket_channel: ^3.0.1 dev_dependencies: build_runner: ^2.4.12 - firebase_app_check_platform_interface: ^0.2.2 - firebase_auth_platform_interface: ^8.1.8 - flutter_lints: ^4.0.0 + firebase_app_check_platform_interface: ^0.4.1 + firebase_auth_platform_interface: ^9.0.3 + flutter_lints: ^6.0.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.dart index dec53744b7e3..6848824996c4 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.dart @@ -233,7 +233,7 @@ void main() { }); test('maxAge conformance', () async { - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; final mockResponseSuccess = http.Response('{"success": true}', 200); if (dataConnect.cacheManager == null) { diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.mocks.dart index 09f67581ad75..05a303db4353 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/cache/cache_manager_test.mocks.dart @@ -120,6 +120,20 @@ class MockFirebaseApp extends _i1.Mock implements _i3.FirebaseApp { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + + @override + void registerService( + T service, { + _i5.Future Function(T)? dispose, + }) => + super.noSuchMethod( + Invocation.method( + #registerService, + [service], + {#dispose: dispose}, + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [ConnectorConfig]. diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/common/common_library_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/common/common_library_test.dart index 353f34aeec75..a3798daf98f7 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/common/common_library_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/common/common_library_test.dart @@ -103,8 +103,10 @@ void main() { test('should handle invokeQuery with proper deserializer', () async { const queryName = 'testQuery'; - final deserializer = (json) => json; + const queryId = 'testQueryId'; + deserializer(json) => json; final result = await transport.invokeQuery( + queryId, queryName, deserializer, emptySerializer, @@ -117,8 +119,10 @@ void main() { test('should handle invokeMutation with proper deserializer', () async { const queryName = 'testMutation'; - final deserializer = (json) => json; + const operationId = 'testMutationId'; + deserializer(json) => json; final result = await transport.invokeMutation( + operationId, queryName, deserializer, emptySerializer, @@ -134,17 +138,18 @@ void main() { // Test class extending DataConnectTransport for testing purposes class TestDataConnectTransport extends DataConnectTransport { TestDataConnectTransport( - TransportOptions transportOptions, - DataConnectOptions options, - String appId, - CallerSDKType sdkType, { + super.transportOptions, + super.options, + super.appId, + super.sdkType, { FirebaseAppCheck? appCheck, - }) : super(transportOptions, options, appId, sdkType) { + }) { this.appCheck = appCheck; } @override Future invokeQuery( + String operationId, String queryName, Deserializer deserializer, Serializer? serializer, @@ -157,6 +162,7 @@ class TestDataConnectTransport extends DataConnectTransport { @override Future invokeMutation( + String operationId, String queryName, Deserializer deserializer, Serializer? serializer, @@ -166,4 +172,16 @@ class TestDataConnectTransport extends DataConnectTransport { // Simulate mutation invocation logic here return ServerResponse({}); } + + @override + Stream invokeStreamQuery( + String operationId, + String queryName, + Deserializer deserializer, + Serializer? serializer, + Variables? vars, + String? authToken, + ) { + return Stream.value(ServerResponse({})); + } } diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/common/dataconnect_error_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/common/dataconnect_error_test.dart index 3cb6c9ce26c8..5735f324f3b4 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/common/dataconnect_error_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/common/dataconnect_error_test.dart @@ -79,8 +79,7 @@ void main() { group('Serializer and Deserializer', () { test('should serialize variables into string format', () { - Serializer> serializer = - (Map vars) => vars.toString(); + String serializer(Map vars) => vars.toString(); final inputVars = {'key1': 'value1', 'key2': 123}; final serializedString = serializer(inputVars); @@ -89,8 +88,7 @@ void main() { }); test('should deserialize string data into expected format', () { - Deserializer> deserializer = - (String data) => {'data': data}; + deserializer(String data) => {'data': data}; const inputData = '{"message": "Hello World"}'; final deserializedData = deserializer(inputData); diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart index 636cae0cde52..b057f4aeb71f 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/core/ref_test.dart @@ -82,7 +82,7 @@ void main() { test( 'addQuery should create a new StreamController if query does not exist', () { - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; String varSerializer(Object? _) { return 'varsAsStr'; } @@ -98,8 +98,7 @@ void main() { ); final stream = queryManager.addQuery(ref); - //expect(queryManager.trackedQueries['testQuery'], isNotNull); - expect(queryManager.trackedQueries['testQuery::varsAsStr'], isNotNull); + expect(queryManager.trackedQueries.values.contains(ref), isTrue); expect(stream, isA()); }); }); @@ -149,7 +148,7 @@ void main() { mockDataConnect.transport = transport; }); test('executeQuery should gracefully handle getIdToken failures', () async { - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; final mockResponseSuccess = http.Response('{"success": true}', 200); when(mockUser.getIdToken()).thenThrow(Exception('Auth error')); QueryRef ref = QueryRef( @@ -175,7 +174,7 @@ void main() { () async { final mockResponse = http.Response('{"error": "Unauthorized"}', 401); final mockResponseSuccess = http.Response('{"success": true}', 200); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; int count = 0; int idTokenCount = 0; QueryRef ref = QueryRef( @@ -219,7 +218,7 @@ void main() { }); test('throw Error if server throws one', () { - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; final mockResponse = http.Response( ''' { @@ -285,10 +284,10 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) { + AbcHolder deserializer(String data) { Map decoded = jsonDecode(data) as Map; return AbcHolder(decoded['abc']!); - }; + } QueryRef ref = QueryRef( mockDataConnect, @@ -339,10 +338,10 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) { + AbcHolder deserializer(String data) { Map decoded = jsonDecode(data) as Map; return AbcHolder(decoded['abc']!); - }; + } QueryRef ref = QueryRef( mockDataConnect, @@ -390,10 +389,10 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) { + AbcHolder deserializer(String data) { Map decoded = jsonDecode(data) as Map; return AbcHolder(decoded['abc']!); - }; + } QueryRef ref = QueryRef( mockDataConnect, diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.dart index 2deb28f22749..eabf82393de8 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.dart @@ -25,10 +25,48 @@ import 'package:mockito/mockito.dart'; @GenerateNiceMocks([MockSpec(), MockSpec()]) import 'firebase_data_connect_test.mocks.dart'; -class MockFirebaseAuth extends Mock implements FirebaseAuth {} +class MockFirebaseAuth extends Mock implements FirebaseAuth { + @override + Stream idTokenChanges() { + return super.noSuchMethod( + Invocation.method(#idTokenChanges, []), + returnValue: const Stream.empty(), + returnValueForMissingStub: const Stream.empty(), + ) as Stream; + } +} class MockFirebaseAppCheck extends Mock implements FirebaseAppCheck {} +class DynamicMockFirebaseApp extends Mock implements FirebaseApp { + DynamicMockFirebaseApp({ + required this.name, + required this.options, + this.mockAuth, + this.mockAppCheck, + }); + + @override + final String name; + + @override + final FirebaseOptions options; + + final FirebaseAuth? mockAuth; + final FirebaseAppCheck? mockAppCheck; + + @override + T? getService() { + if (T == FirebaseAppCheck) { + return mockAppCheck as T?; + } + if (T == FirebaseAuth) { + return mockAuth as T?; + } + return null; + } +} + class MockTransportOptions extends Mock implements TransportOptions {} class MockDataConnectTransport extends Mock implements DataConnectTransport {} @@ -186,5 +224,35 @@ void main() { equals(instance), ); }); + + test( + 'checkTransport resolves dynamic service instances from registry just-in-time', + () { + FirebaseDataConnect.cachedInstances.clear(); + + final dynamicApp = DynamicMockFirebaseApp( + name: 'transportAppName', + options: const FirebaseOptions( + apiKey: 'fake_api_key', + appId: 'fake_app_id', + messagingSenderId: 'fake_messaging_sender_id', + projectId: 'fake_project_id', + ), + mockAuth: mockAuth, + mockAppCheck: mockAppCheck, + ); + + final instance = FirebaseDataConnect( + app: dynamicApp, + connectorConfig: mockConnectorConfig, + ); + + instance.checkTransport(); + + final dynamic routingTransport = instance.transport; + expect(routingTransport.rest.appCheck, equals(mockAppCheck)); + expect(routingTransport.websocket.auth, equals(mockAuth)); + expect(routingTransport.websocket.appCheck, equals(mockAppCheck)); + }); }); } diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart index 06634cc3f1ec..b4e1b1ed8a00 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/firebase_data_connect_test.mocks.dart @@ -120,6 +120,20 @@ class MockFirebaseApp extends _i1.Mock implements _i3.FirebaseApp { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + + @override + void registerService( + T service, { + _i5.Future Function(T)? dispose, + }) => + super.noSuchMethod( + Invocation.method( + #registerService, + [service], + {#dispose: dispose}, + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [ConnectorConfig]. diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart index bad97d364d32..d3dbc2902d12 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart @@ -98,7 +98,7 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; expect( () => transport.invokeOperation( @@ -124,7 +124,7 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; expect( () => transport.invokeOperation( @@ -150,9 +150,10 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; - await transport.invokeQuery('testQuery', deserializer, null, null, null); + await transport.invokeQuery( + 'testQueryId', 'testQuery', deserializer, null, null, null); verify( mockHttpClient.post( @@ -178,9 +179,10 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) => 'Deserialized Mutation Data'; + String deserializer(String data) => 'Deserialized Mutation Data'; await transport.invokeMutation( + 'testMutationId', 'testMutation', deserializer, null, @@ -215,7 +217,7 @@ void main() { when(mockUser.getIdToken()).thenAnswer((_) async => 'authToken123'); when(mockAppCheck.getToken()).thenAnswer((_) async => 'appCheckToken123'); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; await transport.invokeOperation( 'testQuery', @@ -250,7 +252,7 @@ void main() { when(mockUser.getIdToken()).thenAnswer((_) async => 'authToken123'); when(mockAppCheck.getToken()).thenAnswer((_) async => 'appCheckToken123'); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; await transport.invokeOperation( 'testQuery', @@ -297,7 +299,7 @@ void main() { ), ).thenAnswer((_) async => mockResponse); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; final result = await transport.invokeOperation( 'testQuery', @@ -327,7 +329,7 @@ void main() { when(mockUser.getIdToken()).thenThrow(Exception('Auth error')); when(mockAppCheck.getToken()).thenThrow(Exception('AppCheck error')); - final deserializer = (String data) => 'Deserialized Data'; + String deserializer(String data) => 'Deserialized Data'; await transport.invokeOperation( 'testQuery', diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart index 625e092f5989..828b48d4f7e7 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart @@ -778,6 +778,8 @@ class MockFirebaseAppCheck extends _i1.Mock implements _i10.FirebaseAppCheck { const _i11.AndroidPlayIntegrityProvider(), _i11.AppleAppCheckProvider? providerApple = const _i11.AppleDeviceCheckProvider(), + _i11.WindowsAppCheckProvider? providerWindows = + const _i11.WindowsDebugProvider(), }) => (super.noSuchMethod( Invocation.method( @@ -790,6 +792,7 @@ class MockFirebaseAppCheck extends _i1.Mock implements _i10.FirebaseAppCheck { #appleProvider: appleProvider, #providerAndroid: providerAndroid, #providerApple: providerApple, + #providerWindows: providerWindows, }, ), returnValue: _i6.Future.value(), diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/transport_stub_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/transport_stub_test.dart index d8c6a865d316..daeffedd48cc 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/transport_stub_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/transport_stub_test.dart @@ -65,6 +65,7 @@ void main() { expect( () async => transportStub.invokeMutation( + 'operationId', 'queryName', (json) => json, null, @@ -86,6 +87,7 @@ void main() { expect( () async => transportStub.invokeQuery( + 'operationId', 'queryName', (json) => json, null, diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.dart new file mode 100644 index 000000000000..e45a58b63fad --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.dart @@ -0,0 +1,89 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; + +import 'package:firebase_app_check/firebase_app_check.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_data_connect/src/common/common_library.dart'; +import 'package:firebase_data_connect/src/network/transport_library.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'websocket_transport_test.mocks.dart'; + +@GenerateMocks([FirebaseAuth, User, FirebaseAppCheck]) +void main() { + late WebSocketTransport transport; + late MockFirebaseAuth mockAuth; + late MockFirebaseAppCheck mockAppCheck; + late MockUser mockUser1; + late MockUser mockUser2; + late StreamController authChangesController; + + setUp(() { + mockAuth = MockFirebaseAuth(); + mockAppCheck = MockFirebaseAppCheck(); + mockUser1 = MockUser(); + mockUser2 = MockUser(); + authChangesController = StreamController.broadcast(); + + when(mockUser1.uid).thenReturn('uid-1'); + when(mockUser2.uid).thenReturn('uid-2'); + when(mockAuth.currentUser).thenReturn(mockUser1); + when(mockAuth.idTokenChanges()) + .thenAnswer((_) => authChangesController.stream); + + transport = WebSocketTransport( + TransportOptions('testhost', 443, true), + DataConnectOptions( + 'testProject', + 'testLocation', + 'testConnector', + 'testService', + ), + 'testAppId', + CallerSDKType.core, + mockAppCheck, + mockAuth, + ); + }); + + tearDown(() async { + await authChangesController.close(); + }); + + group('WebSocketTransport Idle Reconnection Guard', () { + test( + 'should not schedule or perform any reconnect on auth user switch if there are no active subscriptions', + () async { + // Emit initial user (uid-1) + authChangesController.add(mockUser1); + await Future.delayed(Duration.zero); + + // Emit different user (uid-2) to trigger a user switch reconnect scenario + authChangesController.add(mockUser2); + await Future.delayed(Duration.zero); + + // Wait for longer than the initial reconnect delay (1000ms) + await Future.delayed(const Duration(milliseconds: 1500)); + + // Verify that the transport never attempted to refresh the token + // (which is the first step of a reconnect) since the client is idle. + verifyNever(mockUser2.getIdToken()); + expect(transport.isConnected, isFalse); + }); + }); +} diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.mocks.dart new file mode 100644 index 000000000000..f73d415a9c22 --- /dev/null +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.mocks.dart @@ -0,0 +1,1164 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Mocks generated by Mockito 5.4.6 from annotations +// in firebase_data_connect/test/src/network/websocket_transport_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i5; + +import 'package:firebase_app_check/firebase_app_check.dart' as _i7; +import 'package:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart' + as _i8; +import 'package:firebase_auth/firebase_auth.dart' as _i4; +import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart' + as _i3; +import 'package:firebase_core/firebase_core.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member + +class _FakeFirebaseApp_0 extends _i1.SmartFake implements _i2.FirebaseApp { + _FakeFirebaseApp_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeActionCodeInfo_1 extends _i1.SmartFake + implements _i3.ActionCodeInfo { + _FakeActionCodeInfo_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUserCredential_2 extends _i1.SmartFake + implements _i4.UserCredential { + _FakeUserCredential_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeConfirmationResult_3 extends _i1.SmartFake + implements _i4.ConfirmationResult { + _FakeConfirmationResult_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePasswordValidationStatus_4 extends _i1.SmartFake + implements _i3.PasswordValidationStatus { + _FakePasswordValidationStatus_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUserMetadata_5 extends _i1.SmartFake implements _i3.UserMetadata { + _FakeUserMetadata_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeMultiFactor_6 extends _i1.SmartFake implements _i4.MultiFactor { + _FakeMultiFactor_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeIdTokenResult_7 extends _i1.SmartFake implements _i3.IdTokenResult { + _FakeIdTokenResult_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUser_8 extends _i1.SmartFake implements _i4.User { + _FakeUser_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [FirebaseAuth]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFirebaseAuth extends _i1.Mock implements _i4.FirebaseAuth { + MockFirebaseAuth() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.FirebaseApp get app => (super.noSuchMethod( + Invocation.getter(#app), + returnValue: _FakeFirebaseApp_0( + this, + Invocation.getter(#app), + ), + ) as _i2.FirebaseApp); + + @override + set app(_i2.FirebaseApp? value) => super.noSuchMethod( + Invocation.setter( + #app, + value, + ), + returnValueForMissingStub: null, + ); + + @override + set tenantId(String? tenantId) => super.noSuchMethod( + Invocation.setter( + #tenantId, + tenantId, + ), + returnValueForMissingStub: null, + ); + + @override + set customAuthDomain(String? customAuthDomain) => super.noSuchMethod( + Invocation.setter( + #customAuthDomain, + customAuthDomain, + ), + returnValueForMissingStub: null, + ); + + @override + Map get pluginConstants => (super.noSuchMethod( + Invocation.getter(#pluginConstants), + returnValue: {}, + ) as Map); + + @override + _i5.Future useAuthEmulator( + String? host, + int? port, { + bool? automaticHostMapping = true, + }) => + (super.noSuchMethod( + Invocation.method( + #useAuthEmulator, + [ + host, + port, + ], + {#automaticHostMapping: automaticHostMapping}, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future applyActionCode(String? code) => (super.noSuchMethod( + Invocation.method( + #applyActionCode, + [code], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i3.ActionCodeInfo> checkActionCode(String? code) => + (super.noSuchMethod( + Invocation.method( + #checkActionCode, + [code], + ), + returnValue: _i5.Future<_i3.ActionCodeInfo>.value(_FakeActionCodeInfo_1( + this, + Invocation.method( + #checkActionCode, + [code], + ), + )), + ) as _i5.Future<_i3.ActionCodeInfo>); + + @override + _i5.Future confirmPasswordReset({ + required String? code, + required String? newPassword, + }) => + (super.noSuchMethod( + Invocation.method( + #confirmPasswordReset, + [], + { + #code: code, + #newPassword: newPassword, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i4.UserCredential> createUserWithEmailAndPassword({ + required String? email, + required String? password, + }) => + (super.noSuchMethod( + Invocation.method( + #createUserWithEmailAndPassword, + [], + { + #email: email, + #password: password, + }, + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #createUserWithEmailAndPassword, + [], + { + #email: email, + #password: password, + }, + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> getRedirectResult() => (super.noSuchMethod( + Invocation.method( + #getRedirectResult, + [], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #getRedirectResult, + [], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + bool isSignInWithEmailLink(String? emailLink) => (super.noSuchMethod( + Invocation.method( + #isSignInWithEmailLink, + [emailLink], + ), + returnValue: false, + ) as bool); + + @override + _i5.Stream<_i4.User?> authStateChanges() => (super.noSuchMethod( + Invocation.method( + #authStateChanges, + [], + ), + returnValue: _i5.Stream<_i4.User?>.empty(), + ) as _i5.Stream<_i4.User?>); + + @override + _i5.Stream<_i4.User?> idTokenChanges() => (super.noSuchMethod( + Invocation.method( + #idTokenChanges, + [], + ), + returnValue: _i5.Stream<_i4.User?>.empty(), + ) as _i5.Stream<_i4.User?>); + + @override + _i5.Stream<_i4.User?> userChanges() => (super.noSuchMethod( + Invocation.method( + #userChanges, + [], + ), + returnValue: _i5.Stream<_i4.User?>.empty(), + ) as _i5.Stream<_i4.User?>); + + @override + _i5.Future sendPasswordResetEmail({ + required String? email, + _i3.ActionCodeSettings? actionCodeSettings, + }) => + (super.noSuchMethod( + Invocation.method( + #sendPasswordResetEmail, + [], + { + #email: email, + #actionCodeSettings: actionCodeSettings, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future sendSignInLinkToEmail({ + required String? email, + required _i3.ActionCodeSettings? actionCodeSettings, + }) => + (super.noSuchMethod( + Invocation.method( + #sendSignInLinkToEmail, + [], + { + #email: email, + #actionCodeSettings: actionCodeSettings, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future setLanguageCode(String? languageCode) => (super.noSuchMethod( + Invocation.method( + #setLanguageCode, + [languageCode], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future setSettings({ + bool? appVerificationDisabledForTesting = false, + String? userAccessGroup, + String? phoneNumber, + String? smsCode, + bool? forceRecaptchaFlow, + }) => + (super.noSuchMethod( + Invocation.method( + #setSettings, + [], + { + #appVerificationDisabledForTesting: + appVerificationDisabledForTesting, + #userAccessGroup: userAccessGroup, + #phoneNumber: phoneNumber, + #smsCode: smsCode, + #forceRecaptchaFlow: forceRecaptchaFlow, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future setPersistence(_i3.Persistence? persistence) => + (super.noSuchMethod( + Invocation.method( + #setPersistence, + [persistence], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i4.UserCredential> signInAnonymously() => (super.noSuchMethod( + Invocation.method( + #signInAnonymously, + [], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #signInAnonymously, + [], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> signInWithCredential( + _i3.AuthCredential? credential) => + (super.noSuchMethod( + Invocation.method( + #signInWithCredential, + [credential], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #signInWithCredential, + [credential], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> signInWithCustomToken(String? token) => + (super.noSuchMethod( + Invocation.method( + #signInWithCustomToken, + [token], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #signInWithCustomToken, + [token], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> signInWithEmailAndPassword({ + required String? email, + required String? password, + }) => + (super.noSuchMethod( + Invocation.method( + #signInWithEmailAndPassword, + [], + { + #email: email, + #password: password, + }, + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #signInWithEmailAndPassword, + [], + { + #email: email, + #password: password, + }, + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> signInWithEmailLink({ + required String? email, + required String? emailLink, + }) => + (super.noSuchMethod( + Invocation.method( + #signInWithEmailLink, + [], + { + #email: email, + #emailLink: emailLink, + }, + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #signInWithEmailLink, + [], + { + #email: email, + #emailLink: emailLink, + }, + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> signInWithProvider( + _i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #signInWithProvider, + [provider], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #signInWithProvider, + [provider], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.ConfirmationResult> signInWithPhoneNumber( + String? phoneNumber, [ + _i4.RecaptchaVerifier? verifier, + ]) => + (super.noSuchMethod( + Invocation.method( + #signInWithPhoneNumber, + [ + phoneNumber, + verifier, + ], + ), + returnValue: + _i5.Future<_i4.ConfirmationResult>.value(_FakeConfirmationResult_3( + this, + Invocation.method( + #signInWithPhoneNumber, + [ + phoneNumber, + verifier, + ], + ), + )), + ) as _i5.Future<_i4.ConfirmationResult>); + + @override + _i5.Future<_i4.UserCredential> signInWithPopup(_i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #signInWithPopup, + [provider], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #signInWithPopup, + [provider], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future signInWithRedirect(_i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #signInWithRedirect, + [provider], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future verifyPasswordResetCode(String? code) => + (super.noSuchMethod( + Invocation.method( + #verifyPasswordResetCode, + [code], + ), + returnValue: _i5.Future.value(_i6.dummyValue( + this, + Invocation.method( + #verifyPasswordResetCode, + [code], + ), + )), + ) as _i5.Future); + + @override + _i5.Future verifyPhoneNumber({ + String? phoneNumber, + _i3.PhoneMultiFactorInfo? multiFactorInfo, + required _i3.PhoneVerificationCompleted? verificationCompleted, + required _i3.PhoneVerificationFailed? verificationFailed, + required _i3.PhoneCodeSent? codeSent, + required _i3.PhoneCodeAutoRetrievalTimeout? codeAutoRetrievalTimeout, + String? autoRetrievedSmsCodeForTesting, + Duration? timeout = const Duration(seconds: 30), + int? forceResendingToken, + _i3.MultiFactorSession? multiFactorSession, + }) => + (super.noSuchMethod( + Invocation.method( + #verifyPhoneNumber, + [], + { + #phoneNumber: phoneNumber, + #multiFactorInfo: multiFactorInfo, + #verificationCompleted: verificationCompleted, + #verificationFailed: verificationFailed, + #codeSent: codeSent, + #codeAutoRetrievalTimeout: codeAutoRetrievalTimeout, + #autoRetrievedSmsCodeForTesting: autoRetrievedSmsCodeForTesting, + #timeout: timeout, + #forceResendingToken: forceResendingToken, + #multiFactorSession: multiFactorSession, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future revokeTokenWithAuthorizationCode( + String? authorizationCode) => + (super.noSuchMethod( + Invocation.method( + #revokeTokenWithAuthorizationCode, + [authorizationCode], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future revokeAccessToken(String? accessToken) => + (super.noSuchMethod( + Invocation.method( + #revokeAccessToken, + [accessToken], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future signOut() => (super.noSuchMethod( + Invocation.method( + #signOut, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future initializeRecaptchaConfig() => (super.noSuchMethod( + Invocation.method( + #initializeRecaptchaConfig, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i3.PasswordValidationStatus> validatePassword( + _i4.FirebaseAuth? auth, + String? password, + ) => + (super.noSuchMethod( + Invocation.method( + #validatePassword, + [ + auth, + password, + ], + ), + returnValue: _i5.Future<_i3.PasswordValidationStatus>.value( + _FakePasswordValidationStatus_4( + this, + Invocation.method( + #validatePassword, + [ + auth, + password, + ], + ), + )), + ) as _i5.Future<_i3.PasswordValidationStatus>); +} + +/// A class which mocks [User]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockUser extends _i1.Mock implements _i4.User { + MockUser() { + _i1.throwOnMissingStub(this); + } + + @override + bool get emailVerified => (super.noSuchMethod( + Invocation.getter(#emailVerified), + returnValue: false, + ) as bool); + + @override + bool get isAnonymous => (super.noSuchMethod( + Invocation.getter(#isAnonymous), + returnValue: false, + ) as bool); + + @override + _i3.UserMetadata get metadata => (super.noSuchMethod( + Invocation.getter(#metadata), + returnValue: _FakeUserMetadata_5( + this, + Invocation.getter(#metadata), + ), + ) as _i3.UserMetadata); + + @override + List<_i3.UserInfo> get providerData => (super.noSuchMethod( + Invocation.getter(#providerData), + returnValue: <_i3.UserInfo>[], + ) as List<_i3.UserInfo>); + + @override + String get uid => (super.noSuchMethod( + Invocation.getter(#uid), + returnValue: _i6.dummyValue( + this, + Invocation.getter(#uid), + ), + ) as String); + + @override + _i4.MultiFactor get multiFactor => (super.noSuchMethod( + Invocation.getter(#multiFactor), + returnValue: _FakeMultiFactor_6( + this, + Invocation.getter(#multiFactor), + ), + ) as _i4.MultiFactor); + + @override + _i5.Future delete() => (super.noSuchMethod( + Invocation.method( + #delete, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future getIdToken([bool? forceRefresh = false]) => + (super.noSuchMethod( + Invocation.method( + #getIdToken, + [forceRefresh], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i3.IdTokenResult> getIdTokenResult( + [bool? forceRefresh = false]) => + (super.noSuchMethod( + Invocation.method( + #getIdTokenResult, + [forceRefresh], + ), + returnValue: _i5.Future<_i3.IdTokenResult>.value(_FakeIdTokenResult_7( + this, + Invocation.method( + #getIdTokenResult, + [forceRefresh], + ), + )), + ) as _i5.Future<_i3.IdTokenResult>); + + @override + _i5.Future<_i4.UserCredential> linkWithCredential( + _i3.AuthCredential? credential) => + (super.noSuchMethod( + Invocation.method( + #linkWithCredential, + [credential], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #linkWithCredential, + [credential], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> linkWithProvider(_i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #linkWithProvider, + [provider], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #linkWithProvider, + [provider], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> reauthenticateWithProvider( + _i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithProvider, + [provider], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #reauthenticateWithProvider, + [provider], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future<_i4.UserCredential> reauthenticateWithPopup( + _i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithPopup, + [provider], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #reauthenticateWithPopup, + [provider], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future reauthenticateWithRedirect(_i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithRedirect, + [provider], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i4.UserCredential> linkWithPopup(_i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #linkWithPopup, + [provider], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #linkWithPopup, + [provider], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future linkWithRedirect(_i3.AuthProvider? provider) => + (super.noSuchMethod( + Invocation.method( + #linkWithRedirect, + [provider], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i4.ConfirmationResult> linkWithPhoneNumber( + String? phoneNumber, [ + _i4.RecaptchaVerifier? verifier, + ]) => + (super.noSuchMethod( + Invocation.method( + #linkWithPhoneNumber, + [ + phoneNumber, + verifier, + ], + ), + returnValue: + _i5.Future<_i4.ConfirmationResult>.value(_FakeConfirmationResult_3( + this, + Invocation.method( + #linkWithPhoneNumber, + [ + phoneNumber, + verifier, + ], + ), + )), + ) as _i5.Future<_i4.ConfirmationResult>); + + @override + _i5.Future<_i4.UserCredential> reauthenticateWithCredential( + _i3.AuthCredential? credential) => + (super.noSuchMethod( + Invocation.method( + #reauthenticateWithCredential, + [credential], + ), + returnValue: _i5.Future<_i4.UserCredential>.value(_FakeUserCredential_2( + this, + Invocation.method( + #reauthenticateWithCredential, + [credential], + ), + )), + ) as _i5.Future<_i4.UserCredential>); + + @override + _i5.Future reload() => (super.noSuchMethod( + Invocation.method( + #reload, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future sendEmailVerification( + [_i3.ActionCodeSettings? actionCodeSettings]) => + (super.noSuchMethod( + Invocation.method( + #sendEmailVerification, + [actionCodeSettings], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future<_i4.User> unlink(String? providerId) => (super.noSuchMethod( + Invocation.method( + #unlink, + [providerId], + ), + returnValue: _i5.Future<_i4.User>.value(_FakeUser_8( + this, + Invocation.method( + #unlink, + [providerId], + ), + )), + ) as _i5.Future<_i4.User>); + + @override + _i5.Future updatePassword(String? newPassword) => (super.noSuchMethod( + Invocation.method( + #updatePassword, + [newPassword], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future updatePhoneNumber( + _i3.PhoneAuthCredential? phoneCredential) => + (super.noSuchMethod( + Invocation.method( + #updatePhoneNumber, + [phoneCredential], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future updateDisplayName(String? displayName) => + (super.noSuchMethod( + Invocation.method( + #updateDisplayName, + [displayName], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future updatePhotoURL(String? photoURL) => (super.noSuchMethod( + Invocation.method( + #updatePhotoURL, + [photoURL], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future updateProfile({ + String? displayName, + String? photoURL, + }) => + (super.noSuchMethod( + Invocation.method( + #updateProfile, + [], + { + #displayName: displayName, + #photoURL: photoURL, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future verifyBeforeUpdateEmail( + String? newEmail, [ + _i3.ActionCodeSettings? actionCodeSettings, + ]) => + (super.noSuchMethod( + Invocation.method( + #verifyBeforeUpdateEmail, + [ + newEmail, + actionCodeSettings, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); +} + +/// A class which mocks [FirebaseAppCheck]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFirebaseAppCheck extends _i1.Mock implements _i7.FirebaseAppCheck { + MockFirebaseAppCheck() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.FirebaseApp get app => (super.noSuchMethod( + Invocation.getter(#app), + returnValue: _FakeFirebaseApp_0( + this, + Invocation.getter(#app), + ), + ) as _i2.FirebaseApp); + + @override + _i5.Stream get onTokenChange => (super.noSuchMethod( + Invocation.getter(#onTokenChange), + returnValue: _i5.Stream.empty(), + ) as _i5.Stream); + + @override + set app(_i2.FirebaseApp? value) => super.noSuchMethod( + Invocation.setter( + #app, + value, + ), + returnValueForMissingStub: null, + ); + + @override + Map get pluginConstants => (super.noSuchMethod( + Invocation.getter(#pluginConstants), + returnValue: {}, + ) as Map); + + @override + _i5.Future activate({ + _i8.WebProvider? webProvider, + _i8.WebProvider? providerWeb, + _i8.AndroidProvider? androidProvider = _i8.AndroidProvider.playIntegrity, + _i8.AppleProvider? appleProvider = _i8.AppleProvider.deviceCheck, + _i8.AndroidAppCheckProvider? providerAndroid = + const _i8.AndroidPlayIntegrityProvider(), + _i8.AppleAppCheckProvider? providerApple = + const _i8.AppleDeviceCheckProvider(), + _i8.WindowsAppCheckProvider? providerWindows = + const _i8.WindowsDebugProvider(), + }) => + (super.noSuchMethod( + Invocation.method( + #activate, + [], + { + #webProvider: webProvider, + #providerWeb: providerWeb, + #androidProvider: androidProvider, + #appleProvider: appleProvider, + #providerAndroid: providerAndroid, + #providerApple: providerApple, + #providerWindows: providerWindows, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future getToken([bool? forceRefresh]) => (super.noSuchMethod( + Invocation.method( + #getToken, + [forceRefresh], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future setTokenAutoRefreshEnabled( + bool? isTokenAutoRefreshEnabled) => + (super.noSuchMethod( + Invocation.method( + #setTokenAutoRefreshEnabled, + [isTokenAutoRefreshEnabled], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future getLimitedUseToken() => (super.noSuchMethod( + Invocation.method( + #getLimitedUseToken, + [], + ), + returnValue: _i5.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getLimitedUseToken, + [], + ), + )), + ) as _i5.Future); +} diff --git a/packages/firebase_database/firebase_database/CHANGELOG.md b/packages/firebase_database/firebase_database/CHANGELOG.md index ac0c39b975f6..e7184d6478d2 100644 --- a/packages/firebase_database/firebase_database/CHANGELOG.md +++ b/packages/firebase_database/firebase_database/CHANGELOG.md @@ -1,3 +1,33 @@ +## 12.4.4 + + - **FIX**(database,iOS): prevent duplicate database instance initialization in `FLTFirebaseDatabasePlugin` ([#18373](https://github.com/firebase/flutterfire/issues/18373)). ([bad45287](https://github.com/firebase/flutterfire/commit/bad452875def7ec070ef3c11261eb8063f11f7de)) + +## 12.4.3 + + - Update a dependency to the latest release. + +## 12.4.2 + + - Update a dependency to the latest release. + +## 12.4.1 + + - Update a dependency to the latest release. + +## 12.4.0 + + - **REFACTOR**(database,android): simplify query handling by extracting queryFromModifiers method ([#18221](https://github.com/firebase/flutterfire/issues/18221)). ([65d9bb71](https://github.com/firebase/flutterfire/commit/65d9bb7104f59de82010e3e82fd0ddddbf9a2e23)) + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(database,android): fix a regression where rapidly opening and closing query streams on the same path could throw ([#18262](https://github.com/firebase/flutterfire/issues/18262)). ([e23347b6](https://github.com/firebase/flutterfire/commit/e23347b6ae96d2174c4c2b93fd60f40d31a221c7)) + - **FIX**(database,android): fix an issue where setPersistenceEnabled needed to be called first ([#18259](https://github.com/firebase/flutterfire/issues/18259)). ([11bdedfb](https://github.com/firebase/flutterfire/commit/11bdedfb356d2c84e352e26abfc79de4c5653089)) + - **FIX**(database): fix a regression with database localEvents handling ([#18257](https://github.com/firebase/flutterfire/issues/18257)). ([40fd2904](https://github.com/firebase/flutterfire/commit/40fd2904e4634d9257241c1c2e779aa5bfc61624)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 12.3.0 + + - **FEAT**(database,android): fix order issue ([#18142](https://github.com/firebase/flutterfire/issues/18142)). ([5dd661cb](https://github.com/firebase/flutterfire/commit/5dd661cb7b9efa9e02c1bc9233222860be8be7bd)) + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 12.2.0 - **FIX**(database,iOS): remove unnecessary order modifier checks in query construction ([#18134](https://github.com/firebase/flutterfire/issues/18134)). ([4fa10c36](https://github.com/firebase/flutterfire/commit/4fa10c36d195d4cd67c39d89984cfe5a1eee5d85)) diff --git a/packages/firebase_database/firebase_database/android/build.gradle b/packages/firebase_database/firebase_database/android/build.gradle index 4826a7d16f7a..a0db4c898a25 100755 --- a/packages/firebase_database/firebase_database/android/build.gradle +++ b/packages/firebase_database/firebase_database/android/build.gradle @@ -4,9 +4,13 @@ version '1.0-SNAPSHOT' apply plugin: 'com.android.library' apply from: file("local-config.gradle") -// AGP 9+ has built-in Kotlin support; older versions need the plugin explicitly. +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int -if (agpMajor < 9) { +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { apply plugin: 'kotlin-android' } @@ -55,12 +59,6 @@ android { targetCompatibility project.ext.javaVersion } - if (agpMajor < 9) { - kotlinOptions { - jvmTarget = project.ext.javaVersion - } - } - sourceSets { main { java { @@ -85,5 +83,13 @@ android { } } +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } + } +} + apply from: file("./user-agent.gradle") diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ChildEventsProxy.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ChildEventsProxy.kt index 707b8b8b5e5a..82de31ae09a0 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ChildEventsProxy.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ChildEventsProxy.kt @@ -14,43 +14,42 @@ import com.google.firebase.database.DatabaseError import io.flutter.plugin.common.EventChannel.EventSink class ChildEventsProxy - @JvmOverloads - constructor( +@JvmOverloads +constructor( @NonNull eventSink: EventSink, @NonNull eventType: String, - ) : EventsProxy(eventSink, eventType), - ChildEventListener { - override fun onChildAdded( +) : EventsProxy(eventSink, eventType), ChildEventListener { + override fun onChildAdded( @NonNull snapshot: DataSnapshot, @Nullable previousChildName: String?, - ) { - sendEvent(Constants.EVENT_TYPE_CHILD_ADDED, snapshot, previousChildName) - } + ) { + sendEvent(Constants.EVENT_TYPE_CHILD_ADDED, snapshot, previousChildName) + } - override fun onChildChanged( + override fun onChildChanged( @NonNull snapshot: DataSnapshot, @Nullable previousChildName: String?, - ) { - sendEvent(Constants.EVENT_TYPE_CHILD_CHANGED, snapshot, previousChildName) - } + ) { + sendEvent(Constants.EVENT_TYPE_CHILD_CHANGED, snapshot, previousChildName) + } - override fun onChildRemoved( + override fun onChildRemoved( @NonNull snapshot: DataSnapshot, - ) { - sendEvent(Constants.EVENT_TYPE_CHILD_REMOVED, snapshot, null) - } + ) { + sendEvent(Constants.EVENT_TYPE_CHILD_REMOVED, snapshot, null) + } - override fun onChildMoved( + override fun onChildMoved( @NonNull snapshot: DataSnapshot, @Nullable previousChildName: String?, - ) { - sendEvent(Constants.EVENT_TYPE_CHILD_MOVED, snapshot, previousChildName) - } + ) { + sendEvent(Constants.EVENT_TYPE_CHILD_MOVED, snapshot, previousChildName) + } - override fun onCancelled( + override fun onCancelled( @NonNull error: DatabaseError, - ) { - val e = FlutterFirebaseDatabaseException.fromDatabaseError(error) - eventSink.error(e.code, e.message, e.additionalData) - } + ) { + val e = FlutterFirebaseDatabaseException.fromDatabaseError(error) + eventSink.error(e.code, e.message, e.additionalData) } +} diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/Constants.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/Constants.kt index 474bf2fd83ca..43f8e2cf316c 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/Constants.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/Constants.kt @@ -49,8 +49,7 @@ object Constants { const val CHILD_KEYS = "childKeys" const val PREVIOUS_CHILD_NAME = "previousChildKey" - const val METHOD_CALL_TRANSACTION_HANDLER = - "FirebaseDatabase#callTransactionHandler" + const val METHOD_CALL_TRANSACTION_HANDLER = "FirebaseDatabase#callTransactionHandler" const val TRANSACTION_KEY = "transactionKey" const val TRANSACTION_APPLY_LOCALLY = "transactionApplyLocally" diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventStreamHandler.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventStreamHandler.kt index 7a2fd171fd70..54cf8f23e932 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventStreamHandler.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventStreamHandler.kt @@ -18,53 +18,53 @@ interface OnDispose { } class EventStreamHandler - @JvmOverloads - constructor( +@JvmOverloads +constructor( private val query: Query, private val onDispose: OnDispose, - ) : StreamHandler { - private var valueEventListener: ValueEventListener? = null - private var childEventListener: ChildEventListener? = null +) : StreamHandler { + private var valueEventListener: ValueEventListener? = null + private var childEventListener: ChildEventListener? = null - @Suppress("UNCHECKED_CAST") - override fun onListen( + @Suppress("UNCHECKED_CAST") + override fun onListen( arguments: Any?, events: EventChannel.EventSink?, - ) { - val args = arguments as Map - val eventType = args[Constants.EVENT_TYPE] as String + ) { + val args = arguments as Map + val eventType = args[Constants.EVENT_TYPE] as String - if (Constants.EVENT_TYPE_VALUE == eventType) { - events?.let { eventSink -> - valueEventListener = ValueEventsProxy(eventSink) - query.addValueEventListener(valueEventListener!!) - } - } else { - events?.let { eventSink -> - childEventListener = ChildEventsProxy(eventSink, eventType) - query.addChildEventListener(childEventListener!!) - } + if (Constants.EVENT_TYPE_VALUE == eventType) { + events?.let { eventSink -> + valueEventListener = ValueEventsProxy(eventSink) + query.addValueEventListener(valueEventListener!!) + } + } else { + events?.let { eventSink -> + childEventListener = ChildEventsProxy(eventSink, eventType) + query.addChildEventListener(childEventListener!!) } } + } - override fun onCancel(arguments: Any?) { - try { - // Remove listeners first to prevent any new events - valueEventListener?.let { - query.removeEventListener(it) - valueEventListener = null - } - - childEventListener?.let { - query.removeEventListener(it) - childEventListener = null - } + override fun onCancel(arguments: Any?) { + try { + // Remove listeners first to prevent any new events + valueEventListener?.let { + query.removeEventListener(it) + valueEventListener = null + } - // Then run the dispose callback - onDispose.run() - } catch (e: Exception) { - // Log any cleanup errors but don't throw - android.util.Log.w("EventStreamHandler", "Error during cleanup: ${e.message}") + childEventListener?.let { + query.removeEventListener(it) + childEventListener = null } + + // Then run the dispose callback + onDispose.run() + } catch (e: Exception) { + // Log any cleanup errors but don't throw + android.util.Log.w("EventStreamHandler", "Error during cleanup: ${e.message}") } } +} diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventsProxy.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventsProxy.kt index b58ca737f333..40e4735f87cb 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventsProxy.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/EventsProxy.kt @@ -15,35 +15,35 @@ import java.util.* @RestrictTo(RestrictTo.Scope.LIBRARY) abstract class EventsProxy - @JvmOverloads - constructor( +@JvmOverloads +constructor( protected val eventSink: EventChannel.EventSink, private val eventType: String, - ) { - fun buildAdditionalParams( +) { + fun buildAdditionalParams( @NonNull eventType: String, @Nullable previousChildName: String?, - ): Map { - val params = mutableMapOf() - params[Constants.EVENT_TYPE] = eventType - - if (previousChildName != null) { - params[Constants.PREVIOUS_CHILD_NAME] = previousChildName - } + ): Map { + val params = mutableMapOf() + params[Constants.EVENT_TYPE] = eventType - return params + if (previousChildName != null) { + params[Constants.PREVIOUS_CHILD_NAME] = previousChildName } - protected fun sendEvent( + return params + } + + protected fun sendEvent( @NonNull eventType: String, snapshot: DataSnapshot, @Nullable previousChildName: String?, - ) { - if (this.eventType != eventType) return + ) { + if (this.eventType != eventType) return - val payload = FlutterDataSnapshotPayload(snapshot) - val additionalParams = buildAdditionalParams(eventType, previousChildName) + val payload = FlutterDataSnapshotPayload(snapshot) + val additionalParams = buildAdditionalParams(eventType, previousChildName) - eventSink.success(payload.withAdditionalParams(additionalParams).toMap()) - } + eventSink.success(payload.withAdditionalParams(additionalParams).toMap()) } +} diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FirebaseDatabasePlugin.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FirebaseDatabasePlugin.kt index 808cf7c0e6b6..0654954d2c81 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FirebaseDatabasePlugin.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FirebaseDatabasePlugin.kt @@ -4,7 +4,6 @@ package io.flutter.plugins.firebase.database -import android.util.Log import androidx.annotation.NonNull import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.TaskCompletionSource @@ -15,16 +14,13 @@ import com.google.firebase.database.DatabaseException import com.google.firebase.database.DatabaseReference import com.google.firebase.database.FirebaseDatabase import com.google.firebase.database.Logger -import com.google.firebase.database.OnDisconnect import com.google.firebase.database.Query import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel.StreamHandler -import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugins.firebase.core.FlutterFirebasePlugin import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry @@ -33,10 +29,7 @@ import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import kotlin.Result as KotlinResult -class FirebaseDatabasePlugin : - FlutterFirebasePlugin, - FlutterPlugin, - FirebaseDatabaseHostApi { +class FirebaseDatabasePlugin : FlutterFirebasePlugin, FlutterPlugin, FirebaseDatabaseHostApi { companion object { private const val METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_database" private val databaseInstanceCache = HashMap() @@ -56,8 +49,8 @@ class FirebaseDatabasePlugin : } private fun setCachedFirebaseDatabaseInstanceForKey( - database: FirebaseDatabase, - key: String, + database: FirebaseDatabase, + key: String, ) { synchronized(databaseInstanceCache) { val existingInstance = databaseInstanceCache[key] @@ -90,11 +83,11 @@ class FirebaseDatabasePlugin : val app = FirebaseApp.getInstance(appName) val database = - if (databaseURL.isNotEmpty()) { - FirebaseDatabase.getInstance(app, databaseURL) - } else { - FirebaseDatabase.getInstance(app) - } + if (databaseURL.isNotEmpty()) { + FirebaseDatabase.getInstance(app, databaseURL) + } else { + FirebaseDatabase.getInstance(app) + } val loggingEnabled = arguments[Constants.DATABASE_LOGGING_ENABLED] as Boolean? val persistenceEnabled = arguments[Constants.DATABASE_PERSISTENCE_ENABLED] as Boolean? @@ -111,9 +104,7 @@ class FirebaseDatabasePlugin : database.useEmulator(emulatorHost, emulatorPort) } - persistenceEnabled?.let { enabled -> - database.setPersistenceEnabled(enabled) - } + persistenceEnabled?.let { enabled -> database.setPersistenceEnabled(enabled) } cacheSizeBytes?.let { size -> when (size) { @@ -123,7 +114,8 @@ class FirebaseDatabasePlugin : } } catch (e: DatabaseException) { val message = e.message - if (message != null && !message.contains("must be made before any other usage of FirebaseDatabase")) { + if (message != null && + !message.contains("must be made before any other usage of FirebaseDatabase")) { throw e } } @@ -141,10 +133,16 @@ class FirebaseDatabasePlugin : @Suppress("UNCHECKED_CAST") private fun getQuery(arguments: Map): Query { val ref = getReference(arguments) - val modifiers = arguments[Constants.MODIFIERS] as List> - return QueryBuilder(ref, modifiers).build() + val modifiers = arguments[Constants.MODIFIERS] as List> + return queryFromModifiers(ref, modifiers) } + /** Applies [modifiers]. */ + private fun queryFromModifiers( + reference: DatabaseReference, + modifiers: List>, + ): Query = QueryBuilder(reference, modifiers).build() + private fun goOnline(arguments: Map): Task { val taskCompletionSource = TaskCompletionSource() @@ -235,10 +233,9 @@ class FirebaseDatabasePlugin : try { val ref = getReference(arguments) - @Suppress("UNCHECKED_CAST") - val value = arguments[Constants.VALUE] as Map + @Suppress("UNCHECKED_CAST") val value = arguments[Constants.VALUE] as Map Tasks.await(ref.updateChildren(value)) - taskCompletionSource.setResult(null) + taskCompletionSource.setResult(null) } catch (e: Exception) { taskCompletionSource.setException(e) } @@ -331,14 +328,14 @@ class FirebaseDatabasePlugin : val eventChannel = EventChannel(messenger, eventChannelName) val streamHandler = - EventStreamHandler( - query, - object : OnDispose { - override fun run() { - eventChannel.setStreamHandler(null) - } - }, - ) + EventStreamHandler( + query, + object : OnDispose { + override fun run() { + eventChannel.setStreamHandler(null) + } + }, + ) eventChannel.setStreamHandler(streamHandler) streamHandlers[eventChannel] = streamHandler @@ -379,12 +376,12 @@ class FirebaseDatabasePlugin : val onDisconnect = getReference(arguments).onDisconnect() val onDisconnectTask = - when (priority) { - is Double -> onDisconnect.setValue(value, priority) - is String -> onDisconnect.setValue(value, priority) - null -> onDisconnect.setValue(value, null as String?) - else -> throw Exception("Invalid priority value for OnDisconnect.setWithPriority") - } + when (priority) { + is Double -> onDisconnect.setValue(value, priority) + is String -> onDisconnect.setValue(value, priority) + null -> onDisconnect.setValue(value, null as String?) + else -> throw Exception("Invalid priority value for OnDisconnect.setWithPriority") + } Tasks.await(onDisconnectTask) taskCompletionSource.setResult(null) @@ -403,8 +400,7 @@ class FirebaseDatabasePlugin : try { val ref = getReference(arguments) - @Suppress("UNCHECKED_CAST") - val value = arguments[Constants.VALUE] as Map + @Suppress("UNCHECKED_CAST") val value = arguments[Constants.VALUE] as Map val task = ref.onDisconnect().updateChildren(value) Tasks.await(task) taskCompletionSource.setResult(null) @@ -437,7 +433,7 @@ class FirebaseDatabasePlugin : } override fun onDetachedFromEngine( - @NonNull binding: FlutterPluginBinding, + @NonNull binding: FlutterPluginBinding, ) { methodChannel.setMethodCallHandler(null) cleanup() @@ -479,7 +475,7 @@ class FirebaseDatabasePlugin : } private fun removeEventStreamHandlers() { - for ((eventChannel, streamHandler) in streamHandlers) { + for ((eventChannel, streamHandler) in streamHandlers.toMap()) { streamHandler?.onCancel(null) eventChannel.setStreamHandler(null) } @@ -507,27 +503,43 @@ class FirebaseDatabasePlugin : } } - override fun setPersistenceEnabled(app: DatabasePigeonFirebaseApp, enabled: Boolean, callback: (KotlinResult) -> Unit) { + override fun setPersistenceEnabled( + app: DatabasePigeonFirebaseApp, + enabled: Boolean, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) - database.setPersistenceEnabled(enabled) + if (app.settings.persistenceEnabled == null) { + database.setPersistenceEnabled(enabled) + } callback(KotlinResult.success(Unit)) } catch (e: Exception) { callback(KotlinResult.failure(e)) } } - override fun setPersistenceCacheSizeBytes(app: DatabasePigeonFirebaseApp, cacheSize: Long, callback: (KotlinResult) -> Unit) { + override fun setPersistenceCacheSizeBytes( + app: DatabasePigeonFirebaseApp, + cacheSize: Long, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) - database.setPersistenceCacheSizeBytes(cacheSize) + if (app.settings.cacheSizeBytes == null) { + database.setPersistenceCacheSizeBytes(cacheSize) + } callback(KotlinResult.success(Unit)) } catch (e: Exception) { callback(KotlinResult.failure(e)) } } - override fun setLoggingEnabled(app: DatabasePigeonFirebaseApp, enabled: Boolean, callback: (KotlinResult) -> Unit) { + override fun setLoggingEnabled( + app: DatabasePigeonFirebaseApp, + enabled: Boolean, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) database.setLogLevel(if (enabled) Logger.Level.DEBUG else Logger.Level.NONE) @@ -537,7 +549,12 @@ class FirebaseDatabasePlugin : } } - override fun useDatabaseEmulator(app: DatabasePigeonFirebaseApp, host: String, port: Long, callback: (KotlinResult) -> Unit) { + override fun useDatabaseEmulator( + app: DatabasePigeonFirebaseApp, + host: String, + port: Long, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) database.useEmulator(host, port.toInt()) @@ -547,7 +564,11 @@ class FirebaseDatabasePlugin : } } - override fun ref(app: DatabasePigeonFirebaseApp, path: String?, callback: (KotlinResult) -> Unit) { + override fun ref( + app: DatabasePigeonFirebaseApp, + path: String?, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = if (path.isNullOrEmpty()) database.reference else database.getReference(path) @@ -558,7 +579,11 @@ class FirebaseDatabasePlugin : } } - override fun refFromURL(app: DatabasePigeonFirebaseApp, url: String, callback: (KotlinResult) -> Unit) { + override fun refFromURL( + app: DatabasePigeonFirebaseApp, + url: String, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReferenceFromUrl(url) @@ -569,7 +594,10 @@ class FirebaseDatabasePlugin : } } - override fun purgeOutstandingWrites(app: DatabasePigeonFirebaseApp, callback: (KotlinResult) -> Unit) { + override fun purgeOutstandingWrites( + app: DatabasePigeonFirebaseApp, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) database.purgeOutstandingWrites() @@ -579,7 +607,11 @@ class FirebaseDatabasePlugin : } } - override fun databaseReferenceSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (KotlinResult) -> Unit) { + override fun databaseReferenceSet( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) @@ -592,7 +624,8 @@ class FirebaseDatabasePlugin : callback(KotlinResult.success(Unit)) } else { val exception = completedTask.exception ?: Exception("Unknown error setting value") - callback(KotlinResult.failure(FlutterError("firebase_database", exception.message, null))) + callback( + KotlinResult.failure(FlutterError("firebase_database", exception.message, null))) } } } @@ -601,22 +634,28 @@ class FirebaseDatabasePlugin : } } - override fun databaseReferenceSetWithPriority(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (KotlinResult) -> Unit) { + override fun databaseReferenceSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) // Handle priority type conversion - Firebase Database expects Any? but Pigeon sends Object? - val priority = when (request.priority) { - is String -> request.priority - is Number -> request.priority - null -> null - else -> { - // Log the unexpected type for debugging - println("Warning: Unexpected priority type: ${request.priority?.javaClass?.simpleName}, value: $request.priority") - request.priority.toString() - } - } + val priority = + when (request.priority) { + is String -> request.priority + is Number -> request.priority + null -> null + else -> { + // Log the unexpected type for debugging + println( + "Warning: Unexpected priority type: ${request.priority?.javaClass?.simpleName}, value: $request.priority") + request.priority.toString() + } + } val task = reference.setValue(request.value, priority) var callbackCalled = false @@ -626,8 +665,10 @@ class FirebaseDatabasePlugin : if (completedTask.isSuccessful) { callback(KotlinResult.success(Unit)) } else { - val exception = completedTask.exception ?: Exception("Unknown error setting value with priority") - callback(KotlinResult.failure(FlutterError("firebase_database", exception.message, null))) + val exception = + completedTask.exception ?: Exception("Unknown error setting value with priority") + callback( + KotlinResult.failure(FlutterError("firebase_database", exception.message, null))) } } } @@ -639,37 +680,46 @@ class FirebaseDatabasePlugin : } } - override fun databaseReferenceUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, callback: (KotlinResult) -> Unit) { - val database = getDatabaseFromPigeonApp(app) - val reference = database.getReference(request.path) - reference.updateChildren(request.value).addOnCompleteListener { task-> - if(task.isSuccessful){ - callback(KotlinResult.success(Unit)) - } - else { - val exception = task.exception - callback(KotlinResult.failure(FlutterError("firebase_database", exception?.message, null))) - } + override fun databaseReferenceUpdate( + app: DatabasePigeonFirebaseApp, + request: UpdateRequest, + callback: (KotlinResult) -> Unit + ) { + val database = getDatabaseFromPigeonApp(app) + val reference = database.getReference(request.path) + reference.updateChildren(request.value).addOnCompleteListener { task -> + if (task.isSuccessful) { + callback(KotlinResult.success(Unit)) + } else { + val exception = task.exception + callback(KotlinResult.failure(FlutterError("firebase_database", exception?.message, null))) } + } } - override fun databaseReferenceSetPriority(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (KotlinResult) -> Unit) { + override fun databaseReferenceSetPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) // Handle priority type conversion - Firebase Database expects Any? but Pigeon sends Object? // Convert the priority to the appropriate type for Firebase - val priority = when (request.priority) { - is String -> request.priority - is Number -> request.priority - null -> null - else -> { - // Log the unexpected type for debugging - println("Warning: Unexpected priority type: ${request.priority?.javaClass?.simpleName}, value: $request.priority") - request.priority.toString() - } - } + val priority = + when (request.priority) { + is String -> request.priority + is Number -> request.priority + null -> null + else -> { + // Log the unexpected type for debugging + println( + "Warning: Unexpected priority type: ${request.priority?.javaClass?.simpleName}, value: $request.priority") + request.priority.toString() + } + } val task = reference.setPriority(priority) var callbackCalled = false @@ -688,13 +738,17 @@ class FirebaseDatabasePlugin : } // Fallback timeout to ensure callback is always called - android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ - if (!callbackCalled && !task.isComplete) { - callbackCalled = true - println("Firebase Database setPriority timeout - calling callback anyway") - callback(KotlinResult.success(Unit)) - } - }, 3000) // 3 second timeout + android.os + .Handler(android.os.Looper.getMainLooper()) + .postDelayed( + { + if (!callbackCalled && !task.isComplete) { + callbackCalled = true + println("Firebase Database setPriority timeout - calling callback anyway") + callback(KotlinResult.success(Unit)) + } + }, + 3000) // 3 second timeout } catch (e: Exception) { // Log the exception for debugging println("Firebase Database setPriority error: ${e.message}") @@ -703,7 +757,11 @@ class FirebaseDatabasePlugin : } } - override fun databaseReferenceRunTransaction(app: DatabasePigeonFirebaseApp, request: TransactionRequest, callback: (KotlinResult) -> Unit) { + override fun databaseReferenceRunTransaction( + app: DatabasePigeonFirebaseApp, + request: TransactionRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) @@ -712,67 +770,88 @@ class FirebaseDatabasePlugin : transactionRequests[request.transactionKey] = request // Start the transaction - simplified approach like iOS - reference.runTransaction(object : com.google.firebase.database.Transaction.Handler { - override fun doTransaction(mutableData: com.google.firebase.database.MutableData): com.google.firebase.database.Transaction.Result { - val semaphore = java.util.concurrent.CountDownLatch(1) - var transactionResult: TransactionHandlerResult? = null - - // Call the Flutter transaction handler on the main thread (required by FlutterJNI) - val mainHandler = android.os.Handler(android.os.Looper.getMainLooper()) - mainHandler.post { - val flutterApi = FirebaseDatabaseFlutterApi(messenger) - flutterApi.callTransactionHandler(request.transactionKey, mutableData.value) { result -> - result.fold( - onSuccess = { transactionResult = it }, - onFailure = { - transactionResult = TransactionHandlerResult(value = null, aborted = true, exception = true) + reference.runTransaction( + object : com.google.firebase.database.Transaction.Handler { + override fun doTransaction( + mutableData: com.google.firebase.database.MutableData + ): com.google.firebase.database.Transaction.Result { + val semaphore = java.util.concurrent.CountDownLatch(1) + var transactionResult: TransactionHandlerResult? = null + + // Call the Flutter transaction handler on the main thread (required by FlutterJNI) + val mainHandler = android.os.Handler(android.os.Looper.getMainLooper()) + mainHandler.post { + val flutterApi = FirebaseDatabaseFlutterApi(messenger) + flutterApi.callTransactionHandler(request.transactionKey, mutableData.value) { + result -> + result.fold( + onSuccess = { transactionResult = it }, + onFailure = { + transactionResult = + TransactionHandlerResult(value = null, aborted = true, exception = true) + }) + semaphore.countDown() } - ) - semaphore.countDown() - } - } - - semaphore.await() + } - val result = transactionResult ?: return com.google.firebase.database.Transaction.abort() + semaphore.await() - if (result.aborted || result.exception) { - return com.google.firebase.database.Transaction.abort() - } + val result = + transactionResult ?: return com.google.firebase.database.Transaction.abort() - mutableData.value = result.value - return com.google.firebase.database.Transaction.success(mutableData) - } + if (result.aborted || result.exception) { + return com.google.firebase.database.Transaction.abort() + } - override fun onComplete(error: com.google.firebase.database.DatabaseError?, committed: Boolean, currentData: com.google.firebase.database.DataSnapshot?) { - // Store the transaction result for later retrieval - val result = mapOf( - "committed" to committed, - "snapshot" to mapOf( - "value" to currentData?.value, - "key" to currentData?.key, - "exists" to currentData?.exists() - ) - ) - transactionResults[request.transactionKey] = result + mutableData.value = result.value + return com.google.firebase.database.Transaction.success(mutableData) + } - // Complete the transaction - simplified like iOS - if (error != null) { - val ex = FlutterFirebaseDatabaseException.fromDatabaseError(error) - callback(KotlinResult.failure(FlutterError("firebase_database", ex.message, ex.additionalData))) - } else { - callback(KotlinResult.success(Unit)) - } - } - }) + override fun onComplete( + error: com.google.firebase.database.DatabaseError?, + committed: Boolean, + currentData: com.google.firebase.database.DataSnapshot? + ) { + // Store the transaction result for later retrieval + val result = + mapOf( + "committed" to committed, + "snapshot" to + mapOf( + "value" to currentData?.value, + "key" to currentData?.key, + "exists" to currentData?.exists())) + transactionResults[request.transactionKey] = result + + // Complete the transaction - simplified like iOS + if (error != null) { + val ex = FlutterFirebaseDatabaseException.fromDatabaseError(error) + callback( + KotlinResult.failure( + FlutterError("firebase_database", ex.message, ex.additionalData))) + } else { + callback(KotlinResult.success(Unit)) + } + } + }, + request.applyLocally) } catch (e: Exception) { // Convert generic exceptions to FlutterFirebaseDatabaseException for proper error handling - val flutterException = if (e is FlutterFirebaseDatabaseException) e else FlutterFirebaseDatabaseException.unknown(e.message ?: "Unknown transaction error") - callback(KotlinResult.failure(FlutterError("firebase_database", flutterException.message, flutterException.additionalData))) + val flutterException = + if (e is FlutterFirebaseDatabaseException) e + else FlutterFirebaseDatabaseException.unknown(e.message ?: "Unknown transaction error") + callback( + KotlinResult.failure( + FlutterError( + "firebase_database", flutterException.message, flutterException.additionalData))) } } - override fun databaseReferenceGetTransactionResult(app: DatabasePigeonFirebaseApp, transactionKey: Long, callback: (KotlinResult>) -> Unit) { + override fun databaseReferenceGetTransactionResult( + app: DatabasePigeonFirebaseApp, + transactionKey: Long, + callback: (KotlinResult>) -> Unit + ) { try { // Return the stored transaction result val result = transactionResults[transactionKey] @@ -780,10 +859,7 @@ class FirebaseDatabasePlugin : callback(KotlinResult.success(result)) } else { // If no result is available yet, return a default result - val defaultResult = mapOf( - "committed" to false, - "snapshot" to mapOf("value" to null) - ) + val defaultResult = mapOf("committed" to false, "snapshot" to mapOf("value" to null)) callback(KotlinResult.success(defaultResult)) } } catch (e: Exception) { @@ -791,7 +867,11 @@ class FirebaseDatabasePlugin : } } - override fun onDisconnectSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (KotlinResult) -> Unit) { + override fun onDisconnectSet( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) @@ -803,7 +883,11 @@ class FirebaseDatabasePlugin : } } - override fun onDisconnectSetWithPriority(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (KotlinResult) -> Unit) { + override fun onDisconnectSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) @@ -815,7 +899,11 @@ class FirebaseDatabasePlugin : } } - override fun onDisconnectUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, callback: (KotlinResult) -> Unit) { + override fun onDisconnectUpdate( + app: DatabasePigeonFirebaseApp, + request: UpdateRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) @@ -827,7 +915,11 @@ class FirebaseDatabasePlugin : } } - override fun onDisconnectCancel(app: DatabasePigeonFirebaseApp, path: String, callback: (KotlinResult) -> Unit) { + override fun onDisconnectCancel( + app: DatabasePigeonFirebaseApp, + path: String, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(path) @@ -839,101 +931,31 @@ class FirebaseDatabasePlugin : } } - override fun queryObserve(app: DatabasePigeonFirebaseApp, request: QueryRequest, callback: (KotlinResult) -> Unit) { + override fun queryObserve( + app: DatabasePigeonFirebaseApp, + request: QueryRequest, + callback: (KotlinResult) -> Unit + ) { try { - Log.d("FirebaseDatabase", "🔍 Kotlin: Setting up query observe for path=${request.path}") val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) - - // Apply query modifiers if any - var query: com.google.firebase.database.Query = reference - // Note: no hasOrderModifier needed — Android SDK defaults to PriorityIndex - // when no orderBy is specified, so cursors work without an explicit orderBy. - - for (modifier in request.modifiers) { - when (modifier["type"] as String) { - "orderBy" -> { - when (modifier["name"] as String) { - "orderByChild" -> { - query = query.orderByChild(modifier["path"] as String) - } - "orderByKey" -> { - query = query.orderByKey() - } - "orderByValue" -> { - query = query.orderByValue() - } - "orderByPriority" -> { - query = query.orderByPriority() - } - } - } - "cursor" -> { - when (modifier["name"] as String) { - "startAt" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.startAt(value) else query.startAt(value, key) - is Number -> if (key == null) query.startAt(value.toDouble()) else query.startAt(value.toDouble(), key) - else -> if (key == null) query.startAt(value.toString()) else query.startAt(value.toString(), key) - } - } - "startAfter" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.startAfter(value) else query.startAfter(value, key) - is Number -> if (key == null) query.startAfter(value.toDouble()) else query.startAfter(value.toDouble(), key) - else -> if (key == null) query.startAfter(value.toString()) else query.startAfter(value.toString(), key) - } - } - "endAt" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.endAt(value) else query.endAt(value, key) - is Number -> if (key == null) query.endAt(value.toDouble()) else query.endAt(value.toDouble(), key) - else -> if (key == null) query.endAt(value.toString()) else query.endAt(value.toString(), key) - } - } - "endBefore" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.endBefore(value) else query.endBefore(value, key) - is Number -> if (key == null) query.endBefore(value.toDouble()) else query.endBefore(value.toDouble(), key) - else -> if (key == null) query.endBefore(value.toString()) else query.endBefore(value.toString(), key) - } - } - } - } - "limit" -> { - when (modifier["name"] as String) { - "limitToFirst" -> { - val value = (modifier["limit"] as Number).toInt() - query = query.limitToFirst(value) - } - "limitToLast" -> { - val value = (modifier["limit"] as Number).toInt() - query = query.limitToLast(value) - } - } - } - } - } + val query = queryFromModifiers(reference, request.modifiers) // Generate a unique channel name - val channelName = "firebase_database_query_${System.currentTimeMillis()}_${request.path.hashCode()}" + val channelName = synchronized(this) { "firebase_database_query_${listenerCount++}" } // Set up the event channel val eventChannel = EventChannel(messenger, channelName) - val streamHandler = EventStreamHandler(query, object : OnDispose { - override fun run() { - // Clean up when the stream is disposed - eventChannel.setStreamHandler(null) - } - }) + val streamHandler = + EventStreamHandler( + query, + object : OnDispose { + override fun run() { + // Clean up when the stream is disposed + eventChannel.setStreamHandler(null) + streamHandlers.remove(eventChannel) + } + }) eventChannel.setStreamHandler(streamHandler) streamHandlers[eventChannel] = streamHandler @@ -943,88 +965,15 @@ class FirebaseDatabasePlugin : } } - override fun queryKeepSynced(app: DatabasePigeonFirebaseApp, request: QueryRequest, callback: (KotlinResult) -> Unit) { + override fun queryKeepSynced( + app: DatabasePigeonFirebaseApp, + request: QueryRequest, + callback: (KotlinResult) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) - - // Apply query modifiers if any - var query: com.google.firebase.database.Query = reference - // Note: no hasOrderModifier needed — Android SDK defaults to PriorityIndex - // when no orderBy is specified, so cursors work without an explicit orderBy. - - for (modifier in request.modifiers) { - when (modifier["type"] as String) { - "orderBy" -> { - when (modifier["name"] as String) { - "orderByChild" -> { - query = query.orderByChild(modifier["path"] as String) - } - "orderByKey" -> { - query = query.orderByKey() - } - "orderByValue" -> { - query = query.orderByValue() - } - "orderByPriority" -> { - query = query.orderByPriority() - } - } - } - "cursor" -> { - when (modifier["name"] as String) { - "startAt" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.startAt(value) else query.startAt(value, key) - is Number -> if (key == null) query.startAt(value.toDouble()) else query.startAt(value.toDouble(), key) - else -> if (key == null) query.startAt(value.toString()) else query.startAt(value.toString(), key) - } - } - "startAfter" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.startAfter(value) else query.startAfter(value, key) - is Number -> if (key == null) query.startAfter(value.toDouble()) else query.startAfter(value.toDouble(), key) - else -> if (key == null) query.startAfter(value.toString()) else query.startAfter(value.toString(), key) - } - } - "endAt" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.endAt(value) else query.endAt(value, key) - is Number -> if (key == null) query.endAt(value.toDouble()) else query.endAt(value.toDouble(), key) - else -> if (key == null) query.endAt(value.toString()) else query.endAt(value.toString(), key) - } - } - "endBefore" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.endBefore(value) else query.endBefore(value, key) - is Number -> if (key == null) query.endBefore(value.toDouble()) else query.endBefore(value.toDouble(), key) - else -> if (key == null) query.endBefore(value.toString()) else query.endBefore(value.toString(), key) - } - } - } - } - "limit" -> { - when (modifier["name"] as String) { - "limitToFirst" -> { - val value = (modifier["limit"] as Number).toInt() - query = query.limitToFirst(value) - } - "limitToLast" -> { - val value = (modifier["limit"] as Number).toInt() - query = query.limitToLast(value) - } - } - } - } - } + val query = queryFromModifiers(reference, request.modifiers) // Add keepSynced to the query query.keepSynced(request.value ?: false) @@ -1034,96 +983,15 @@ class FirebaseDatabasePlugin : } } - override fun queryGet(app: DatabasePigeonFirebaseApp, request: QueryRequest, callback: (KotlinResult>) -> Unit) { + override fun queryGet( + app: DatabasePigeonFirebaseApp, + request: QueryRequest, + callback: (KotlinResult>) -> Unit + ) { try { val database = getDatabaseFromPigeonApp(app) val reference = database.getReference(request.path) - - // Apply query modifiers if any - var query: com.google.firebase.database.Query = reference - // Note: no hasOrderModifier needed — Android SDK defaults to PriorityIndex - // when no orderBy is specified, so cursors work without an explicit orderBy. - - for (modifier in request.modifiers) { - when (modifier["type"] as String) { - "orderBy" -> { - when (modifier["name"] as String) { - "orderByChild" -> { - query = query.orderByChild(modifier["path"] as String) - } - "orderByKey" -> { - query = query.orderByKey() - } - "orderByValue" -> { - query = query.orderByValue() - } - "orderByPriority" -> { - query = query.orderByPriority() - } - } - } - "cursor" -> { - when (modifier["name"] as String) { - "startAt" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.startAt(value) else query.startAt(value, key) - is Number -> if (key == null) query.startAt(value.toDouble()) else query.startAt(value.toDouble(), key) - else -> if (key == null) query.startAt(value.toString()) else query.startAt(value.toString(), key) - } - } - "startAfter" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.startAfter(value) else query.startAfter(value, key) - is Number -> if (key == null) query.startAfter(value.toDouble()) else query.startAfter(value.toDouble(), key) - else -> if (key == null) query.startAfter(value.toString()) else query.startAfter(value.toString(), key) - } - } - "endAt" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.endAt(value) else query.endAt(value, key) - is Number -> if (key == null) query.endAt(value.toDouble()) else query.endAt(value.toDouble(), key) - else -> if (key == null) query.endAt(value.toString()) else query.endAt(value.toString(), key) - } - } - "endBefore" -> { - val value = modifier["value"] - val key = modifier["key"] as String? - query = when (value) { - is Boolean -> if (key == null) query.endBefore(value) else query.endBefore(value, key) - is Number -> if (key == null) query.endBefore(value.toDouble()) else query.endBefore(value.toDouble(), key) - else -> if (key == null) query.endBefore(value.toString()) else query.endBefore(value.toString(), key) - } - } - } - } - "limit" -> { - when (modifier["name"] as String) { - "limitToFirst" -> { - val value = when (val limit = modifier["limit"]) { - is Int -> limit - is Number -> limit.toInt() - else -> throw IllegalArgumentException("Invalid limit value: $limit") - } - query = query.limitToFirst(value) - } - "limitToLast" -> { - val value = when (val limit = modifier["limit"]) { - is Int -> limit - is Number -> limit.toInt() - else -> throw IllegalArgumentException("Invalid limit value: $limit") - } - query = query.limitToLast(value) - } - } - } - } - } + val query = queryFromModifiers(reference, request.modifiers) // Get the data query.get().addOnCompleteListener { task -> @@ -1143,11 +1011,12 @@ class FirebaseDatabasePlugin : // Helper method to get FirebaseDatabase from Pigeon app private fun getDatabaseFromPigeonApp(app: DatabasePigeonFirebaseApp): FirebaseDatabase { val firebaseApp = FirebaseApp.getInstance(app.appName) - val database = if (app.databaseURL != null) { - FirebaseDatabase.getInstance(firebaseApp, app.databaseURL) - } else { - FirebaseDatabase.getInstance(firebaseApp) - } + val database = + if (app.databaseURL != null) { + FirebaseDatabase.getInstance(firebaseApp, app.databaseURL) + } else { + FirebaseDatabase.getInstance(firebaseApp) + } // Apply settings carried on the Pigeon app object (idempotent across calls) try { @@ -1162,13 +1031,9 @@ class FirebaseDatabasePlugin : database.useEmulator(emulatorHost, emulatorPort.toInt()) } - app.settings.persistenceEnabled?.let { enabled -> - database.setPersistenceEnabled(enabled) - } + app.settings.persistenceEnabled?.let { enabled -> database.setPersistenceEnabled(enabled) } - app.settings.cacheSizeBytes?.let { size -> - database.setPersistenceCacheSizeBytes(size) - } + app.settings.cacheSizeBytes?.let { size -> database.setPersistenceCacheSizeBytes(size) } } catch (e: DatabaseException) { // Ignore ordering errors if the instance was already used; settings that require // pre-use configuration would have no effect and should not crash tests. diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterDataSnapshotPayload.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterDataSnapshotPayload.kt index 3687ebabeb3c..c95bc304ad64 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterDataSnapshotPayload.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterDataSnapshotPayload.kt @@ -10,7 +10,7 @@ import com.google.firebase.database.DataSnapshot import java.util.* class FlutterDataSnapshotPayload( - snapshot: DataSnapshot, + snapshot: DataSnapshot, ) { private var payloadMap: MutableMap = mutableMapOf() diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseAppRegistrar.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseAppRegistrar.kt index a78fd2044ecb..2c74d47c57eb 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseAppRegistrar.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseAppRegistrar.kt @@ -12,7 +12,7 @@ import com.google.firebase.platforminfo.LibraryVersionComponent @Keep class FlutterFirebaseAppRegistrar : ComponentRegistrar { override fun getComponents(): List> = - listOf( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION), - ) + listOf( + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION), + ) } diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseDatabaseException.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseDatabaseException.kt index 573a760f072e..c2f1a2f9915d 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseDatabaseException.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/FlutterFirebaseDatabaseException.kt @@ -13,91 +13,100 @@ import com.google.firebase.database.DatabaseException import java.util.* class FlutterFirebaseDatabaseException - @JvmOverloads - constructor( +@JvmOverloads +constructor( @NonNull val code: String, @NonNull val errorMessage: String, @Nullable additionalData: Map? = null, - ) : Exception(errorMessage) { - companion object { - const val UNKNOWN_ERROR_CODE = "unknown" - const val UNKNOWN_ERROR_MESSAGE = "An unknown error occurred" - private const val MODULE = "firebase_database" +) : Exception(errorMessage) { + companion object { + const val UNKNOWN_ERROR_CODE = "unknown" + const val UNKNOWN_ERROR_MESSAGE = "An unknown error occurred" + private const val MODULE = "firebase_database" - fun fromDatabaseError(e: DatabaseError): FlutterFirebaseDatabaseException { - val errorCode = e.code + fun fromDatabaseError(e: DatabaseError): FlutterFirebaseDatabaseException { + val errorCode = e.code - val (code, message) = + val (code, message) = when (errorCode) { - DatabaseError.DATA_STALE -> "data-stale" to "The transaction needs to be run again with current data." - DatabaseError.OPERATION_FAILED -> "failure" to "The server indicated that this operation failed." - DatabaseError.PERMISSION_DENIED -> "permission-denied" to "Client doesn't have permission to access the desired data." - DatabaseError.DISCONNECTED -> "disconnected" to "The operation had to be aborted due to a network disconnect." + DatabaseError.DATA_STALE -> + "data-stale" to "The transaction needs to be run again with current data." + DatabaseError.OPERATION_FAILED -> + "failure" to "The server indicated that this operation failed." + DatabaseError.PERMISSION_DENIED -> + "permission-denied" to "Client doesn't have permission to access the desired data." + DatabaseError.DISCONNECTED -> + "disconnected" to "The operation had to be aborted due to a network disconnect." DatabaseError.EXPIRED_TOKEN -> "expired-token" to "The supplied auth token has expired." DatabaseError.INVALID_TOKEN -> "invalid-token" to "The supplied auth token was invalid." DatabaseError.MAX_RETRIES -> "max-retries" to "The transaction had too many retries." - DatabaseError.OVERRIDDEN_BY_SET -> "overridden-by-set" to "The transaction was overridden by a subsequent set." + DatabaseError.OVERRIDDEN_BY_SET -> + "overridden-by-set" to "The transaction was overridden by a subsequent set." DatabaseError.UNAVAILABLE -> "unavailable" to "The service is unavailable." - DatabaseError.NETWORK_ERROR -> "network-error" to "The operation could not be performed due to a network error." - DatabaseError.WRITE_CANCELED -> "write-cancelled" to "The write was canceled by the user." + DatabaseError.NETWORK_ERROR -> + "network-error" to "The operation could not be performed due to a network error." + DatabaseError.WRITE_CANCELED -> + "write-cancelled" to "The write was canceled by the user." else -> UNKNOWN_ERROR_CODE to UNKNOWN_ERROR_MESSAGE } - if (code == UNKNOWN_ERROR_CODE) { - return unknown(e.message ?: UNKNOWN_ERROR_MESSAGE) - } - - val additionalData = mutableMapOf() - val errorDetails = e.details - additionalData[Constants.ERROR_DETAILS] = errorDetails - return FlutterFirebaseDatabaseException(code, message, additionalData) + if (code == UNKNOWN_ERROR_CODE) { + return unknown(e.message ?: UNKNOWN_ERROR_MESSAGE) } - fun fromDatabaseException(e: DatabaseException): FlutterFirebaseDatabaseException { - val error = DatabaseError.fromException(e) - return fromDatabaseError(error) - } + val additionalData = mutableMapOf() + val errorDetails = e.details + additionalData[Constants.ERROR_DETAILS] = errorDetails + return FlutterFirebaseDatabaseException(code, message, additionalData) + } - fun fromException(e: Exception?): FlutterFirebaseDatabaseException = + fun fromDatabaseException(e: DatabaseException): FlutterFirebaseDatabaseException { + val error = DatabaseError.fromException(e) + return fromDatabaseError(error) + } + + fun fromException(e: Exception?): FlutterFirebaseDatabaseException = if (e == null) unknown() else unknown(e.message ?: UNKNOWN_ERROR_MESSAGE) - fun unknown(): FlutterFirebaseDatabaseException = unknown(null) + fun unknown(): FlutterFirebaseDatabaseException = unknown(null) - fun unknown(errorMessage: String?): FlutterFirebaseDatabaseException { - val details = mutableMapOf() - var code = UNKNOWN_ERROR_CODE + fun unknown(errorMessage: String?): FlutterFirebaseDatabaseException { + val details = mutableMapOf() + var code = UNKNOWN_ERROR_CODE - var message = errorMessage + var message = errorMessage - if (errorMessage == null) { - message = UNKNOWN_ERROR_MESSAGE - } + if (errorMessage == null) { + message = UNKNOWN_ERROR_MESSAGE + } - when { - message?.contains("Index not defined, add \".indexOn\"") == true -> { - // No known error code for this in DatabaseError, so we manually have to - // detect it. - code = "index-not-defined" - message = message?.replaceFirst("java.lang.Exception: ", "") ?: UNKNOWN_ERROR_MESSAGE - } - message?.contains("Permission denied") == true || message?.contains("Client doesn't have permission") == true -> { - // Permission denied when using Firebase emulator does not correctly come - // through as a DatabaseError. - code = "permission-denied" - message = "Client doesn't have permission to access the desired data." - } + when { + message?.contains("Index not defined, add \".indexOn\"") == true -> { + // No known error code for this in DatabaseError, so we manually have to + // detect it. + code = "index-not-defined" + message = message?.replaceFirst("java.lang.Exception: ", "") ?: UNKNOWN_ERROR_MESSAGE + } + message?.contains("Permission denied") == true || + message?.contains("Client doesn't have permission") == true -> { + // Permission denied when using Firebase emulator does not correctly come + // through as a DatabaseError. + code = "permission-denied" + message = "Client doesn't have permission to access the desired data." } - - return FlutterFirebaseDatabaseException(code, message ?: UNKNOWN_ERROR_MESSAGE, details) } + + return FlutterFirebaseDatabaseException(code, message ?: UNKNOWN_ERROR_MESSAGE, details) } + } - val additionalData: Map = + val additionalData: Map = additionalData?.toMutableMap()?.apply { put(Constants.ERROR_CODE, code) put(Constants.ERROR_MESSAGE, errorMessage) - } ?: mutableMapOf().apply { - put(Constants.ERROR_CODE, code) - put(Constants.ERROR_MESSAGE, errorMessage) } - } + ?: mutableMapOf().apply { + put(Constants.ERROR_CODE, code) + put(Constants.ERROR_MESSAGE, errorMessage) + } +} diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/GeneratedAndroidFirebaseDatabase.g.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/GeneratedAndroidFirebaseDatabase.g.kt index ddef8fb19c37..dd778f34ea6b 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/GeneratedAndroidFirebaseDatabase.g.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/GeneratedAndroidFirebaseDatabase.g.kt @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -10,16 +10,17 @@ package io.flutter.plugins.firebase.database import android.util.Log import io.flutter.plugin.common.BasicMessageChannel import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec import io.flutter.plugin.common.StandardMessageCodec import java.io.ByteArrayOutputStream import java.nio.ByteBuffer + private object GeneratedAndroidFirebaseDatabasePigeonUtils { fun createConnectionError(channelName: String): FlutterError { - return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") } + return FlutterError( + "channel-error", "Unable to establish connection on channel: '$channelName'.", "") + } fun wrapResult(result: Any?): List { return listOf(result) @@ -27,72 +28,181 @@ private object GeneratedAndroidFirebaseDatabasePigeonUtils { fun wrapError(exception: Throwable): List { return if (exception is FlutterError) { - listOf( - exception.code, - exception.message, - exception.details - ) + listOf(exception.code, exception.message, exception.details) } else { listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) } } + + fun doubleEquals(a: Double, b: Double): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN()) + } + + fun floatEquals(a: Float, b: Float): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN()) + } + + fun doubleHash(d: Double): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (d == 0.0) 0.0 else d + val bits = java.lang.Double.doubleToLongBits(normalized) + return (bits xor (bits ushr 32)).toInt() + } + + fun floatHash(f: Float): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (f == 0.0f) 0.0f else f + return java.lang.Float.floatToIntBits(normalized) + } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a === b) { + return true + } + if (a == null || b == null) { + return false + } if (a is ByteArray && b is ByteArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is IntArray && b is IntArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is LongArray && b is LongArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is DoubleArray && b is DoubleArray) { - return a.contentEquals(b) + if (a.size != b.size) return false + for (i in a.indices) { + if (!doubleEquals(a[i], b[i])) return false + } + return true + } + if (a is FloatArray && b is FloatArray) { + if (a.size != b.size) return false + for (i in a.indices) { + if (!floatEquals(a[i], b[i])) return false + } + return true } if (a is Array<*> && b is Array<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + for (i in a.indices) { + if (!deepEquals(a[i], b[i])) return false + } + return true } if (a is List<*> && b is List<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + val iterA = a.iterator() + val iterB = b.iterator() + while (iterA.hasNext() && iterB.hasNext()) { + if (!deepEquals(iterA.next(), iterB.next())) return false + } + return true } if (a is Map<*, *> && b is Map<*, *>) { - return a.size == b.size && a.all { - (b as Map).containsKey(it.key) && - deepEquals(it.value, b[it.key]) + if (a.size != b.size) return false + for (entry in a) { + val key = entry.key + var found = false + for (bEntry in b) { + if (deepEquals(key, bEntry.key)) { + if (deepEquals(entry.value, bEntry.value)) { + found = true + break + } else { + return false + } + } + } + if (!found) return false } + return true + } + if (a is Double && b is Double) { + return doubleEquals(a, b) + } + if (a is Float && b is Float) { + return floatEquals(a, b) } return a == b } - + + fun deepHash(value: Any?): Int { + return when (value) { + null -> 0 + is ByteArray -> value.contentHashCode() + is IntArray -> value.contentHashCode() + is LongArray -> value.contentHashCode() + is DoubleArray -> { + var result = 1 + for (item in value) { + result = 31 * result + doubleHash(item) + } + result + } + is FloatArray -> { + var result = 1 + for (item in value) { + result = 31 * result + floatHash(item) + } + result + } + is Array<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is List<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is Map<*, *> -> { + var result = 0 + for (entry in value) { + result += ((deepHash(entry.key) * 31) xor deepHash(entry.value)) + } + result + } + is Double -> doubleHash(value) + is Float -> floatHash(value) + else -> value.hashCode() + } + } } /** * 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 FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() /** Generated class from Pigeon that represents data sent in messages. */ -data class DatabasePigeonSettings ( - val persistenceEnabled: Boolean? = null, - val cacheSizeBytes: Long? = null, - val loggingEnabled: Boolean? = null, - val emulatorHost: String? = null, - val emulatorPort: Long? = null -) - { +data class DatabasePigeonSettings( + val persistenceEnabled: Boolean? = null, + val cacheSizeBytes: Long? = null, + val loggingEnabled: Boolean? = null, + val emulatorHost: String? = null, + val emulatorPort: Long? = null +) { companion object { fun fromList(pigeonVar_list: List): DatabasePigeonSettings { val persistenceEnabled = pigeonVar_list[0] as Boolean? @@ -100,37 +210,59 @@ data class DatabasePigeonSettings ( val loggingEnabled = pigeonVar_list[2] as Boolean? val emulatorHost = pigeonVar_list[3] as String? val emulatorPort = pigeonVar_list[4] as Long? - return DatabasePigeonSettings(persistenceEnabled, cacheSizeBytes, loggingEnabled, emulatorHost, emulatorPort) + return DatabasePigeonSettings( + persistenceEnabled, cacheSizeBytes, loggingEnabled, emulatorHost, emulatorPort) } } + fun toList(): List { return listOf( - persistenceEnabled, - cacheSizeBytes, - loggingEnabled, - emulatorHost, - emulatorPort, + persistenceEnabled, + cacheSizeBytes, + loggingEnabled, + emulatorHost, + emulatorPort, ) } + override fun equals(other: Any?): Boolean { - if (other !is DatabasePigeonSettings) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as DatabasePigeonSettings + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.persistenceEnabled, other.persistenceEnabled) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.cacheSizeBytes, other.cacheSizeBytes) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.loggingEnabled, other.loggingEnabled) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.emulatorHost, other.emulatorHost) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.emulatorPort, other.emulatorPort) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = + 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.persistenceEnabled) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.cacheSizeBytes) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.loggingEnabled) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.emulatorHost) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.emulatorPort) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class DatabasePigeonFirebaseApp ( - val appName: String, - val databaseURL: String? = null, - val settings: DatabasePigeonSettings -) - { +data class DatabasePigeonFirebaseApp( + val appName: String, + val databaseURL: String? = null, + val settings: DatabasePigeonSettings +) { companion object { fun fromList(pigeonVar_list: List): DatabasePigeonFirebaseApp { val appName = pigeonVar_list[0] as String @@ -139,60 +271,77 @@ data class DatabasePigeonFirebaseApp ( return DatabasePigeonFirebaseApp(appName, databaseURL, settings) } } + fun toList(): List { return listOf( - appName, - databaseURL, - settings, + appName, + databaseURL, + settings, ) } + override fun equals(other: Any?): Boolean { - if (other !is DatabasePigeonFirebaseApp) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as DatabasePigeonFirebaseApp + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.appName, other.appName) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.databaseURL, other.databaseURL) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.settings, other.settings) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.appName) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.databaseURL) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.settings) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class DatabaseReferencePlatform ( - val path: String -) - { +data class DatabaseReferencePlatform(val path: String) { companion object { fun fromList(pigeonVar_list: List): DatabaseReferencePlatform { val path = pigeonVar_list[0] as String return DatabaseReferencePlatform(path) } } + fun toList(): List { return listOf( - path, + path, ) } + override fun equals(other: Any?): Boolean { - if (other !is DatabaseReferencePlatform) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as DatabaseReferencePlatform + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.path, other.path) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.path) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class DatabaseReferenceRequest ( - val path: String, - val value: Any? = null, - val priority: Any? = null -) - { +data class DatabaseReferenceRequest( + val path: String, + val value: Any? = null, + val priority: Any? = null +) { companion object { fun fromList(pigeonVar_list: List): DatabaseReferenceRequest { val path = pigeonVar_list[0] as String @@ -201,31 +350,39 @@ data class DatabaseReferenceRequest ( return DatabaseReferenceRequest(path, value, priority) } } + fun toList(): List { return listOf( - path, - value, - priority, + path, + value, + priority, ) } + override fun equals(other: Any?): Boolean { - if (other !is DatabaseReferenceRequest) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as DatabaseReferenceRequest + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.path, other.path) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.value, other.value) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.priority, other.priority) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.path) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.value) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.priority) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class UpdateRequest ( - val path: String, - val value: Map -) - { +data class UpdateRequest(val path: String, val value: Map) { companion object { fun fromList(pigeonVar_list: List): UpdateRequest { val path = pigeonVar_list[0] as String @@ -233,31 +390,40 @@ data class UpdateRequest ( return UpdateRequest(path, value) } } + fun toList(): List { return listOf( - path, - value, + path, + value, ) } + override fun equals(other: Any?): Boolean { - if (other !is UpdateRequest) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as UpdateRequest + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.path, other.path) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.value, other.value) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.path) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.value) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class TransactionRequest ( - val path: String, - val transactionKey: Long, - val applyLocally: Boolean -) - { +data class TransactionRequest( + val path: String, + val transactionKey: Long, + val applyLocally: Boolean +) { companion object { fun fromList(pigeonVar_list: List): TransactionRequest { val path = pigeonVar_list[0] as String @@ -266,32 +432,45 @@ data class TransactionRequest ( return TransactionRequest(path, transactionKey, applyLocally) } } + fun toList(): List { return listOf( - path, - transactionKey, - applyLocally, + path, + transactionKey, + applyLocally, ) } + override fun equals(other: Any?): Boolean { - if (other !is TransactionRequest) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as TransactionRequest + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.path, other.path) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.transactionKey, other.transactionKey) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals( + this.applyLocally, other.applyLocally) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.path) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.transactionKey) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.applyLocally) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class QueryRequest ( - val path: String, - val modifiers: List>, - val value: Boolean? = null -) - { +data class QueryRequest( + val path: String, + val modifiers: List>, + val value: Boolean? = null +) { companion object { fun fromList(pigeonVar_list: List): QueryRequest { val path = pigeonVar_list[0] as String @@ -300,32 +479,43 @@ data class QueryRequest ( return QueryRequest(path, modifiers, value) } } + fun toList(): List { return listOf( - path, - modifiers, - value, + path, + modifiers, + value, ) } + override fun equals(other: Any?): Boolean { - if (other !is QueryRequest) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as QueryRequest + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.path, other.path) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.modifiers, other.modifiers) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.value, other.value) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.path) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.modifiers) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.value) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class TransactionHandlerResult ( - val value: Any? = null, - val aborted: Boolean, - val exception: Boolean -) - { +data class TransactionHandlerResult( + val value: Any? = null, + val aborted: Boolean, + val exception: Boolean +) { companion object { fun fromList(pigeonVar_list: List): TransactionHandlerResult { val value = pigeonVar_list[0] @@ -334,71 +524,69 @@ data class TransactionHandlerResult ( return TransactionHandlerResult(value, aborted, exception) } } + fun toList(): List { return listOf( - value, - aborted, - exception, + value, + aborted, + exception, ) } + override fun equals(other: Any?): Boolean { - if (other !is TransactionHandlerResult) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as TransactionHandlerResult + return GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.value, other.value) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.aborted, other.aborted) && + GeneratedAndroidFirebaseDatabasePigeonUtils.deepEquals(this.exception, other.exception) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.value) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.aborted) + result = 31 * result + GeneratedAndroidFirebaseDatabasePigeonUtils.deepHash(this.exception) + return result + } } + private open class GeneratedAndroidFirebaseDatabasePigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - DatabasePigeonSettings.fromList(it) - } + return (readValue(buffer) as? List)?.let { DatabasePigeonSettings.fromList(it) } } 130.toByte() -> { - return (readValue(buffer) as? List)?.let { - DatabasePigeonFirebaseApp.fromList(it) - } + return (readValue(buffer) as? List)?.let { DatabasePigeonFirebaseApp.fromList(it) } } 131.toByte() -> { - return (readValue(buffer) as? List)?.let { - DatabaseReferencePlatform.fromList(it) - } + return (readValue(buffer) as? List)?.let { DatabaseReferencePlatform.fromList(it) } } 132.toByte() -> { - return (readValue(buffer) as? List)?.let { - DatabaseReferenceRequest.fromList(it) - } + return (readValue(buffer) as? List)?.let { DatabaseReferenceRequest.fromList(it) } } 133.toByte() -> { - return (readValue(buffer) as? List)?.let { - UpdateRequest.fromList(it) - } + return (readValue(buffer) as? List)?.let { UpdateRequest.fromList(it) } } 134.toByte() -> { - return (readValue(buffer) as? List)?.let { - TransactionRequest.fromList(it) - } + return (readValue(buffer) as? List)?.let { TransactionRequest.fromList(it) } } 135.toByte() -> { - return (readValue(buffer) as? List)?.let { - QueryRequest.fromList(it) - } + return (readValue(buffer) as? List)?.let { QueryRequest.fromList(it) } } 136.toByte() -> { - return (readValue(buffer) as? List)?.let { - TransactionHandlerResult.fromList(it) - } + return (readValue(buffer) as? List)?.let { TransactionHandlerResult.fromList(it) } } else -> super.readValueOfType(type, buffer) } } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { is DatabasePigeonSettings -> { stream.write(129) @@ -437,43 +625,150 @@ private open class GeneratedAndroidFirebaseDatabasePigeonCodec : StandardMessage } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface FirebaseDatabaseHostApi { fun goOnline(app: DatabasePigeonFirebaseApp, callback: (Result) -> Unit) + fun goOffline(app: DatabasePigeonFirebaseApp, callback: (Result) -> Unit) - fun setPersistenceEnabled(app: DatabasePigeonFirebaseApp, enabled: Boolean, callback: (Result) -> Unit) - fun setPersistenceCacheSizeBytes(app: DatabasePigeonFirebaseApp, cacheSize: Long, callback: (Result) -> Unit) - fun setLoggingEnabled(app: DatabasePigeonFirebaseApp, enabled: Boolean, callback: (Result) -> Unit) - fun useDatabaseEmulator(app: DatabasePigeonFirebaseApp, host: String, port: Long, callback: (Result) -> Unit) - fun ref(app: DatabasePigeonFirebaseApp, path: String?, callback: (Result) -> Unit) - fun refFromURL(app: DatabasePigeonFirebaseApp, url: String, callback: (Result) -> Unit) + + fun setPersistenceEnabled( + app: DatabasePigeonFirebaseApp, + enabled: Boolean, + callback: (Result) -> Unit + ) + + fun setPersistenceCacheSizeBytes( + app: DatabasePigeonFirebaseApp, + cacheSize: Long, + callback: (Result) -> Unit + ) + + fun setLoggingEnabled( + app: DatabasePigeonFirebaseApp, + enabled: Boolean, + callback: (Result) -> Unit + ) + + fun useDatabaseEmulator( + app: DatabasePigeonFirebaseApp, + host: String, + port: Long, + callback: (Result) -> Unit + ) + + fun ref( + app: DatabasePigeonFirebaseApp, + path: String?, + callback: (Result) -> Unit + ) + + fun refFromURL( + app: DatabasePigeonFirebaseApp, + url: String, + callback: (Result) -> Unit + ) + fun purgeOutstandingWrites(app: DatabasePigeonFirebaseApp, callback: (Result) -> Unit) - fun databaseReferenceSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (Result) -> Unit) - fun databaseReferenceSetWithPriority(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (Result) -> Unit) - fun databaseReferenceUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, callback: (Result) -> Unit) - fun databaseReferenceSetPriority(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (Result) -> Unit) - fun databaseReferenceRunTransaction(app: DatabasePigeonFirebaseApp, request: TransactionRequest, callback: (Result) -> Unit) - fun databaseReferenceGetTransactionResult(app: DatabasePigeonFirebaseApp, transactionKey: Long, callback: (Result>) -> Unit) - fun onDisconnectSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (Result) -> Unit) - fun onDisconnectSetWithPriority(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, callback: (Result) -> Unit) - fun onDisconnectUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, callback: (Result) -> Unit) - fun onDisconnectCancel(app: DatabasePigeonFirebaseApp, path: String, callback: (Result) -> Unit) - fun queryObserve(app: DatabasePigeonFirebaseApp, request: QueryRequest, callback: (Result) -> Unit) - fun queryKeepSynced(app: DatabasePigeonFirebaseApp, request: QueryRequest, callback: (Result) -> Unit) - fun queryGet(app: DatabasePigeonFirebaseApp, request: QueryRequest, callback: (Result>) -> Unit) + + fun databaseReferenceSet( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (Result) -> Unit + ) + + fun databaseReferenceSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (Result) -> Unit + ) + + fun databaseReferenceUpdate( + app: DatabasePigeonFirebaseApp, + request: UpdateRequest, + callback: (Result) -> Unit + ) + + fun databaseReferenceSetPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (Result) -> Unit + ) + + fun databaseReferenceRunTransaction( + app: DatabasePigeonFirebaseApp, + request: TransactionRequest, + callback: (Result) -> Unit + ) + + fun databaseReferenceGetTransactionResult( + app: DatabasePigeonFirebaseApp, + transactionKey: Long, + callback: (Result>) -> Unit + ) + + fun onDisconnectSet( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (Result) -> Unit + ) + + fun onDisconnectSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + callback: (Result) -> Unit + ) + + fun onDisconnectUpdate( + app: DatabasePigeonFirebaseApp, + request: UpdateRequest, + callback: (Result) -> Unit + ) + + fun onDisconnectCancel( + app: DatabasePigeonFirebaseApp, + path: String, + callback: (Result) -> Unit + ) + + fun queryObserve( + app: DatabasePigeonFirebaseApp, + request: QueryRequest, + callback: (Result) -> Unit + ) + + fun queryKeepSynced( + app: DatabasePigeonFirebaseApp, + request: QueryRequest, + callback: (Result) -> Unit + ) + + fun queryGet( + app: DatabasePigeonFirebaseApp, + request: QueryRequest, + callback: (Result>) -> Unit + ) companion object { /** The codec used by FirebaseDatabaseHostApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidFirebaseDatabasePigeonCodec() - } - /** Sets up an instance of `FirebaseDatabaseHostApi` to handle messages through the `binaryMessenger`. */ + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseDatabasePigeonCodec() } + /** + * Sets up an instance of `FirebaseDatabaseHostApi` to handle messages through the + * `binaryMessenger`. + */ @JvmOverloads - fun setUp(binaryMessenger: BinaryMessenger, api: FirebaseDatabaseHostApi?, messageChannelSuffix: String = "") { - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebaseDatabaseHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -492,7 +787,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -511,7 +810,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -531,7 +834,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -551,7 +858,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -571,7 +882,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -592,7 +907,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -613,7 +932,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -634,7 +957,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -653,7 +980,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -673,7 +1004,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -693,7 +1028,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -713,7 +1052,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -733,7 +1076,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -753,13 +1100,18 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val appArg = args[0] as DatabasePigeonFirebaseApp val transactionKeyArg = args[1] as Long - api.databaseReferenceGetTransactionResult(appArg, transactionKeyArg) { result: Result> -> + api.databaseReferenceGetTransactionResult(appArg, transactionKeyArg) { + result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebaseDatabasePigeonUtils.wrapError(error)) @@ -774,7 +1126,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -794,7 +1150,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -814,7 +1174,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -834,7 +1198,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -854,7 +1222,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -875,7 +1247,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -895,7 +1271,11 @@ interface FirebaseDatabaseHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -919,31 +1299,45 @@ interface FirebaseDatabaseHostApi { } } /** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ -class FirebaseDatabaseFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { +class FirebaseDatabaseFlutterApi( + private val binaryMessenger: BinaryMessenger, + private val messageChannelSuffix: String = "" +) { companion object { /** The codec used by FirebaseDatabaseFlutterApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidFirebaseDatabasePigeonCodec() - } + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseDatabasePigeonCodec() } } - fun callTransactionHandler(transactionKeyArg: Long, snapshotValueArg: Any?, callback: (Result) -> Unit) -{ - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" - val channelName = "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseFlutterApi.callTransactionHandler$separatedMessageChannelSuffix" + + fun callTransactionHandler( + transactionKeyArg: Long, + snapshotValueArg: Any?, + callback: (Result) -> Unit + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseFlutterApi.callTransactionHandler$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(listOf(transactionKeyArg, snapshotValueArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { - callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", ""))) + callback( + Result.failure( + FlutterError( + "null-error", + "Flutter api returned null value for non-null return value.", + ""))) } else { val output = it[0] as TransactionHandlerResult callback(Result.success(output)) } } else { - callback(Result.failure(GeneratedAndroidFirebaseDatabasePigeonUtils.createConnectionError(channelName))) - } + callback( + Result.failure( + GeneratedAndroidFirebaseDatabasePigeonUtils.createConnectionError(channelName))) + } } } } diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/QueryBuilder.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/QueryBuilder.kt index 8cadd84e4fe8..9e8a97bd4f70 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/QueryBuilder.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/QueryBuilder.kt @@ -9,48 +9,52 @@ package io.flutter.plugins.firebase.database import androidx.annotation.NonNull import com.google.firebase.database.DatabaseReference import com.google.firebase.database.Query -import java.util.* class QueryBuilder - @JvmOverloads - constructor( +@JvmOverloads +constructor( @NonNull ref: DatabaseReference, - @NonNull private val modifiers: List>, - ) { - private var query: Query = ref + @NonNull private val modifiers: List>, +) { + private var query: Query = ref - fun build(): Query { - if (modifiers.isEmpty()) return query + fun build(): Query { + if (modifiers.isEmpty()) return query - for (modifier in modifiers) { - val type = modifier["type"] as String + for (modifier in modifiers) { + val type = modifier["type"] as String - when (type) { - Constants.LIMIT -> limit(modifier) - Constants.CURSOR -> cursor(modifier) - Constants.ORDER_BY -> orderBy(modifier) - } + when (type) { + Constants.LIMIT -> limit(modifier) + Constants.CURSOR -> cursor(modifier) + Constants.ORDER_BY -> orderBy(modifier) } - - return query } - private fun limit(modifier: Map) { - val name = modifier["name"] as String - val value = modifier["limit"] as Int + return query + } + + private fun limit(modifier: Map) { + val name = modifier["name"] as String + val value = + when (val limit = modifier["limit"]) { + is Int -> limit + is Number -> limit.toInt() + else -> throw IllegalArgumentException("Invalid limit value: $limit") + } - query = + query = when (name) { Constants.LIMIT_TO_FIRST -> query.limitToFirst(value) Constants.LIMIT_TO_LAST -> query.limitToLast(value) else -> query } - } + } - private fun orderBy(modifier: Map) { - val name = modifier["name"] as String + private fun orderBy(modifier: Map) { + val name = modifier["name"] as String - query = + query = when (name) { "orderByKey" -> query.orderByKey() "orderByValue" -> query.orderByValue() @@ -61,64 +65,78 @@ class QueryBuilder } else -> query } - } + } - private fun cursor(modifier: Map) { - val name = modifier["name"] as String + private fun cursor(modifier: Map) { + val name = modifier["name"] as String - when (name) { - Constants.START_AT -> startAt(modifier) - Constants.START_AFTER -> startAfter(modifier) - Constants.END_AT -> endAt(modifier) - Constants.END_BEFORE -> endBefore(modifier) - } + when (name) { + Constants.START_AT -> startAt(modifier) + Constants.START_AFTER -> startAfter(modifier) + Constants.END_AT -> endAt(modifier) + Constants.END_BEFORE -> endBefore(modifier) } + } - private fun startAt(modifier: Map) { - val value = modifier["value"] - val key = modifier["key"] as String? + private fun startAt(modifier: Map) { + val value = modifier["value"] + val key = modifier["key"] as String? - query = + query = when (value) { is Boolean -> if (key == null) query.startAt(value) else query.startAt(value, key) - is Number -> if (key == null) query.startAt(value.toDouble()) else query.startAt(value.toDouble(), key) - else -> if (key == null) query.startAt(value as String) else query.startAt(value as String, key) + is Number -> + if (key == null) query.startAt(value.toDouble()) + else query.startAt(value.toDouble(), key) + else -> + if (key == null) query.startAt(value.toString()) + else query.startAt(value.toString(), key) } - } + } - private fun startAfter(modifier: Map) { - val value = modifier["value"] - val key = modifier["key"] as String? + private fun startAfter(modifier: Map) { + val value = modifier["value"] + val key = modifier["key"] as String? - query = + query = when (value) { is Boolean -> if (key == null) query.startAfter(value) else query.startAfter(value, key) - is Number -> if (key == null) query.startAfter(value.toDouble()) else query.startAfter(value.toDouble(), key) - else -> if (key == null) query.startAfter(value as String) else query.startAfter(value as String, key) + is Number -> + if (key == null) query.startAfter(value.toDouble()) + else query.startAfter(value.toDouble(), key) + else -> + if (key == null) query.startAfter(value.toString()) + else query.startAfter(value.toString(), key) } - } + } - private fun endAt(modifier: Map) { - val value = modifier["value"] - val key = modifier["key"] as String? + private fun endAt(modifier: Map) { + val value = modifier["value"] + val key = modifier["key"] as String? - query = + query = when (value) { is Boolean -> if (key == null) query.endAt(value) else query.endAt(value, key) - is Number -> if (key == null) query.endAt(value.toDouble()) else query.endAt(value.toDouble(), key) - else -> if (key == null) query.endAt(value as String) else query.endAt(value as String, key) + is Number -> + if (key == null) query.endAt(value.toDouble()) else query.endAt(value.toDouble(), key) + else -> + if (key == null) query.endAt(value.toString()) else query.endAt(value.toString(), key) } - } + } - private fun endBefore(modifier: Map) { - val value = modifier["value"] - val key = modifier["key"] as String? + private fun endBefore(modifier: Map) { + val value = modifier["value"] + val key = modifier["key"] as String? - query = + query = when (value) { is Boolean -> if (key == null) query.endBefore(value) else query.endBefore(value, key) - is Number -> if (key == null) query.endBefore(value.toDouble()) else query.endBefore(value.toDouble(), key) - else -> if (key == null) query.endBefore(value as String) else query.endBefore(value as String, key) + is Number -> + if (key == null) query.endBefore(value.toDouble()) + else query.endBefore(value.toDouble(), key) + else -> + if (key == null) query.endBefore(value.toString()) + else query.endBefore(value.toString(), key) } - } } +} diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionExecutor.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionExecutor.kt index 4f31edf655e2..c6be9ca7f0bd 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionExecutor.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionExecutor.kt @@ -15,8 +15,9 @@ import io.flutter.plugin.common.MethodChannel import java.util.* import java.util.concurrent.ExecutionException -class TransactionExecutor constructor( - private val channel: MethodChannel, +class TransactionExecutor +constructor( + private val channel: MethodChannel, ) { private val completion = TaskCompletionSource() @@ -24,46 +25,46 @@ class TransactionExecutor constructor( fun execute(arguments: Map): Any { Handler(Looper.getMainLooper()).post { channel.invokeMethod( - Constants.METHOD_CALL_TRANSACTION_HANDLER, - arguments, - object : MethodChannel.Result { - override fun success( - @Nullable result: Any?, - ) { - completion.setResult(result) - } + Constants.METHOD_CALL_TRANSACTION_HANDLER, + arguments, + object : MethodChannel.Result { + override fun success( + @Nullable result: Any?, + ) { + completion.setResult(result) + } - @Suppress("UNCHECKED_CAST") - override fun error( - errorCode: String, - @Nullable errorMessage: String?, - @Nullable errorDetails: Any?, - ) { - var message = errorMessage - val additionalData = mutableMapOf() + @Suppress("UNCHECKED_CAST") + override fun error( + errorCode: String, + @Nullable errorMessage: String?, + @Nullable errorDetails: Any?, + ) { + var message = errorMessage + val additionalData = mutableMapOf() - if (message == null) { - message = FlutterFirebaseDatabaseException.UNKNOWN_ERROR_MESSAGE - } + if (message == null) { + message = FlutterFirebaseDatabaseException.UNKNOWN_ERROR_MESSAGE + } - if (errorDetails is Map<*, *>) { - additionalData.putAll(errorDetails as Map) - } + if (errorDetails is Map<*, *>) { + additionalData.putAll(errorDetails as Map) + } - val e = - FlutterFirebaseDatabaseException( - errorCode, - message, - additionalData, - ) + val e = + FlutterFirebaseDatabaseException( + errorCode, + message, + additionalData, + ) - completion.setException(e) - } + completion.setException(e) + } - override fun notImplemented() { - // never called - } - }, + override fun notImplemented() { + // never called + } + }, ) } diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionHandler.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionHandler.kt index 5bc549ba629e..675ecac3bf5c 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionHandler.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/TransactionHandler.kt @@ -19,87 +19,89 @@ import com.google.firebase.database.Transaction.Handler import io.flutter.plugin.common.MethodChannel class TransactionHandler - @JvmOverloads - constructor( +@JvmOverloads +constructor( @NonNull private val channel: MethodChannel, private val transactionKey: Int, - ) : Handler { - private val transactionCompletionSource = TaskCompletionSource>() +) : Handler { + private val transactionCompletionSource = TaskCompletionSource>() - fun getTask(): Task> = transactionCompletionSource.task + fun getTask(): Task> = transactionCompletionSource.task - @NonNull - override fun doTransaction( + @NonNull + override fun doTransaction( @NonNull currentData: MutableData, - ): Transaction.Result { - val snapshotMap = + ): Transaction.Result { + val snapshotMap = mapOf( - Constants.KEY to (currentData.key ?: ""), - Constants.VALUE to currentData.value, + Constants.KEY to (currentData.key ?: ""), + Constants.VALUE to currentData.value, ) - val transactionArgs = + val transactionArgs = mapOf( - Constants.SNAPSHOT to snapshotMap, - Constants.TRANSACTION_KEY to transactionKey, + Constants.SNAPSHOT to snapshotMap, + Constants.TRANSACTION_KEY to transactionKey, ) - return try { - val executor = TransactionExecutor(channel) - val updatedData: Any? = executor.execute(transactionArgs) + return try { + val executor = TransactionExecutor(channel) + val updatedData: Any? = executor.execute(transactionArgs) - @Suppress("UNCHECKED_CAST") - val transactionHandlerResult: Map = + @Suppress("UNCHECKED_CAST") + val transactionHandlerResult: Map = when (updatedData) { is Map<*, *> -> updatedData as Map null -> emptyMap() else -> { - Log.e("firebase_database", "Unexpected transaction result type: ${updatedData::class.java}") + Log.e( + "firebase_database", + "Unexpected transaction result type: ${updatedData::class.java}") emptyMap() } } - val aborted: Boolean = (transactionHandlerResult["aborted"] as? Boolean) ?: false - val exception: Boolean = (transactionHandlerResult["exception"] as? Boolean) ?: false + val aborted: Boolean = (transactionHandlerResult["aborted"] as? Boolean) ?: false + val exception: Boolean = (transactionHandlerResult["exception"] as? Boolean) ?: false - if (aborted || exception) { - Transaction.abort() - } else { - if (transactionHandlerResult.containsKey("value")) { - currentData.value = transactionHandlerResult["value"] - } - Transaction.success(currentData) - } - } catch (e: Exception) { - Log.e("firebase_database", "An unexpected exception occurred for a transaction.", e) + if (aborted || exception) { Transaction.abort() + } else { + if (transactionHandlerResult.containsKey("value")) { + currentData.value = transactionHandlerResult["value"] + } + Transaction.success(currentData) } + } catch (e: Exception) { + Log.e("firebase_database", "An unexpected exception occurred for a transaction.", e) + Transaction.abort() } + } - override fun onComplete( + override fun onComplete( @Nullable error: DatabaseError?, committed: Boolean, @Nullable currentData: DataSnapshot?, - ) { - when { - error != null -> { - transactionCompletionSource.setException( + ) { + when { + error != null -> { + transactionCompletionSource.setException( FlutterFirebaseDatabaseException.fromDatabaseError(error), - ) - } - currentData != null -> { - val payload = FlutterDataSnapshotPayload(currentData) - val additionalParams: MutableMap = + ) + } + currentData != null -> { + val payload = FlutterDataSnapshotPayload(currentData) + val additionalParams: MutableMap = mutableMapOf( - Constants.COMMITTED to committed, + Constants.COMMITTED to committed, ) - transactionCompletionSource.setResult( + transactionCompletionSource.setResult( payload.withAdditionalParams(additionalParams).toMap(), - ) - } - else -> { - transactionCompletionSource.setResult(emptyMap()) - } + ) + } + else -> { + transactionCompletionSource.setResult(emptyMap()) } } } +} diff --git a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ValueEventsProxy.kt b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ValueEventsProxy.kt index 299a129e86b7..41c46a6a54cd 100644 --- a/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ValueEventsProxy.kt +++ b/packages/firebase_database/firebase_database/android/src/main/kotlin/io/flutter/plugins/firebase/database/ValueEventsProxy.kt @@ -13,21 +13,20 @@ import com.google.firebase.database.ValueEventListener import io.flutter.plugin.common.EventChannel.EventSink class ValueEventsProxy - @JvmOverloads - constructor( +@JvmOverloads +constructor( @NonNull eventSink: EventSink, - ) : EventsProxy(eventSink, Constants.EVENT_TYPE_VALUE), - ValueEventListener { - override fun onDataChange( +) : EventsProxy(eventSink, Constants.EVENT_TYPE_VALUE), ValueEventListener { + override fun onDataChange( @NonNull snapshot: DataSnapshot, - ) { - sendEvent(Constants.EVENT_TYPE_VALUE, snapshot, null) - } + ) { + sendEvent(Constants.EVENT_TYPE_VALUE, snapshot, null) + } - override fun onCancelled( + override fun onCancelled( @NonNull error: DatabaseError, - ) { - val e = FlutterFirebaseDatabaseException.fromDatabaseError(error) - eventSink.error(e.code, e.message, e.additionalData) - } + ) { + val e = FlutterFirebaseDatabaseException.fromDatabaseError(error) + eventSink.error(e.code, e.message, e.additionalData) } +} diff --git a/packages/firebase_database/firebase_database/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/database/example/MainActivity.kt b/packages/firebase_database/firebase_database/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/database/example/MainActivity.kt index 8453022cc73f..0fcc054366fe 100644 --- a/packages/firebase_database/firebase_database/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/database/example/MainActivity.kt +++ b/packages/firebase_database/firebase_database/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/database/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.database.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_database/firebase_database/example/macos/Runner/AppDelegate.swift b/packages/firebase_database/firebase_database/example/macos/Runner/AppDelegate.swift index dd8f68d76ac5..b3c176141221 100644 --- a/packages/firebase_database/firebase_database/example/macos/Runner/AppDelegate.swift +++ b/packages/firebase_database/firebase_database/example/macos/Runner/AppDelegate.swift @@ -6,7 +6,7 @@ class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } - + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } diff --git a/packages/firebase_database/firebase_database/example/pubspec.yaml b/packages/firebase_database/firebase_database/example/pubspec.yaml index 44454d902198..fd0970d6a2e6 100755 --- a/packages/firebase_database/firebase_database/example/pubspec.yaml +++ b/packages/firebase_database/firebase_database/example/pubspec.yaml @@ -1,13 +1,14 @@ name: firebase_database_example description: Demonstrates how to use the firebase_database plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_database: ^12.2.0 + firebase_core: ^4.11.0 + firebase_database: ^12.4.4 flutter: sdk: flutter diff --git a/packages/firebase_database/firebase_database/ios/firebase_database/Package.swift b/packages/firebase_database/firebase_database/ios/firebase_database/Package.swift index 2534193a23c4..d0e60a5b8670 100644 --- a/packages/firebase_database/firebase_database/ios/firebase_database/Package.swift +++ b/packages/firebase_database/firebase_database/ios/firebase_database/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "12.1.4" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "12.4.4" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_database", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-database", targets: ["firebase_database"]), + .library(name: "firebase-database", targets: ["firebase_database"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-rtdb\""), ] - ), + ) ] ) diff --git a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseObserveStreamHandler.swift b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseObserveStreamHandler.swift index 4b147e49db5b..86029a6d5d39 100644 --- a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseObserveStreamHandler.swift +++ b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseObserveStreamHandler.swift @@ -22,16 +22,17 @@ import FirebaseDatabase } func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) - -> FlutterError? { + -> FlutterError? + { guard let args = arguments as? [String: Any], - let eventTypeString = args["eventType"] as? String + let eventTypeString = args["eventType"] as? String else { return nil } let observeBlock: (DataSnapshot, String?) -> Void = { [weak self] snapshot, previousChildKey in var eventDictionary: [String: Any] = [ - "eventType": eventTypeString, + "eventType": eventTypeString ] let snapshotDict = FLTFirebaseDatabaseUtils.dictionary( diff --git a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabasePlugin.swift b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabasePlugin.swift index f4f0ac77b8c0..469164b0256f 100644 --- a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabasePlugin.swift +++ b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabasePlugin.swift @@ -17,14 +17,15 @@ import Foundation #else import firebase_core_shared #endif -import FirebaseDatabase /// Channel name constant to match macOS implementation +// swift-format-ignore: AlwaysUseLowerCamelCase let FLTFirebaseDatabaseChannelName = "plugins.flutter.io/firebase_database" @objc(FLTFirebaseDatabasePlugin) public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePluginProtocol, - FirebaseDatabaseHostApi { + FirebaseDatabaseHostApi +{ private var binaryMessenger: FlutterBinaryMessenger private static var cachedDatabaseInstances: [String: Database] = [:] private var streamHandlers: [String: FLTFirebaseDatabaseObserveStreamHandler] = [:] @@ -104,22 +105,28 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug // MARK: - Database Management - func goOnline(app: DatabasePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func goOnline( + app: DatabasePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) database.goOnline() completion(.success(())) } - func goOffline(app: DatabasePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func goOffline( + app: DatabasePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) database.goOffline() completion(.success(())) } - func setPersistenceEnabled(app: DatabasePigeonFirebaseApp, enabled: Bool, - completion: @escaping (Result) -> Void) { + func setPersistenceEnabled( + app: DatabasePigeonFirebaseApp, enabled: Bool, + completion: @escaping (Result) -> Void + ) { let instanceKey = app.appName + (app.databaseURL ?? "") if Self.cachedDatabaseInstances[instanceKey] != nil { completion(.success(())) @@ -131,8 +138,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug completion(.success(())) } - func setPersistenceCacheSizeBytes(app: DatabasePigeonFirebaseApp, cacheSize: Int64, - completion: @escaping (Result) -> Void) { + func setPersistenceCacheSizeBytes( + app: DatabasePigeonFirebaseApp, cacheSize: Int64, + completion: @escaping (Result) -> Void + ) { let instanceKey = app.appName + (app.databaseURL ?? "") if Self.cachedDatabaseInstances[instanceKey] != nil { completion(.success(())) @@ -148,37 +157,53 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug completion(.success(())) } - func setLoggingEnabled(app: DatabasePigeonFirebaseApp, enabled: Bool, - completion: @escaping (Result) -> Void) { + func setLoggingEnabled( + app: DatabasePigeonFirebaseApp, enabled: Bool, + completion: @escaping (Result) -> Void + ) { Database.setLoggingEnabled(enabled) completion(.success(())) } - func useDatabaseEmulator(app: DatabasePigeonFirebaseApp, host: String, port: Int64, - completion: @escaping (Result) -> Void) { + func useDatabaseEmulator( + app: DatabasePigeonFirebaseApp, host: String, port: Int64, + completion: @escaping (Result) -> Void + ) { + let instanceKey = databaseInstanceKey(app) + guard Self.cachedDatabaseInstances[instanceKey] == nil else { + completion(.success(())) + return + } + let database = getDatabaseFromPigeonApp(app) database.useEmulator(withHost: host, port: Int(port)) completion(.success(())) } - func ref(app: DatabasePigeonFirebaseApp, path: String?, - completion: @escaping (Result) -> Void) { + func ref( + app: DatabasePigeonFirebaseApp, path: String?, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: path ?? "") let result = DatabaseReferencePlatform(path: reference.url) completion(.success(result)) } - func refFromURL(app: DatabasePigeonFirebaseApp, url: String, - completion: @escaping (Result) -> Void) { + func refFromURL( + app: DatabasePigeonFirebaseApp, url: String, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(fromURL: url) let result = DatabaseReferencePlatform(path: reference.url) completion(.success(result)) } - func purgeOutstandingWrites(app: DatabasePigeonFirebaseApp, - completion: @escaping (Result) -> Void) { + func purgeOutstandingWrites( + app: DatabasePigeonFirebaseApp, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) database.purgeOutstandingWrites() completion(.success(())) @@ -186,8 +211,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug // MARK: - Database Reference Operations - func databaseReferenceSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) { + func databaseReferenceSet( + app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -200,9 +227,11 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } } - func databaseReferenceSetWithPriority(app: DatabasePigeonFirebaseApp, - request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) { + func databaseReferenceSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -215,8 +244,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } } - func databaseReferenceUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, - completion: @escaping (Result) -> Void) { + func databaseReferenceUpdate( + app: DatabasePigeonFirebaseApp, request: UpdateRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -231,9 +262,11 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } } - func databaseReferenceSetPriority(app: DatabasePigeonFirebaseApp, - request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) { + func databaseReferenceSetPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -246,84 +279,98 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } } - func databaseReferenceRunTransaction(app: DatabasePigeonFirebaseApp, request: TransactionRequest, - completion: @escaping (Result) -> Void) { + func databaseReferenceRunTransaction( + app: DatabasePigeonFirebaseApp, request: TransactionRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) - reference.runTransactionBlock { currentData in - let semaphore = DispatchSemaphore(value: 0) - var transactionResult: TransactionHandlerResult? - - DispatchQueue.main.async { - let flutterApi = FirebaseDatabaseFlutterApi(binaryMessenger: self.binaryMessenger) - flutterApi.callTransactionHandler( - transactionKey: request.transactionKey, - snapshotValue: currentData.value - ) { result in - switch result { - case let .success(handlerResult): - transactionResult = handlerResult - case let .failure(error): - print("Transaction handler error: \(error)") - transactionResult = TransactionHandlerResult(value: nil, aborted: true, exception: true) + reference.runTransactionBlock( + { currentData in + let semaphore = DispatchSemaphore(value: 0) + var transactionResult: TransactionHandlerResult? + + DispatchQueue.main.async { + let flutterApi = FirebaseDatabaseFlutterApi(binaryMessenger: self.binaryMessenger) + flutterApi.callTransactionHandler( + transactionKey: request.transactionKey, + snapshotValue: currentData.value + ) { result in + switch result { + case .success(let handlerResult): + transactionResult = handlerResult + case .failure(let error): + print("Transaction handler error: \(error)") + transactionResult = TransactionHandlerResult( + value: nil, aborted: true, exception: true + ) + } + semaphore.signal() } - semaphore.signal() } - } - semaphore.wait() + semaphore.wait() - guard let result = transactionResult else { - return TransactionResult.abort() - } + guard let result = transactionResult else { + return TransactionResult.abort() + } - if result.aborted || result.exception { - return TransactionResult.abort() - } + if result.aborted || result.exception { + return TransactionResult.abort() + } - currentData.value = result.value - return TransactionResult.success(withValue: currentData) - } andCompletionBlock: { error, committed, snapshot in - if let error { - completion(.failure(self.createFlutterError(error))) - return - } + currentData.value = result.value + return TransactionResult.success(withValue: currentData) + }, + andCompletionBlock: { error, committed, snapshot in + if let error { + completion(.failure(self.createFlutterError(error))) + return + } - var snapshotMap: [String: Any?] - if let snapshot { - let snapshotDict = FLTFirebaseDatabaseUtils.dictionary(from: snapshot) - snapshotMap = ["snapshot": snapshotDict] - } else { - snapshotMap = ["snapshot": NSNull()] - } + var snapshotMap: [String: Any?] + if let snapshot { + let snapshotDict = FLTFirebaseDatabaseUtils.dictionary(from: snapshot) + snapshotMap = ["snapshot": snapshotDict] + } else { + snapshotMap = ["snapshot": NSNull()] + } - self.transactionResults[request.transactionKey] = [ - "committed": committed, - "snapshot": snapshotMap["snapshot"] as Any, - ] + self.transactionResults[request.transactionKey] = [ + "committed": committed, + "snapshot": snapshotMap["snapshot"] as Any, + ] - completion(.success(())) - } + completion(.success(())) + }, withLocalEvents: request.applyLocally + ) } - func databaseReferenceGetTransactionResult(app: DatabasePigeonFirebaseApp, transactionKey: Int64, - completion: @escaping (Result<[String: Any?], Error>) - -> Void) { + func databaseReferenceGetTransactionResult( + app: DatabasePigeonFirebaseApp, transactionKey: Int64, + completion: + @escaping (Result<[String: Any?], Error>) + -> Void + ) { if let result = transactionResults.removeValue(forKey: transactionKey) { completion(.success(result)) } else { - completion(.success([ - "committed": false, - "snapshot": ["value": NSNull()], - ])) + completion( + .success([ + "committed": false, + "snapshot": ["value": NSNull()], + ]) + ) } } // MARK: - OnDisconnect Operations - func onDisconnectSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) { + func onDisconnectSet( + app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -336,9 +383,11 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } } - func onDisconnectSetWithPriority(app: DatabasePigeonFirebaseApp, - request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) { + func onDisconnectSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -351,8 +400,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } } - func onDisconnectUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, - completion: @escaping (Result) -> Void) { + func onDisconnectUpdate( + app: DatabasePigeonFirebaseApp, request: UpdateRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -367,8 +418,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } } - func onDisconnectCancel(app: DatabasePigeonFirebaseApp, path: String, - completion: @escaping (Result) -> Void) { + func onDisconnectCancel( + app: DatabasePigeonFirebaseApp, path: String, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: path) @@ -383,8 +436,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug // MARK: - Query Operations - func queryObserve(app: DatabasePigeonFirebaseApp, request: QueryRequest, - completion: @escaping (Result) -> Void) { + func queryObserve( + app: DatabasePigeonFirebaseApp, request: QueryRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -447,7 +502,8 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug case "limit": if let name = modifier["name"] as? String, - let limit = modifier["limit"] as? NSNumber { + let limit = modifier["limit"] as? NSNumber + { switch name { case "limitToFirst": query = query.queryLimited(toFirst: limit.uintValue) @@ -485,8 +541,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug completion(.success(channelName)) } - func queryKeepSynced(app: DatabasePigeonFirebaseApp, request: QueryRequest, - completion: @escaping (Result) -> Void) { + func queryKeepSynced( + app: DatabasePigeonFirebaseApp, request: QueryRequest, + completion: @escaping (Result) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -549,7 +607,8 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug case "limit": if let name = modifier["name"] as? String, - let limit = modifier["limit"] as? NSNumber { + let limit = modifier["limit"] as? NSNumber + { switch name { case "limitToFirst": query = query.queryLimited(toFirst: limit.uintValue) @@ -572,8 +631,10 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug completion(.success(())) } - func queryGet(app: DatabasePigeonFirebaseApp, request: QueryRequest, - completion: @escaping (Result<[String: Any?], Error>) -> Void) { + func queryGet( + app: DatabasePigeonFirebaseApp, request: QueryRequest, + completion: @escaping (Result<[String: Any?], Error>) -> Void + ) { let database = getDatabaseFromPigeonApp(app) let reference = database.reference(withPath: request.path) @@ -647,7 +708,8 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug case "limit": if let name = modifier["name"] as? String, - let limit = modifier["limit"] as? NSNumber { + let limit = modifier["limit"] as? NSNumber + { switch name { case "limitToFirst": query = query.queryLimited(toFirst: limit.uintValue) @@ -677,8 +739,12 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug // MARK: - Helper Methods + private func databaseInstanceKey(_ app: DatabasePigeonFirebaseApp) -> String { + app.appName + (app.databaseURL ?? "") + } + private func getDatabaseFromPigeonApp(_ app: DatabasePigeonFirebaseApp) -> Database { - let instanceKey = app.appName + (app.databaseURL ?? "") + let instanceKey = databaseInstanceKey(app) if let cachedInstance = Self.cachedDatabaseInstances[instanceKey] { return cachedInstance @@ -710,7 +776,8 @@ public class FLTFirebaseDatabasePlugin: NSObject, FlutterPlugin, FLTFirebasePlug } if let emulatorHost = app.settings.emulatorHost, - let emulatorPort = app.settings.emulatorPort { + let emulatorPort = app.settings.emulatorPort + { database.useEmulator(withHost: emulatorHost, port: Int(emulatorPort)) } diff --git a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseUtils.swift b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseUtils.swift index 9561af721ff1..c51f5ac67a2a 100644 --- a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseUtils.swift +++ b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FLTFirebaseDatabaseUtils.swift @@ -55,7 +55,8 @@ import Foundation } if let emulatorHost = arguments["emulatorHost"] as? String, - let emulatorPort = arguments["emulatorPort"] as? Int { + let emulatorPort = arguments["emulatorPort"] as? Int + { database.useEmulator(withHost: emulatorHost, port: emulatorPort) } @@ -69,8 +70,10 @@ import Foundation return database.reference(withPath: path) } - private static func databaseQuery(_ query: DatabaseQuery, - applyLimitModifier modifier: [String: Any]) -> DatabaseQuery { + private static func databaseQuery( + _ query: DatabaseQuery, + applyLimitModifier modifier: [String: Any] + ) -> DatabaseQuery { let name = modifier["name"] as? String ?? "" let limit = modifier["limit"] as? UInt ?? 0 @@ -84,8 +87,10 @@ import Foundation } } - private static func databaseQuery(_ query: DatabaseQuery, - applyOrderModifier modifier: [String: Any]) -> DatabaseQuery { + private static func databaseQuery( + _ query: DatabaseQuery, + applyOrderModifier modifier: [String: Any] + ) -> DatabaseQuery { let name = modifier["name"] as? String ?? "" switch name { @@ -103,8 +108,10 @@ import Foundation } } - private static func databaseQuery(_ query: DatabaseQuery, - applyCursorModifier modifier: [String: Any]) -> DatabaseQuery { + private static func databaseQuery( + _ query: DatabaseQuery, + applyCursorModifier modifier: [String: Any] + ) -> DatabaseQuery { let name = modifier["name"] as? String ?? "" let key = modifier["key"] as? String let value = modifier["value"] @@ -161,8 +168,10 @@ import Foundation return query } - static func dictionary(from snapshot: DataSnapshot, - withPreviousChildKey previousChildKey: String?) -> [String: Any] { + static func dictionary( + from snapshot: DataSnapshot, + withPreviousChildKey previousChildKey: String? + ) -> [String: Any] { [ "snapshot": dictionary(from: snapshot), "previousChildKey": previousChildKey ?? NSNull(), diff --git a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FirebaseDatabaseMessages.g.swift b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FirebaseDatabaseMessages.g.swift index e91097627f7e..6a0fb0d765f5 100644 --- a/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FirebaseDatabaseMessages.g.swift +++ b/packages/firebase_database/firebase_database/ios/firebase_database/Sources/firebase_database/FirebaseDatabaseMessages.g.swift @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -52,7 +52,7 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } @@ -74,6 +74,19 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +private func doubleEqualsFirebaseDatabaseMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirebaseDatabaseMessages(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8_0000_0000_0000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } +} + func deepEqualsFirebaseDatabaseMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? @@ -84,59 +97,90 @@ func deepEqualsFirebaseDatabaseMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { case (nil, _), (_, nil): return false - case is (Void, Void): + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: return true - case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): - return cleanLhsHashable == cleanRhsHashable + case is (Void, Void): + return true - case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): - guard cleanLhsArray.count == cleanRhsArray.count else { return false } - for (index, element) in cleanLhsArray.enumerated() { - if !deepEqualsFirebaseDatabaseMessages(element, cleanRhsArray[index]) { + case let (lhsArray, rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsFirebaseDatabaseMessages(element, rhsArray[index]) { return false } } return true - case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): - guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } - for (key, cleanLhsValue) in cleanLhsDictionary { - guard cleanRhsDictionary.index(forKey: key) != nil else { return false } - if !deepEqualsFirebaseDatabaseMessages(cleanLhsValue, cleanRhsDictionary[key]!) { + case let (lhsArray, rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsFirebaseDatabaseMessages(element, rhsArray[index]) { return false } } return true + case let (lhsDictionary, rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEqualsFirebaseDatabaseMessages(lhsKey, rhsKey) { + if deepEqualsFirebaseDatabaseMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirebaseDatabaseMessages(lhs, rhs) + + case let (lhsHashable, rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + default: - // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be - // untrue. return false } } func deepHashFirebaseDatabaseMessages(value: Any?, hasher: inout Hasher) { - if let valueList = value as? [AnyHashable] { - for item in valueList { - deepHashFirebaseDatabaseMessages(value: item, hasher: &hasher) - } - return - } - - if let valueDict = value as? [AnyHashable: AnyHashable] { - for key in valueDict.keys { - hasher.combine(key) - deepHashFirebaseDatabaseMessages(value: valueDict[key]!, hasher: &hasher) + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirebaseDatabaseMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirebaseDatabaseMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirebaseDatabaseMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirebaseDatabaseMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirebaseDatabaseMessages(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) } - return + } else { + hasher.combine(0) } - - if let hashableValue = value as? AnyHashable { - hasher.combine(hashableValue.hashValue) - } - - return hasher.combine(String(describing: value)) } /// Generated class from Pigeon that represents data sent in messages. @@ -175,11 +219,27 @@ struct DatabasePigeonSettings: Hashable { } static func == (lhs: DatabasePigeonSettings, rhs: DatabasePigeonSettings) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.persistenceEnabled, rhs.persistenceEnabled) + && deepEqualsFirebaseDatabaseMessages( + lhs.cacheSizeBytes, + rhs.cacheSizeBytes + ) && deepEqualsFirebaseDatabaseMessages(lhs.loggingEnabled, rhs.loggingEnabled) + && deepEqualsFirebaseDatabaseMessages( + lhs.emulatorHost, + rhs.emulatorHost + ) && deepEqualsFirebaseDatabaseMessages(lhs.emulatorPort, rhs.emulatorPort) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("DatabasePigeonSettings") + deepHashFirebaseDatabaseMessages(value: persistenceEnabled, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: cacheSizeBytes, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: loggingEnabled, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: emulatorHost, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: emulatorPort, hasher: &hasher) } } @@ -211,11 +271,21 @@ struct DatabasePigeonFirebaseApp: Hashable { } static func == (lhs: DatabasePigeonFirebaseApp, rhs: DatabasePigeonFirebaseApp) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.appName, rhs.appName) + && deepEqualsFirebaseDatabaseMessages( + lhs.databaseURL, + rhs.databaseURL + ) && deepEqualsFirebaseDatabaseMessages(lhs.settings, rhs.settings) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("DatabasePigeonFirebaseApp") + deepHashFirebaseDatabaseMessages(value: appName, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: databaseURL, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: settings, hasher: &hasher) } } @@ -234,16 +304,20 @@ struct DatabaseReferencePlatform: Hashable { func toList() -> [Any?] { [ - path, + path ] } static func == (lhs: DatabaseReferencePlatform, rhs: DatabaseReferencePlatform) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.path, rhs.path) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("DatabaseReferencePlatform") + deepHashFirebaseDatabaseMessages(value: path, hasher: &hasher) } } @@ -275,11 +349,21 @@ struct DatabaseReferenceRequest: Hashable { } static func == (lhs: DatabaseReferenceRequest, rhs: DatabaseReferenceRequest) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.path, rhs.path) + && deepEqualsFirebaseDatabaseMessages( + lhs.value, + rhs.value + ) && deepEqualsFirebaseDatabaseMessages(lhs.priority, rhs.priority) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("DatabaseReferenceRequest") + deepHashFirebaseDatabaseMessages(value: path, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: value, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: priority, hasher: &hasher) } } @@ -307,11 +391,20 @@ struct UpdateRequest: Hashable { } static func == (lhs: UpdateRequest, rhs: UpdateRequest) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.path, rhs.path) + && deepEqualsFirebaseDatabaseMessages( + lhs.value, + rhs.value + ) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("UpdateRequest") + deepHashFirebaseDatabaseMessages(value: path, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: value, hasher: &hasher) } } @@ -343,11 +436,21 @@ struct TransactionRequest: Hashable { } static func == (lhs: TransactionRequest, rhs: TransactionRequest) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.path, rhs.path) + && deepEqualsFirebaseDatabaseMessages( + lhs.transactionKey, + rhs.transactionKey + ) && deepEqualsFirebaseDatabaseMessages(lhs.applyLocally, rhs.applyLocally) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("TransactionRequest") + deepHashFirebaseDatabaseMessages(value: path, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: transactionKey, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: applyLocally, hasher: &hasher) } } @@ -379,11 +482,21 @@ struct QueryRequest: Hashable { } static func == (lhs: QueryRequest, rhs: QueryRequest) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.path, rhs.path) + && deepEqualsFirebaseDatabaseMessages( + lhs.modifiers, + rhs.modifiers + ) && deepEqualsFirebaseDatabaseMessages(lhs.value, rhs.value) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("QueryRequest") + deepHashFirebaseDatabaseMessages(value: path, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: modifiers, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: value, hasher: &hasher) } } @@ -415,11 +528,21 @@ struct TransactionHandlerResult: Hashable { } static func == (lhs: TransactionHandlerResult, rhs: TransactionHandlerResult) -> Bool { - deepEqualsFirebaseDatabaseMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseDatabaseMessages(lhs.value, rhs.value) + && deepEqualsFirebaseDatabaseMessages( + lhs.aborted, + rhs.aborted + ) && deepEqualsFirebaseDatabaseMessages(lhs.exception, rhs.exception) } func hash(into hasher: inout Hasher) { - deepHashFirebaseDatabaseMessages(value: toList(), hasher: &hasher) + hasher.combine("TransactionHandlerResult") + deepHashFirebaseDatabaseMessages(value: value, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: aborted, hasher: &hasher) + deepHashFirebaseDatabaseMessages(value: exception, hasher: &hasher) } } @@ -500,52 +623,74 @@ class FirebaseDatabaseMessagesPigeonCodec: FlutterStandardMessageCodec, @uncheck /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol FirebaseDatabaseHostApi { func goOnline(app: DatabasePigeonFirebaseApp, completion: @escaping (Result) -> Void) - func goOffline(app: DatabasePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func setPersistenceEnabled(app: DatabasePigeonFirebaseApp, enabled: Bool, - completion: @escaping (Result) -> Void) - func setPersistenceCacheSizeBytes(app: DatabasePigeonFirebaseApp, cacheSize: Int64, - completion: @escaping (Result) -> Void) - func setLoggingEnabled(app: DatabasePigeonFirebaseApp, enabled: Bool, - completion: @escaping (Result) -> Void) - func useDatabaseEmulator(app: DatabasePigeonFirebaseApp, host: String, port: Int64, - completion: @escaping (Result) -> Void) - func ref(app: DatabasePigeonFirebaseApp, path: String?, - completion: @escaping (Result) -> Void) - func refFromURL(app: DatabasePigeonFirebaseApp, url: String, - completion: @escaping (Result) -> Void) - func purgeOutstandingWrites(app: DatabasePigeonFirebaseApp, - completion: @escaping (Result) -> Void) - func databaseReferenceSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) - func databaseReferenceSetWithPriority(app: DatabasePigeonFirebaseApp, - request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) - func databaseReferenceUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, - completion: @escaping (Result) -> Void) - func databaseReferenceSetPriority(app: DatabasePigeonFirebaseApp, - request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) - func databaseReferenceRunTransaction(app: DatabasePigeonFirebaseApp, request: TransactionRequest, - completion: @escaping (Result) -> Void) - func databaseReferenceGetTransactionResult(app: DatabasePigeonFirebaseApp, transactionKey: Int64, - completion: @escaping (Result<[String: Any?], Error>) - -> Void) - func onDisconnectSet(app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) - func onDisconnectSetWithPriority(app: DatabasePigeonFirebaseApp, - request: DatabaseReferenceRequest, - completion: @escaping (Result) -> Void) - func onDisconnectUpdate(app: DatabasePigeonFirebaseApp, request: UpdateRequest, - completion: @escaping (Result) -> Void) - func onDisconnectCancel(app: DatabasePigeonFirebaseApp, path: String, - completion: @escaping (Result) -> Void) - func queryObserve(app: DatabasePigeonFirebaseApp, request: QueryRequest, - completion: @escaping (Result) -> Void) - func queryKeepSynced(app: DatabasePigeonFirebaseApp, request: QueryRequest, - completion: @escaping (Result) -> Void) - func queryGet(app: DatabasePigeonFirebaseApp, request: QueryRequest, - completion: @escaping (Result<[String: Any?], Error>) -> Void) + func goOffline( + app: DatabasePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func setPersistenceEnabled( + app: DatabasePigeonFirebaseApp, enabled: Bool, + completion: @escaping (Result) -> Void) + func setPersistenceCacheSizeBytes( + app: DatabasePigeonFirebaseApp, cacheSize: Int64, + completion: @escaping (Result) -> Void) + func setLoggingEnabled( + app: DatabasePigeonFirebaseApp, enabled: Bool, + completion: @escaping (Result) -> Void) + func useDatabaseEmulator( + app: DatabasePigeonFirebaseApp, host: String, port: Int64, + completion: @escaping (Result) -> Void) + func ref( + app: DatabasePigeonFirebaseApp, path: String?, + completion: @escaping (Result) -> Void) + func refFromURL( + app: DatabasePigeonFirebaseApp, url: String, + completion: @escaping (Result) -> Void) + func purgeOutstandingWrites( + app: DatabasePigeonFirebaseApp, + completion: @escaping (Result) -> Void) + func databaseReferenceSet( + app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void) + func databaseReferenceSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void) + func databaseReferenceUpdate( + app: DatabasePigeonFirebaseApp, request: UpdateRequest, + completion: @escaping (Result) -> Void) + func databaseReferenceSetPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void) + func databaseReferenceRunTransaction( + app: DatabasePigeonFirebaseApp, request: TransactionRequest, + completion: @escaping (Result) -> Void) + func databaseReferenceGetTransactionResult( + app: DatabasePigeonFirebaseApp, transactionKey: Int64, + completion: + @escaping (Result<[String: Any?], Error>) + -> Void) + func onDisconnectSet( + app: DatabasePigeonFirebaseApp, request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void) + func onDisconnectSetWithPriority( + app: DatabasePigeonFirebaseApp, + request: DatabaseReferenceRequest, + completion: @escaping (Result) -> Void) + func onDisconnectUpdate( + app: DatabasePigeonFirebaseApp, request: UpdateRequest, + completion: @escaping (Result) -> Void) + func onDisconnectCancel( + app: DatabasePigeonFirebaseApp, path: String, + completion: @escaping (Result) -> Void) + func queryObserve( + app: DatabasePigeonFirebaseApp, request: QueryRequest, + completion: @escaping (Result) -> Void) + func queryKeepSynced( + app: DatabasePigeonFirebaseApp, request: QueryRequest, + completion: @escaping (Result) -> Void) + func queryGet( + app: DatabasePigeonFirebaseApp, request: QueryRequest, + completion: @escaping (Result<[String: Any?], Error>) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -556,11 +701,14 @@ class FirebaseDatabaseHostApiSetup { /// Sets up an instance of `FirebaseDatabaseHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseDatabaseHostApi?, - messageChannelSuffix: String = "") { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseDatabaseHostApi?, + messageChannelSuffix: String = "" + ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let goOnlineChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -572,7 +720,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -581,7 +729,8 @@ class FirebaseDatabaseHostApiSetup { goOnlineChannel.setMessageHandler(nil) } let goOfflineChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -593,7 +742,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -602,7 +751,8 @@ class FirebaseDatabaseHostApiSetup { goOfflineChannel.setMessageHandler(nil) } let setPersistenceEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -615,7 +765,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -624,7 +774,8 @@ class FirebaseDatabaseHostApiSetup { setPersistenceEnabledChannel.setMessageHandler(nil) } let setPersistenceCacheSizeBytesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -637,7 +788,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -646,7 +797,8 @@ class FirebaseDatabaseHostApiSetup { setPersistenceCacheSizeBytesChannel.setMessageHandler(nil) } let setLoggingEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -659,7 +811,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -668,7 +820,8 @@ class FirebaseDatabaseHostApiSetup { setLoggingEnabledChannel.setMessageHandler(nil) } let useDatabaseEmulatorChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -682,7 +835,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -691,7 +844,8 @@ class FirebaseDatabaseHostApiSetup { useDatabaseEmulatorChannel.setMessageHandler(nil) } let refChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -702,9 +856,9 @@ class FirebaseDatabaseHostApiSetup { let pathArg: String? = nilOrValue(args[1]) api.ref(app: appArg, path: pathArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -713,7 +867,8 @@ class FirebaseDatabaseHostApiSetup { refChannel.setMessageHandler(nil) } let refFromURLChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -724,9 +879,9 @@ class FirebaseDatabaseHostApiSetup { let urlArg = args[1] as! String api.refFromURL(app: appArg, url: urlArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -735,7 +890,8 @@ class FirebaseDatabaseHostApiSetup { refFromURLChannel.setMessageHandler(nil) } let purgeOutstandingWritesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -747,7 +903,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -756,7 +912,8 @@ class FirebaseDatabaseHostApiSetup { purgeOutstandingWritesChannel.setMessageHandler(nil) } let databaseReferenceSetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -769,7 +926,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -778,7 +935,8 @@ class FirebaseDatabaseHostApiSetup { databaseReferenceSetChannel.setMessageHandler(nil) } let databaseReferenceSetWithPriorityChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -791,7 +949,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -800,7 +958,8 @@ class FirebaseDatabaseHostApiSetup { databaseReferenceSetWithPriorityChannel.setMessageHandler(nil) } let databaseReferenceUpdateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -813,7 +972,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -822,7 +981,8 @@ class FirebaseDatabaseHostApiSetup { databaseReferenceUpdateChannel.setMessageHandler(nil) } let databaseReferenceSetPriorityChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -835,7 +995,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -844,7 +1004,8 @@ class FirebaseDatabaseHostApiSetup { databaseReferenceSetPriorityChannel.setMessageHandler(nil) } let databaseReferenceRunTransactionChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -857,7 +1018,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -866,7 +1027,8 @@ class FirebaseDatabaseHostApiSetup { databaseReferenceRunTransactionChannel.setMessageHandler(nil) } let databaseReferenceGetTransactionResultChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -876,12 +1038,14 @@ class FirebaseDatabaseHostApiSetup { let appArg = args[0] as! DatabasePigeonFirebaseApp let transactionKeyArg = args[1] as! Int64 api - .databaseReferenceGetTransactionResult(app: appArg, - transactionKey: transactionKeyArg) { result in + .databaseReferenceGetTransactionResult( + app: appArg, + transactionKey: transactionKeyArg + ) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -890,7 +1054,8 @@ class FirebaseDatabaseHostApiSetup { databaseReferenceGetTransactionResultChannel.setMessageHandler(nil) } let onDisconnectSetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -903,7 +1068,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -912,7 +1077,8 @@ class FirebaseDatabaseHostApiSetup { onDisconnectSetChannel.setMessageHandler(nil) } let onDisconnectSetWithPriorityChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -925,7 +1091,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -934,7 +1100,8 @@ class FirebaseDatabaseHostApiSetup { onDisconnectSetWithPriorityChannel.setMessageHandler(nil) } let onDisconnectUpdateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -947,7 +1114,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -956,7 +1123,8 @@ class FirebaseDatabaseHostApiSetup { onDisconnectUpdateChannel.setMessageHandler(nil) } let onDisconnectCancelChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -969,7 +1137,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -978,7 +1146,8 @@ class FirebaseDatabaseHostApiSetup { onDisconnectCancelChannel.setMessageHandler(nil) } let queryObserveChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -989,9 +1158,9 @@ class FirebaseDatabaseHostApiSetup { let requestArg = args[1] as! QueryRequest api.queryObserve(app: appArg, request: requestArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -1000,7 +1169,8 @@ class FirebaseDatabaseHostApiSetup { queryObserveChannel.setMessageHandler(nil) } let queryKeepSyncedChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1013,7 +1183,7 @@ class FirebaseDatabaseHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -1022,7 +1192,8 @@ class FirebaseDatabaseHostApiSetup { queryKeepSyncedChannel.setMessageHandler(nil) } let queryGetChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -1033,9 +1204,9 @@ class FirebaseDatabaseHostApiSetup { let requestArg = args[1] as! QueryRequest api.queryGet(app: appArg, request: requestArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -1048,10 +1219,12 @@ class FirebaseDatabaseHostApiSetup { /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol FirebaseDatabaseFlutterApiProtocol { - func callTransactionHandler(transactionKey transactionKeyArg: Int64, - snapshotValue snapshotValueArg: Any?, - completion: @escaping (Result) - -> Void) + func callTransactionHandler( + transactionKey transactionKeyArg: Int64, + snapshotValue snapshotValueArg: Any?, + completion: + @escaping (Result) + -> Void) } class FirebaseDatabaseFlutterApi: FirebaseDatabaseFlutterApiProtocol { @@ -1066,11 +1239,15 @@ class FirebaseDatabaseFlutterApi: FirebaseDatabaseFlutterApiProtocol { FirebaseDatabaseMessagesPigeonCodec.shared } - func callTransactionHandler(transactionKey transactionKeyArg: Int64, - snapshotValue snapshotValueArg: Any?, - completion: @escaping (Result) - -> Void) { - let channelName = "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseFlutterApi.callTransactionHandler\(messageChannelSuffix)" + func callTransactionHandler( + transactionKey transactionKeyArg: Int64, + snapshotValue snapshotValueArg: Any?, + completion: + @escaping (Result) + -> Void + ) { + let channelName = + "dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseFlutterApi.callTransactionHandler\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, @@ -1087,11 +1264,15 @@ class FirebaseDatabaseFlutterApi: FirebaseDatabaseFlutterApiProtocol { let details: String? = nilOrValue(listResponse[2]) completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { - completion(.failure(PigeonError( - code: "null-error", - message: "Flutter api returned null value for non-null return value.", - details: "" - ))) + completion( + .failure( + PigeonError( + code: "null-error", + message: "Flutter api returned null value for non-null return value.", + details: "" + ) + ) + ) } else { let result = listResponse[0] as! TransactionHandlerResult completion(.success(result)) diff --git a/packages/firebase_database/firebase_database/lib/firebase_database.dart b/packages/firebase_database/firebase_database/lib/firebase_database.dart index 1da1eb1889f1..80624a3eb319 100755 --- a/packages/firebase_database/firebase_database/lib/firebase_database.dart +++ b/packages/firebase_database/firebase_database/lib/firebase_database.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:firebase_database_platform_interface/firebase_database_platform_interface.dart'; import 'package:flutter/foundation.dart'; diff --git a/packages/firebase_database/firebase_database/lib/src/firebase_database.dart b/packages/firebase_database/firebase_database/lib/src/firebase_database.dart index 8c71d3ff8cc6..4090063e08a4 100644 --- a/packages/firebase_database/firebase_database/lib/src/firebase_database.dart +++ b/packages/firebase_database/firebase_database/lib/src/firebase_database.dart @@ -6,7 +6,7 @@ part of '../firebase_database.dart'; /// The entry point for accessing a Firebase Database. You can get an instance /// by calling `FirebaseDatabase.instance` or `FirebaseDatabase.instanceFor()`. -class FirebaseDatabase extends FirebasePluginPlatform { +class FirebaseDatabase extends FirebasePlugin { FirebaseDatabase._({required this.app, this.databaseURL}) : super(app.name, 'plugins.flutter.io/firebase_database') { if (databaseURL != null && databaseURL!.endsWith('/')) { diff --git a/packages/firebase_database/firebase_database/macos/firebase_database/Package.swift b/packages/firebase_database/firebase_database/macos/firebase_database/Package.swift index fe5afaddaec0..7d7a24d5ddc5 100644 --- a/packages/firebase_database/firebase_database/macos/firebase_database/Package.swift +++ b/packages/firebase_database/firebase_database/macos/firebase_database/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "12.1.4" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "12.4.4" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_database", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-database", targets: ["firebase_database"]), + .library(name: "firebase-database", targets: ["firebase_database"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-rtdb\""), ] - ), + ) ] ) diff --git a/packages/firebase_database/firebase_database/pubspec.yaml b/packages/firebase_database/firebase_database/pubspec.yaml index b04ba24786ca..50aedb988970 100755 --- a/packages/firebase_database/firebase_database/pubspec.yaml +++ b/packages/firebase_database/firebase_database/pubspec.yaml @@ -3,7 +3,8 @@ description: Flutter plugin for Firebase Database, a cloud-hosted NoSQL database with realtime data syncing across Android and iOS clients, and offline access. homepage: https://firebase.google.com/docs/database repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_database/firebase_database -version: 12.2.0 +version: 12.4.4 +resolution: workspace topics: - firebase - database @@ -13,14 +14,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_database_platform_interface: ^0.3.1 - firebase_database_web: ^0.2.7+5 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_database_platform_interface: ^0.4.0+3 + firebase_database_web: ^0.2.7+10 flutter: sdk: flutter diff --git a/packages/firebase_database/firebase_database/windows/messages.g.cpp b/packages/firebase_database/firebase_database/windows/messages.g.cpp index 8c0aeeb54adc..f503c24680bf 100644 --- a/packages/firebase_database/firebase_database/windows/messages.g.cpp +++ b/packages/firebase_database/firebase_database/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace firebase_database_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,6 +33,212 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace // DatabasePigeonSettings DatabasePigeonSettings::DatabasePigeonSettings() {} @@ -161,6 +369,35 @@ DatabasePigeonSettings DatabasePigeonSettings::FromEncodableList( return decoded; } +bool DatabasePigeonSettings::operator==( + const DatabasePigeonSettings& other) const { + return PigeonInternalDeepEquals(persistence_enabled_, + other.persistence_enabled_) && + PigeonInternalDeepEquals(cache_size_bytes_, other.cache_size_bytes_) && + PigeonInternalDeepEquals(logging_enabled_, other.logging_enabled_) && + PigeonInternalDeepEquals(emulator_host_, other.emulator_host_) && + PigeonInternalDeepEquals(emulator_port_, other.emulator_port_); +} + +bool DatabasePigeonSettings::operator!=( + const DatabasePigeonSettings& other) const { + return !(*this == other); +} + +size_t DatabasePigeonSettings::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(persistence_enabled_); + result = result * 31 + PigeonInternalDeepHash(cache_size_bytes_); + result = result * 31 + PigeonInternalDeepHash(logging_enabled_); + result = result * 31 + PigeonInternalDeepHash(emulator_host_); + result = result * 31 + PigeonInternalDeepHash(emulator_port_); + return result; +} + +size_t PigeonInternalDeepHash(const DatabasePigeonSettings& v) { + return v.Hash(); +} + // DatabasePigeonFirebaseApp DatabasePigeonFirebaseApp::DatabasePigeonFirebaseApp( @@ -247,6 +484,30 @@ DatabasePigeonFirebaseApp DatabasePigeonFirebaseApp::FromEncodableList( return decoded; } +bool DatabasePigeonFirebaseApp::operator==( + const DatabasePigeonFirebaseApp& other) const { + return PigeonInternalDeepEquals(app_name_, other.app_name_) && + PigeonInternalDeepEquals(database_u_r_l_, other.database_u_r_l_) && + PigeonInternalDeepEquals(settings_, other.settings_); +} + +bool DatabasePigeonFirebaseApp::operator!=( + const DatabasePigeonFirebaseApp& other) const { + return !(*this == other); +} + +size_t DatabasePigeonFirebaseApp::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(app_name_); + result = result * 31 + PigeonInternalDeepHash(database_u_r_l_); + result = result * 31 + PigeonInternalDeepHash(settings_); + return result; +} + +size_t PigeonInternalDeepHash(const DatabasePigeonFirebaseApp& v) { + return v.Hash(); +} + // DatabaseReferencePlatform DatabaseReferencePlatform::DatabaseReferencePlatform(const std::string& path) @@ -271,6 +532,26 @@ DatabaseReferencePlatform DatabaseReferencePlatform::FromEncodableList( return decoded; } +bool DatabaseReferencePlatform::operator==( + const DatabaseReferencePlatform& other) const { + return PigeonInternalDeepEquals(path_, other.path_); +} + +bool DatabaseReferencePlatform::operator!=( + const DatabaseReferencePlatform& other) const { + return !(*this == other); +} + +size_t DatabaseReferencePlatform::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(path_); + return result; +} + +size_t PigeonInternalDeepHash(const DatabaseReferencePlatform& v) { + return v.Hash(); +} + // DatabaseReferenceRequest DatabaseReferenceRequest::DatabaseReferenceRequest(const std::string& path) @@ -338,6 +619,30 @@ DatabaseReferenceRequest DatabaseReferenceRequest::FromEncodableList( return decoded; } +bool DatabaseReferenceRequest::operator==( + const DatabaseReferenceRequest& other) const { + return PigeonInternalDeepEquals(path_, other.path_) && + PigeonInternalDeepEquals(value_, other.value_) && + PigeonInternalDeepEquals(priority_, other.priority_); +} + +bool DatabaseReferenceRequest::operator!=( + const DatabaseReferenceRequest& other) const { + return !(*this == other); +} + +size_t DatabaseReferenceRequest::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(path_); + result = result * 31 + PigeonInternalDeepHash(value_); + result = result * 31 + PigeonInternalDeepHash(priority_); + return result; +} + +size_t PigeonInternalDeepHash(const DatabaseReferenceRequest& v) { + return v.Hash(); +} + // UpdateRequest UpdateRequest::UpdateRequest(const std::string& path, const EncodableMap& value) @@ -367,6 +672,24 @@ UpdateRequest UpdateRequest::FromEncodableList(const EncodableList& list) { return decoded; } +bool UpdateRequest::operator==(const UpdateRequest& other) const { + return PigeonInternalDeepEquals(path_, other.path_) && + PigeonInternalDeepEquals(value_, other.value_); +} + +bool UpdateRequest::operator!=(const UpdateRequest& other) const { + return !(*this == other); +} + +size_t UpdateRequest::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(path_); + result = result * 31 + PigeonInternalDeepHash(value_); + return result; +} + +size_t PigeonInternalDeepHash(const UpdateRequest& v) { return v.Hash(); } + // TransactionRequest TransactionRequest::TransactionRequest(const std::string& path, @@ -411,6 +734,26 @@ TransactionRequest TransactionRequest::FromEncodableList( return decoded; } +bool TransactionRequest::operator==(const TransactionRequest& other) const { + return PigeonInternalDeepEquals(path_, other.path_) && + PigeonInternalDeepEquals(transaction_key_, other.transaction_key_) && + PigeonInternalDeepEquals(apply_locally_, other.apply_locally_); +} + +bool TransactionRequest::operator!=(const TransactionRequest& other) const { + return !(*this == other); +} + +size_t TransactionRequest::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(path_); + result = result * 31 + PigeonInternalDeepHash(transaction_key_); + result = result * 31 + PigeonInternalDeepHash(apply_locally_); + return result; +} + +size_t PigeonInternalDeepHash(const TransactionRequest& v) { return v.Hash(); } + // QueryRequest QueryRequest::QueryRequest(const std::string& path, @@ -462,6 +805,26 @@ QueryRequest QueryRequest::FromEncodableList(const EncodableList& list) { return decoded; } +bool QueryRequest::operator==(const QueryRequest& other) const { + return PigeonInternalDeepEquals(path_, other.path_) && + PigeonInternalDeepEquals(modifiers_, other.modifiers_) && + PigeonInternalDeepEquals(value_, other.value_); +} + +bool QueryRequest::operator!=(const QueryRequest& other) const { + return !(*this == other); +} + +size_t QueryRequest::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(path_); + result = result * 31 + PigeonInternalDeepHash(modifiers_); + result = result * 31 + PigeonInternalDeepHash(value_); + return result; +} + +size_t PigeonInternalDeepHash(const QueryRequest& v) { return v.Hash(); } + // TransactionHandlerResult TransactionHandlerResult::TransactionHandlerResult(bool aborted, bool exception) @@ -517,10 +880,34 @@ TransactionHandlerResult TransactionHandlerResult::FromEncodableList( return decoded; } +bool TransactionHandlerResult::operator==( + const TransactionHandlerResult& other) const { + return PigeonInternalDeepEquals(value_, other.value_) && + PigeonInternalDeepEquals(aborted_, other.aborted_) && + PigeonInternalDeepEquals(exception_, other.exception_); +} + +bool TransactionHandlerResult::operator!=( + const TransactionHandlerResult& other) const { + return !(*this == other); +} + +size_t TransactionHandlerResult::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(value_); + result = result * 31 + PigeonInternalDeepHash(aborted_); + result = result * 31 + PigeonInternalDeepHash(exception_); + return result; +} + +size_t PigeonInternalDeepHash(const TransactionHandlerResult& v) { + return v.Hash(); +} + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { case 129: { return CustomEncodableValue(DatabasePigeonSettings::FromEncodableList( @@ -555,12 +942,12 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( std::get(ReadValue(stream)))); } default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { if (custom_value->type() == typeid(DatabasePigeonSettings)) { @@ -627,25 +1014,26 @@ void PigeonInternalCodecSerializer::WriteValue( return; } } - flutter::StandardCodecSerializer::WriteValue(value, stream); + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebaseDatabaseHostApi. -const flutter::StandardMessageCodec& FirebaseDatabaseHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebaseDatabaseHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseDatabaseHostApi` to handle messages through // the `binary_messenger`. -void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseDatabaseHostApi* api) { +void FirebaseDatabaseHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseDatabaseHostApi* api) { FirebaseDatabaseHostApi::SetUp(binary_messenger, api, ""); } -void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseDatabaseHostApi* api, - const std::string& message_channel_suffix) { +void FirebaseDatabaseHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, FirebaseDatabaseHostApi* api, + const std::string& message_channel_suffix) { const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix @@ -660,7 +1048,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -699,7 +1087,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -738,7 +1126,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -784,7 +1172,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -831,7 +1219,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -877,7 +1265,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -929,7 +1317,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -972,7 +1360,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1019,7 +1407,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1058,7 +1446,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1106,7 +1494,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1154,7 +1542,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1201,7 +1589,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1249,7 +1637,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1297,7 +1685,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1345,7 +1733,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1393,7 +1781,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1441,7 +1829,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1488,7 +1876,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1534,7 +1922,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1581,7 +1969,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1628,7 +2016,7 @@ void FirebaseDatabaseHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1683,19 +2071,19 @@ EncodableValue FirebaseDatabaseHostApi::WrapError(const FlutterError& error) { // Generated class from Pigeon that represents Flutter messages that can be // called from C++. FirebaseDatabaseFlutterApi::FirebaseDatabaseFlutterApi( - flutter::BinaryMessenger* binary_messenger) + ::flutter::BinaryMessenger* binary_messenger) : binary_messenger_(binary_messenger), message_channel_suffix_("") {} FirebaseDatabaseFlutterApi::FirebaseDatabaseFlutterApi( - flutter::BinaryMessenger* binary_messenger, + ::flutter::BinaryMessenger* binary_messenger, const std::string& message_channel_suffix) : binary_messenger_(binary_messenger), message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} -const flutter::StandardMessageCodec& FirebaseDatabaseFlutterApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebaseDatabaseFlutterApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } diff --git a/packages/firebase_database/firebase_database/windows/messages.g.h b/packages/firebase_database/firebase_database/windows/messages.g.h index 0a44f40b593f..a25163618fcc 100644 --- a/packages/firebase_database/firebase_database/windows/messages.g.h +++ b/packages/firebase_database/firebase_database/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -92,10 +92,22 @@ class DatabasePigeonSettings { void set_emulator_port(const int64_t* value_arg); void set_emulator_port(int64_t value_arg); + bool operator==(const DatabasePigeonSettings& other) const; + bool operator!=(const DatabasePigeonSettings& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static DatabasePigeonSettings FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class DatabasePigeonFirebaseApp; friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; @@ -135,10 +147,22 @@ class DatabasePigeonFirebaseApp { const DatabasePigeonSettings& settings() const; void set_settings(const DatabasePigeonSettings& value_arg); + bool operator==(const DatabasePigeonFirebaseApp& other) const; + bool operator!=(const DatabasePigeonFirebaseApp& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static DatabasePigeonFirebaseApp FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; friend class PigeonInternalCodecSerializer; @@ -156,10 +180,22 @@ class DatabaseReferencePlatform { const std::string& path() const; void set_path(std::string_view value_arg); + bool operator==(const DatabaseReferencePlatform& other) const; + bool operator!=(const DatabaseReferencePlatform& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static DatabaseReferencePlatform FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; friend class PigeonInternalCodecSerializer; @@ -174,30 +210,42 @@ class DatabaseReferenceRequest { // Constructs an object setting all fields. explicit DatabaseReferenceRequest(const std::string& path, - const flutter::EncodableValue* value, - const flutter::EncodableValue* priority); + const ::flutter::EncodableValue* value, + const ::flutter::EncodableValue* priority); const std::string& path() const; void set_path(std::string_view value_arg); - const flutter::EncodableValue* value() const; - void set_value(const flutter::EncodableValue* value_arg); - void set_value(const flutter::EncodableValue& value_arg); + const ::flutter::EncodableValue* value() const; + void set_value(const ::flutter::EncodableValue* value_arg); + void set_value(const ::flutter::EncodableValue& value_arg); - const flutter::EncodableValue* priority() const; - void set_priority(const flutter::EncodableValue* value_arg); - void set_priority(const flutter::EncodableValue& value_arg); + const ::flutter::EncodableValue* priority() const; + void set_priority(const ::flutter::EncodableValue* value_arg); + void set_priority(const ::flutter::EncodableValue& value_arg); + + bool operator==(const DatabaseReferenceRequest& other) const; + bool operator!=(const DatabaseReferenceRequest& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; private: static DatabaseReferenceRequest FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; friend class PigeonInternalCodecSerializer; std::string path_; - std::optional value_; - std::optional priority_; + std::optional<::flutter::EncodableValue> value_; + std::optional<::flutter::EncodableValue> priority_; }; // Generated class from Pigeon that represents data sent in messages. @@ -205,22 +253,34 @@ class UpdateRequest { public: // Constructs an object setting all fields. explicit UpdateRequest(const std::string& path, - const flutter::EncodableMap& value); + const ::flutter::EncodableMap& value); const std::string& path() const; void set_path(std::string_view value_arg); - const flutter::EncodableMap& value() const; - void set_value(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap& value() const; + void set_value(const ::flutter::EncodableMap& value_arg); + bool operator==(const UpdateRequest& other) const; + bool operator!=(const UpdateRequest& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static UpdateRequest FromEncodableList(const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static UpdateRequest FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; friend class PigeonInternalCodecSerializer; std::string path_; - flutter::EncodableMap value_; + ::flutter::EncodableMap value_; }; // Generated class from Pigeon that represents data sent in messages. @@ -239,10 +299,22 @@ class TransactionRequest { bool apply_locally() const; void set_apply_locally(bool value_arg); + bool operator==(const TransactionRequest& other) const; + bool operator!=(const TransactionRequest& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static TransactionRequest FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; friend class PigeonInternalCodecSerializer; @@ -256,31 +328,43 @@ class QueryRequest { public: // Constructs an object setting all non-nullable fields. explicit QueryRequest(const std::string& path, - const flutter::EncodableList& modifiers); + const ::flutter::EncodableList& modifiers); // Constructs an object setting all fields. explicit QueryRequest(const std::string& path, - const flutter::EncodableList& modifiers, + const ::flutter::EncodableList& modifiers, const bool* value); const std::string& path() const; void set_path(std::string_view value_arg); - const flutter::EncodableList& modifiers() const; - void set_modifiers(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList& modifiers() const; + void set_modifiers(const ::flutter::EncodableList& value_arg); const bool* value() const; void set_value(const bool* value_arg); void set_value(bool value_arg); + bool operator==(const QueryRequest& other) const; + bool operator!=(const QueryRequest& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static QueryRequest FromEncodableList(const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static QueryRequest FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; friend class PigeonInternalCodecSerializer; std::string path_; - flutter::EncodableList modifiers_; + ::flutter::EncodableList modifiers_; std::optional value_; }; @@ -291,12 +375,12 @@ class TransactionHandlerResult { explicit TransactionHandlerResult(bool aborted, bool exception); // Constructs an object setting all fields. - explicit TransactionHandlerResult(const flutter::EncodableValue* value, + explicit TransactionHandlerResult(const ::flutter::EncodableValue* value, bool aborted, bool exception); - const flutter::EncodableValue* value() const; - void set_value(const flutter::EncodableValue* value_arg); - void set_value(const flutter::EncodableValue& value_arg); + const ::flutter::EncodableValue* value() const; + void set_value(const ::flutter::EncodableValue* value_arg); + void set_value(const ::flutter::EncodableValue& value_arg); bool aborted() const; void set_aborted(bool value_arg); @@ -304,19 +388,32 @@ class TransactionHandlerResult { bool exception() const; void set_exception(bool value_arg); + bool operator==(const TransactionHandlerResult& other) const; + bool operator!=(const TransactionHandlerResult& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static TransactionHandlerResult FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseDatabaseHostApi; friend class FirebaseDatabaseFlutterApi; friend class PigeonInternalCodecSerializer; - std::optional value_; + std::optional<::flutter::EncodableValue> value_; bool aborted_; bool exception_; }; -class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -324,12 +421,12 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -387,7 +484,7 @@ class FirebaseDatabaseHostApi { std::function reply)> result) = 0; virtual void DatabaseReferenceGetTransactionResult( const DatabasePigeonFirebaseApp& app, int64_t transaction_key, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void OnDisconnectSet( const DatabasePigeonFirebaseApp& app, const DatabaseReferenceRequest& request, @@ -410,19 +507,19 @@ class FirebaseDatabaseHostApi { std::function reply)> result) = 0; virtual void QueryGet( const DatabasePigeonFirebaseApp& app, const QueryRequest& request, - std::function reply)> result) = 0; + std::function reply)> result) = 0; // The codec used by FirebaseDatabaseHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseDatabaseHostApi` to handle messages through // the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseDatabaseHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseDatabaseHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseDatabaseHostApi() = default; @@ -431,17 +528,17 @@ class FirebaseDatabaseHostApi { // called from C++. class FirebaseDatabaseFlutterApi { public: - FirebaseDatabaseFlutterApi(flutter::BinaryMessenger* binary_messenger); - FirebaseDatabaseFlutterApi(flutter::BinaryMessenger* binary_messenger, + FirebaseDatabaseFlutterApi(::flutter::BinaryMessenger* binary_messenger); + FirebaseDatabaseFlutterApi(::flutter::BinaryMessenger* binary_messenger, const std::string& message_channel_suffix); - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); void CallTransactionHandler( - int64_t transaction_key, const flutter::EncodableValue* snapshot_value, + int64_t transaction_key, const ::flutter::EncodableValue* snapshot_value, std::function&& on_success, std::function&& on_error); private: - flutter::BinaryMessenger* binary_messenger_; + ::flutter::BinaryMessenger* binary_messenger_; std::string message_channel_suffix_; }; diff --git a/packages/firebase_database/firebase_database_platform_interface/CHANGELOG.md b/packages/firebase_database/firebase_database_platform_interface/CHANGELOG.md index a6092a3ce12d..22029dd973e2 100755 --- a/packages/firebase_database/firebase_database_platform_interface/CHANGELOG.md +++ b/packages/firebase_database/firebase_database_platform_interface/CHANGELOG.md @@ -1,3 +1,26 @@ +## 0.4.0+3 + + - Update a dependency to the latest release. + +## 0.4.0+2 + + - Update a dependency to the latest release. + +## 0.4.0+1 + + - Update a dependency to the latest release. + +## 0.4.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 0.3.1+1 + + - Update a dependency to the latest release. + ## 0.3.1 - **FEAT**(database,windows): add support for Realtime Database to windows ([#18079](https://github.com/firebase/flutterfire/issues/18079)). ([007689f9](https://github.com/firebase/flutterfire/commit/007689f99866582828a063d174c52ebba13ac0ef)) diff --git a/packages/firebase_database/firebase_database_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_database/firebase_database_platform_interface/lib/src/pigeon/messages.pigeon.dart index 592d072fca61..f8b622a7016a 100644 --- a/packages/firebase_database/firebase_database_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_database/firebase_database_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,21 +1,40 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.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, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -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".', - ); +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } List wrapResponse( @@ -30,20 +49,67 @@ List wrapResponse( } bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } if (a is List && b is List) { return a.length == b.length && a.indexed .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; } return a == b; } +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + class DatabasePigeonSettings { DatabasePigeonSettings({ this.persistenceEnabled, @@ -97,12 +163,16 @@ class DatabasePigeonSettings { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(persistenceEnabled, other.persistenceEnabled) && + _deepEquals(cacheSizeBytes, other.cacheSizeBytes) && + _deepEquals(loggingEnabled, other.loggingEnabled) && + _deepEquals(emulatorHost, other.emulatorHost) && + _deepEquals(emulatorPort, other.emulatorPort); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class DatabasePigeonFirebaseApp { @@ -149,12 +219,14 @@ class DatabasePigeonFirebaseApp { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(appName, other.appName) && + _deepEquals(databaseURL, other.databaseURL) && + _deepEquals(settings, other.settings); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class DatabaseReferencePlatform { @@ -191,12 +263,12 @@ class DatabaseReferencePlatform { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(path, other.path); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class DatabaseReferenceRequest { @@ -243,12 +315,14 @@ class DatabaseReferenceRequest { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(path, other.path) && + _deepEquals(value, other.value) && + _deepEquals(priority, other.priority); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class UpdateRequest { @@ -276,7 +350,7 @@ class UpdateRequest { result as List; return UpdateRequest( path: result[0]! as String, - value: (result[1] as Map?)!.cast(), + value: (result[1]! as Map).cast(), ); } @@ -289,12 +363,12 @@ class UpdateRequest { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(path, other.path) && _deepEquals(value, other.value); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class TransactionRequest { @@ -340,12 +414,14 @@ class TransactionRequest { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(path, other.path) && + _deepEquals(transactionKey, other.transactionKey) && + _deepEquals(applyLocally, other.applyLocally); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class QueryRequest { @@ -377,7 +453,7 @@ class QueryRequest { result as List; return QueryRequest( path: result[0]! as String, - modifiers: (result[1] as List?)!.cast>(), + modifiers: (result[1]! as List).cast>(), value: result[2] as bool?, ); } @@ -391,12 +467,14 @@ class QueryRequest { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(path, other.path) && + _deepEquals(modifiers, other.modifiers) && + _deepEquals(value, other.value); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class TransactionHandlerResult { @@ -443,12 +521,14 @@ class TransactionHandlerResult { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(value, other.value) && + _deepEquals(aborted, other.aborted) && + _deepEquals(exception, other.exception); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class _PigeonCodec extends StandardMessageCodec { @@ -528,621 +608,447 @@ class FirebaseDatabaseHostApi { final String pigeonVar_messageChannelSuffix; Future goOnline(DatabasePigeonFirebaseApp app) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future goOffline(DatabasePigeonFirebaseApp app) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setPersistenceEnabled( DatabasePigeonFirebaseApp app, bool enabled) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, enabled]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setPersistenceCacheSizeBytes( DatabasePigeonFirebaseApp app, int cacheSize) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, cacheSize]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setLoggingEnabled( DatabasePigeonFirebaseApp app, bool enabled) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, enabled]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future useDatabaseEmulator( DatabasePigeonFirebaseApp app, String host, int port) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, host, port]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future ref(DatabasePigeonFirebaseApp app, [String? path]) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, path]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 DatabaseReferencePlatform?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as DatabaseReferencePlatform; } Future refFromURL( DatabasePigeonFirebaseApp app, String url) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, url]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 DatabaseReferencePlatform?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as DatabaseReferencePlatform; } Future purgeOutstandingWrites(DatabasePigeonFirebaseApp app) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future databaseReferenceSet( DatabasePigeonFirebaseApp app, DatabaseReferenceRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future databaseReferenceSetWithPriority( DatabasePigeonFirebaseApp app, DatabaseReferenceRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future databaseReferenceUpdate( DatabasePigeonFirebaseApp app, UpdateRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future databaseReferenceSetPriority( DatabasePigeonFirebaseApp app, DatabaseReferenceRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future databaseReferenceRunTransaction( DatabasePigeonFirebaseApp app, TransactionRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future> databaseReferenceGetTransactionResult( DatabasePigeonFirebaseApp app, int transactionKey) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, transactionKey]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 Map?)! - .cast(); - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as Map) + .cast(); } Future onDisconnectSet( DatabasePigeonFirebaseApp app, DatabaseReferenceRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future onDisconnectSetWithPriority( DatabasePigeonFirebaseApp app, DatabaseReferenceRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future onDisconnectUpdate( DatabasePigeonFirebaseApp app, UpdateRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future onDisconnectCancel( DatabasePigeonFirebaseApp app, String path) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, path]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future queryObserve( DatabasePigeonFirebaseApp app, QueryRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 String?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future queryKeepSynced( DatabasePigeonFirebaseApp app, QueryRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future> queryGet( DatabasePigeonFirebaseApp app, QueryRequest request) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([app, request]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 Map?)! - .cast(); - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as Map) + .cast(); } } @@ -1160,9 +1066,7 @@ abstract class FirebaseDatabaseFlutterApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseFlutterApi.callTransactionHandler$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -1170,16 +1074,12 @@ abstract class FirebaseDatabaseFlutterApi { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseFlutterApi.callTransactionHandler was null.'); - final List args = (message as List?)!; - final int? arg_transactionKey = (args[0] as int?); - assert(arg_transactionKey != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseFlutterApi.callTransactionHandler was null, expected non-null int.'); - final Object? arg_snapshotValue = (args[1] as Object?); + final List args = message! as List; + final int arg_transactionKey = args[0]! as int; + final Object? arg_snapshotValue = args[1]; try { final TransactionHandlerResult output = await api - .callTransactionHandler(arg_transactionKey!, arg_snapshotValue); + .callTransactionHandler(arg_transactionKey, arg_snapshotValue); return wrapResponse(result: output); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/firebase_database/firebase_database_platform_interface/pubspec.yaml b/packages/firebase_database/firebase_database_platform_interface/pubspec.yaml index bfbbcc7002a7..ff978b08c9fc 100755 --- a/packages/firebase_database/firebase_database_platform_interface/pubspec.yaml +++ b/packages/firebase_database/firebase_database_platform_interface/pubspec.yaml @@ -1,24 +1,25 @@ name: firebase_database_platform_interface description: A common platform interface for the firebase_database plugin. -version: 0.3.1 +version: 0.4.0+3 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_database/firebase_database_platform_interface environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 + _flutterfire_internals: ^1.3.73 collection: ^1.14.3 - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.2 - pigeon: 25.3.2 + pigeon: 26.3.4 diff --git a/packages/firebase_database/firebase_database_platform_interface/test/pigeon/test_api.dart b/packages/firebase_database/firebase_database_platform_interface/test/pigeon/test_api.dart index 9c08cae382af..da5370c5adb2 100644 --- a/packages/firebase_database/firebase_database_platform_interface/test/pigeon/test_api.dart +++ b/packages/firebase_database/firebase_database_platform_interface/test/pigeon/test_api.dart @@ -1,9 +1,9 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers +// 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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -148,9 +148,7 @@ abstract class TestFirebaseDatabaseHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -161,15 +159,11 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOnline was null, expected non-null DatabasePigeonFirebaseApp.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; try { - await api.goOnline(arg_app!); + await api.goOnline(arg_app); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -181,9 +175,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -194,15 +186,11 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.goOffline was null, expected non-null DatabasePigeonFirebaseApp.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; try { - await api.goOffline(arg_app!); + await api.goOffline(arg_app); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -214,9 +202,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -227,18 +213,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled was null, expected non-null DatabasePigeonFirebaseApp.'); - final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceEnabled was null, expected non-null bool.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final bool arg_enabled = args[1]! as bool; try { - await api.setPersistenceEnabled(arg_app!, arg_enabled!); + await api.setPersistenceEnabled(arg_app, arg_enabled); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -250,9 +230,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -263,18 +241,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes was null, expected non-null DatabasePigeonFirebaseApp.'); - final int? arg_cacheSize = (args[1] as int?); - assert(arg_cacheSize != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setPersistenceCacheSizeBytes was null, expected non-null int.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final int arg_cacheSize = args[1]! as int; try { - await api.setPersistenceCacheSizeBytes(arg_app!, arg_cacheSize!); + await api.setPersistenceCacheSizeBytes(arg_app, arg_cacheSize); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -286,9 +258,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -299,18 +269,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled was null, expected non-null DatabasePigeonFirebaseApp.'); - final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.setLoggingEnabled was null, expected non-null bool.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final bool arg_enabled = args[1]! as bool; try { - await api.setLoggingEnabled(arg_app!, arg_enabled!); + await api.setLoggingEnabled(arg_app, arg_enabled); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -322,9 +286,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -335,21 +297,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator was null, expected non-null DatabasePigeonFirebaseApp.'); - final String? arg_host = (args[1] as String?); - assert(arg_host != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator was null, expected non-null String.'); - final int? arg_port = (args[2] as int?); - assert(arg_port != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.useDatabaseEmulator was null, expected non-null int.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final String arg_host = args[1]! as String; + final int arg_port = args[2]! as int; try { - await api.useDatabaseEmulator(arg_app!, arg_host!, arg_port!); + await api.useDatabaseEmulator(arg_app, arg_host, arg_port); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -361,9 +315,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -374,17 +326,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.ref was null, expected non-null DatabasePigeonFirebaseApp.'); - final String? arg_path = (args[1] as String?); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final String? arg_path = args[1] as String?; try { final DatabaseReferencePlatform output = - await api.ref(arg_app!, arg_path); + await api.ref(arg_app, arg_path); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -396,9 +344,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -409,19 +355,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL was null, expected non-null DatabasePigeonFirebaseApp.'); - final String? arg_url = (args[1] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.refFromURL was null, expected non-null String.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final String arg_url = args[1]! as String; try { final DatabaseReferencePlatform output = - await api.refFromURL(arg_app!, arg_url!); + await api.refFromURL(arg_app, arg_url); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -433,9 +373,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -446,15 +384,11 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.purgeOutstandingWrites was null, expected non-null DatabasePigeonFirebaseApp.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; try { - await api.purgeOutstandingWrites(arg_app!); + await api.purgeOutstandingWrites(arg_app); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -466,9 +400,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -479,19 +411,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet was null, expected non-null DatabasePigeonFirebaseApp.'); - final DatabaseReferenceRequest? arg_request = - (args[1] as DatabaseReferenceRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSet was null, expected non-null DatabaseReferenceRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final DatabaseReferenceRequest arg_request = + args[1]! as DatabaseReferenceRequest; try { - await api.databaseReferenceSet(arg_app!, arg_request!); + await api.databaseReferenceSet(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -503,9 +429,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -516,19 +440,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority was null, expected non-null DatabasePigeonFirebaseApp.'); - final DatabaseReferenceRequest? arg_request = - (args[1] as DatabaseReferenceRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetWithPriority was null, expected non-null DatabaseReferenceRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final DatabaseReferenceRequest arg_request = + args[1]! as DatabaseReferenceRequest; try { - await api.databaseReferenceSetWithPriority(arg_app!, arg_request!); + await api.databaseReferenceSetWithPriority(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -540,9 +458,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -553,18 +469,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate was null, expected non-null DatabasePigeonFirebaseApp.'); - final UpdateRequest? arg_request = (args[1] as UpdateRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceUpdate was null, expected non-null UpdateRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final UpdateRequest arg_request = args[1]! as UpdateRequest; try { - await api.databaseReferenceUpdate(arg_app!, arg_request!); + await api.databaseReferenceUpdate(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -576,9 +486,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -589,19 +497,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority was null, expected non-null DatabasePigeonFirebaseApp.'); - final DatabaseReferenceRequest? arg_request = - (args[1] as DatabaseReferenceRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceSetPriority was null, expected non-null DatabaseReferenceRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final DatabaseReferenceRequest arg_request = + args[1]! as DatabaseReferenceRequest; try { - await api.databaseReferenceSetPriority(arg_app!, arg_request!); + await api.databaseReferenceSetPriority(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -613,9 +515,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -626,19 +526,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction was null, expected non-null DatabasePigeonFirebaseApp.'); - final TransactionRequest? arg_request = - (args[1] as TransactionRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceRunTransaction was null, expected non-null TransactionRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final TransactionRequest arg_request = args[1]! as TransactionRequest; try { - await api.databaseReferenceRunTransaction(arg_app!, arg_request!); + await api.databaseReferenceRunTransaction(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -650,9 +543,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -663,20 +554,14 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult was null, expected non-null DatabasePigeonFirebaseApp.'); - final int? arg_transactionKey = (args[1] as int?); - assert(arg_transactionKey != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.databaseReferenceGetTransactionResult was null, expected non-null int.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final int arg_transactionKey = args[1]! as int; try { final Map output = await api.databaseReferenceGetTransactionResult( - arg_app!, arg_transactionKey!); + arg_app, arg_transactionKey); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -688,9 +573,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -701,19 +584,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet was null, expected non-null DatabasePigeonFirebaseApp.'); - final DatabaseReferenceRequest? arg_request = - (args[1] as DatabaseReferenceRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSet was null, expected non-null DatabaseReferenceRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final DatabaseReferenceRequest arg_request = + args[1]! as DatabaseReferenceRequest; try { - await api.onDisconnectSet(arg_app!, arg_request!); + await api.onDisconnectSet(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -725,9 +602,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -738,19 +613,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority was null, expected non-null DatabasePigeonFirebaseApp.'); - final DatabaseReferenceRequest? arg_request = - (args[1] as DatabaseReferenceRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectSetWithPriority was null, expected non-null DatabaseReferenceRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final DatabaseReferenceRequest arg_request = + args[1]! as DatabaseReferenceRequest; try { - await api.onDisconnectSetWithPriority(arg_app!, arg_request!); + await api.onDisconnectSetWithPriority(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -762,9 +631,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -775,18 +642,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate was null, expected non-null DatabasePigeonFirebaseApp.'); - final UpdateRequest? arg_request = (args[1] as UpdateRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectUpdate was null, expected non-null UpdateRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final UpdateRequest arg_request = args[1]! as UpdateRequest; try { - await api.onDisconnectUpdate(arg_app!, arg_request!); + await api.onDisconnectUpdate(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -798,9 +659,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -811,18 +670,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel was null, expected non-null DatabasePigeonFirebaseApp.'); - final String? arg_path = (args[1] as String?); - assert(arg_path != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.onDisconnectCancel was null, expected non-null String.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final String arg_path = args[1]! as String; try { - await api.onDisconnectCancel(arg_app!, arg_path!); + await api.onDisconnectCancel(arg_app, arg_path); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -834,9 +687,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -847,19 +698,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve was null, expected non-null DatabasePigeonFirebaseApp.'); - final QueryRequest? arg_request = (args[1] as QueryRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryObserve was null, expected non-null QueryRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final QueryRequest arg_request = args[1]! as QueryRequest; try { - final String output = - await api.queryObserve(arg_app!, arg_request!); + final String output = await api.queryObserve(arg_app, arg_request); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -871,9 +715,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -884,18 +726,12 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced was null, expected non-null DatabasePigeonFirebaseApp.'); - final QueryRequest? arg_request = (args[1] as QueryRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryKeepSynced was null, expected non-null QueryRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final QueryRequest arg_request = args[1]! as QueryRequest; try { - await api.queryKeepSynced(arg_app!, arg_request!); + await api.queryKeepSynced(arg_app, arg_request); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -907,9 +743,7 @@ abstract class TestFirebaseDatabaseHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -920,19 +754,13 @@ abstract class TestFirebaseDatabaseHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet was null.'); - final List args = (message as List?)!; - final DatabasePigeonFirebaseApp? arg_app = - (args[0] as DatabasePigeonFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet was null, expected non-null DatabasePigeonFirebaseApp.'); - final QueryRequest? arg_request = (args[1] as QueryRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.firebase_database_platform_interface.FirebaseDatabaseHostApi.queryGet was null, expected non-null QueryRequest.'); + final List args = message! as List; + final DatabasePigeonFirebaseApp arg_app = + args[0]! as DatabasePigeonFirebaseApp; + final QueryRequest arg_request = args[1]! as QueryRequest; try { final Map output = - await api.queryGet(arg_app!, arg_request!); + await api.queryGet(arg_app, arg_request); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/firebase_database/firebase_database_web/CHANGELOG.md b/packages/firebase_database/firebase_database_web/CHANGELOG.md index 837eaf1b5862..adcbb395042f 100644 --- a/packages/firebase_database/firebase_database_web/CHANGELOG.md +++ b/packages/firebase_database/firebase_database_web/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.2.7+10 + + - Update a dependency to the latest release. + +## 0.2.7+9 + + - Update a dependency to the latest release. + +## 0.2.7+8 + + - Update a dependency to the latest release. + +## 0.2.7+7 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.2.7+6 + + - Update a dependency to the latest release. + ## 0.2.7+5 - Update a dependency to the latest release. diff --git a/packages/firebase_database/firebase_database_web/lib/src/firebase_database_version.dart b/packages/firebase_database/firebase_database_web/lib/src/firebase_database_version.dart index 26c81d959d07..02b9baec59d4 100644 --- a/packages/firebase_database/firebase_database_web/lib/src/firebase_database_version.dart +++ b/packages/firebase_database/firebase_database_web/lib/src/firebase_database_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '12.2.0'; +const packageVersion = '12.4.4'; diff --git a/packages/firebase_database/firebase_database_web/lib/src/interop/database.dart b/packages/firebase_database/firebase_database_web/lib/src/interop/database.dart index 66a11d3b8337..e9095b557278 100755 --- a/packages/firebase_database/firebase_database_web/lib/src/interop/database.dart +++ b/packages/firebase_database/firebase_database_web/lib/src/interop/database.dart @@ -125,7 +125,7 @@ class DatabaseReference extends Query { /// /// This method returns [ThenableReference], [DatabaseReference] /// with a [Future] property. - ThenableReference push([value]) => ThenableReference.fromJsObject( + ThenableReference push([Object? value]) => ThenableReference.fromJsObject( database_interop.push(jsObject, value?.jsify())); /// Removes data from actual database location. @@ -148,8 +148,8 @@ class DatabaseReference extends Query { /// Sets a priority for data at actual database location. /// /// The [priority] must be a [String], [num] or `null`, or the error is thrown. - Future setPriority(priority) => - database_interop.setPriority(jsObject, priority).toDart; + Future setPriority(Object? priority) => + database_interop.setPriority(jsObject, priority?.jsify()).toDart; /// Sets data [newVal] at actual database location with provided priority /// [newPriority]. @@ -159,9 +159,10 @@ class DatabaseReference extends Query { /// The [newVal] must be a Dart basic type or the error is thrown. /// The [newPriority] must be a [String], [num] or `null`, or the error /// is thrown. - Future setWithPriority(Object? newVal, newPriority) => database_interop - .setWithPriority(jsObject, newVal?.jsify(), newPriority) - .toDart; + Future setWithPriority(Object? newVal, Object? newPriority) => + database_interop + .setWithPriority(jsObject, newVal?.jsify(), newPriority?.jsify()) + .toDart; /// Atomically updates data at actual database location. /// diff --git a/packages/firebase_database/firebase_database_web/pubspec.yaml b/packages/firebase_database/firebase_database_web/pubspec.yaml index e241f66b3883..42b3063bc3c9 100644 --- a/packages/firebase_database/firebase_database_web/pubspec.yaml +++ b/packages/firebase_database/firebase_database_web/pubspec.yaml @@ -1,27 +1,28 @@ name: firebase_database_web description: The web implementation of firebase_database -version: 0.2.7+5 +version: 0.2.7+10 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_database/firebase_database_web environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: collection: ^1.18.0 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 - firebase_database_platform_interface: ^0.3.1 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 + firebase_database_platform_interface: ^0.4.0+3 flutter: sdk: flutter flutter_web_plugins: sdk: flutter dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 flutter: diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging/CHANGELOG.md b/packages/firebase_in_app_messaging/firebase_in_app_messaging/CHANGELOG.md index 56744bcaeb5b..62726a23b7e2 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging/CHANGELOG.md +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.9.2+4 + + - Update a dependency to the latest release. + +## 0.9.2+3 + + - Update a dependency to the latest release. + +## 0.9.2+2 + + - Update a dependency to the latest release. + +## 0.9.2+1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.9.2 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 0.9.1 - **FEAT**(ios): migrate iOS to UIScene lifecycle ([#18054](https://github.com/firebase/flutterfire/issues/18054)). ([3ffa4110](https://github.com/firebase/flutterfire/commit/3ffa411098132fd5182a84be4e7a226106bc7451)) diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt b/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt index 57b5fca33169..88c6950b89c7 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.tests import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/pubspec.yaml b/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/pubspec.yaml index a4622ca2bde2..5b7c6222409c 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/pubspec.yaml +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging/example/pubspec.yaml @@ -1,15 +1,17 @@ name: firebase_in_app_messaging_example description: Demonstrates how to use the firebase_in_app_messaging plugin. +resolution: workspace publish_to: 'none' environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_analytics: ^12.2.0 - firebase_core: ^4.6.0 - firebase_in_app_messaging: ^0.9.1 - firebase_in_app_messaging_platform_interface: ^0.2.5+19 + firebase_analytics: ^12.4.3 + firebase_core: ^4.11.0 + firebase_in_app_messaging: ^0.9.2+4 + firebase_in_app_messaging_platform_interface: ^0.2.5+24 flutter: sdk: flutter diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging/ios/firebase_in_app_messaging/Package.swift b/packages/firebase_in_app_messaging/firebase_in_app_messaging/ios/firebase_in_app_messaging/Package.swift index 967c759d4667..7a0a9c0a68d0 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging/ios/firebase_in_app_messaging/Package.swift +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging/ios/firebase_in_app_messaging/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "0.9.0-7" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "0.9.2-4" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_in_app_messaging", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-in-app-messaging", targets: ["firebase_in_app_messaging"]), + .library(name: "firebase-in-app-messaging", targets: ["firebase_in_app_messaging"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-fiam\""), ] - ), + ) ] ) diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging/lib/firebase_in_app_messaging.dart b/packages/firebase_in_app_messaging/firebase_in_app_messaging/lib/firebase_in_app_messaging.dart index 857c0f25c376..ef35ef27655f 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging/lib/firebase_in_app_messaging.dart +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging/lib/firebase_in_app_messaging.dart @@ -4,10 +4,10 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:firebase_in_app_messaging_platform_interface/firebase_in_app_messaging_platform_interface.dart'; -class FirebaseInAppMessaging extends FirebasePluginPlatform { +class FirebaseInAppMessaging extends FirebasePlugin { FirebaseInAppMessaging._({required this.app}) : super(app.name, 'plugins.flutter.io/firebase_in_app_messaging'); diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging/pubspec.yaml b/packages/firebase_in_app_messaging/firebase_in_app_messaging/pubspec.yaml index b9e73439697a..4667d8612f0d 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging/pubspec.yaml +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging/pubspec.yaml @@ -1,6 +1,7 @@ name: firebase_in_app_messaging description: Flutter plugin for Firebase In-App Messaging. -version: 0.9.1 +version: 0.9.2+4 +resolution: workspace homepage: https://firebase.google.com/docs/in-app-messaging repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_in_app_messaging topics: @@ -13,13 +14,13 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_in_app_messaging_platform_interface: ^0.2.5+19 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_in_app_messaging_platform_interface: ^0.2.5+24 flutter: sdk: flutter meta: ^1.8.0 diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/CHANGELOG.md b/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/CHANGELOG.md index 20b6dcab90d0..4ff64dde37bc 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/CHANGELOG.md +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.2.5+24 + + - Update a dependency to the latest release. + +## 0.2.5+23 + + - Update a dependency to the latest release. + +## 0.2.5+22 + + - Update a dependency to the latest release. + +## 0.2.5+21 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.2.5+20 + + - Update a dependency to the latest release. + ## 0.2.5+19 - Update a dependency to the latest release. diff --git a/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/pubspec.yaml b/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/pubspec.yaml index e0190cdfd00c..41e9ce87d2a3 100644 --- a/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/pubspec.yaml +++ b/packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface/pubspec.yaml @@ -3,21 +3,22 @@ description: A common platform interface for the firebase_in_app_messaging plugi homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_in_app_messaging/firebase_in_app_messagin_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_in_app_messaging/firebase_in_app_messagin_platform_interface -version: 0.2.5+19 +version: 0.2.5+24 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter diff --git a/packages/firebase_messaging/firebase_messaging/CHANGELOG.md b/packages/firebase_messaging/firebase_messaging/CHANGELOG.md index e8b0c56fa251..cf1646bc73d9 100644 --- a/packages/firebase_messaging/firebase_messaging/CHANGELOG.md +++ b/packages/firebase_messaging/firebase_messaging/CHANGELOG.md @@ -1,3 +1,29 @@ +## 16.4.1 + + - **FIX**: resolve FlutterSceneLifeCycleDelegate conformance guard ([#18385](https://github.com/firebase/flutterfire/issues/18385)). ([48d67196](https://github.com/firebase/flutterfire/commit/48d67196a10affe09724529df5f67cf40b62bccf)) + +## 16.4.0 + + - **FIX**(messaging,ios): fix a race condition that could happen when getting initial message ([#18352](https://github.com/firebase/flutterfire/issues/18352)). ([77396b81](https://github.com/firebase/flutterfire/commit/77396b81ae56943a38c23b429249b0b9cbd4bc21)) + - **FEAT**(messaging,ios): add support for actionIdentifier on iOS devices ([#18357](https://github.com/firebase/flutterfire/issues/18357)). ([d60af4d9](https://github.com/firebase/flutterfire/commit/d60af4d9e1345c113490e875c85bd9ac62dad935)) + +## 16.3.0 + + - **FEAT**(messaging,web): add support for custom service worker script path in `getToken` method ([#18290](https://github.com/firebase/flutterfire/issues/18290)). ([b37722db](https://github.com/firebase/flutterfire/commit/b37722db13548aca57b3a24ba0f27b5de021be02)) + +## 16.2.2 + + - Update a dependency to the latest release. + +## 16.2.1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(messaging,android): fix call race that could happen when using requestPermission ([#18256](https://github.com/firebase/flutterfire/issues/18256)). ([57d4c3d0](https://github.com/firebase/flutterfire/commit/57d4c3d050c6a9252390de6cac91a0ca1d5461e3)) + +## 16.2.0 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 16.1.3 - **FIX**(messaging,ios): fix an issue where the scene initializer could be called twice in latest Flutter versions ([#18051](https://github.com/firebase/flutterfire/issues/18051)). ([5b602105](https://github.com/firebase/flutterfire/commit/5b602105faf9f64ac977a4266de5ee10785330bd)) diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundExecutor.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundExecutor.java index a920109065f6..51f48f0a6f21 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundExecutor.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundExecutor.java @@ -31,6 +31,7 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; + /** * An background execution abstraction which handles initializing a background isolate running a * callback dispatcher, used to invoke Dart callbacks while backgrounded. @@ -41,6 +42,7 @@ public class FlutterFirebaseMessagingBackgroundExecutor implements MethodCallHan private static final String USER_CALLBACK_HANDLE_KEY = "user_callback_handle"; private final AtomicBoolean isCallbackDispatcherReady = new AtomicBoolean(false); + /** * The {@link MethodChannel} that connects the Android side of this plugin with the background * Dart isolate that was created by this plugin. @@ -180,7 +182,8 @@ public void executeDartCallbackInBackgroundIsolate(Intent intent, final CountDow if (backgroundFlutterEngine == null) { Log.i( TAG, - "A background message could not be handled in Dart as no onBackgroundMessage handler has been registered."); + "A background message could not be handled in Dart as no onBackgroundMessage handler has" + + " been registered."); return; } @@ -190,7 +193,8 @@ public void executeDartCallbackInBackgroundIsolate(Intent intent, final CountDow new Result() { @Override public void success(Object result) { - // If another thread is waiting, then wake that thread when the callback returns a result. + // If another thread is waiting, then wake that thread when the callback returns a + // result. latch.countDown(); } diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundService.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundService.java index 6b75fa2ad6e5..b4f3ee877b59 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundService.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingBackgroundService.java @@ -122,7 +122,8 @@ protected void onHandleWork(@NonNull final Intent intent) { if (!flutterBackgroundExecutor.isDartBackgroundHandlerRegistered()) { Log.w( TAG, - "A background message could not be handled in Dart as no onBackgroundMessage handler has been registered."); + "A background message could not be handled in Dart as no onBackgroundMessage handler has" + + " been registered."); return; } diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java index 63d1e0dfac0a..868c950899b3 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java @@ -200,6 +200,7 @@ private Task unsubscribeFromTopic(Map arguments) { return taskCompletionSource.getTask(); } + // This API will be removed in a future release. Slated to be removed by June 2024 by Firebase. // https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessaging#send @SuppressWarnings("deprecation") @@ -312,7 +313,8 @@ private Task> getInitialMessage() { FlutterFirebaseMessagingReceiver.notifications.get(messageId); Map notificationMap = null; - // If we can't find a copy of the remote message in memory then check from our persisted store. + // If we can't find a copy of the remote message in memory then check from our persisted + // store. if (remoteMessage == null) { Map messageMap = FlutterFirebaseMessagingStore.getInstance().getFirebaseMessageMap(messageId); @@ -339,7 +341,8 @@ private Task> getInitialMessage() { Map remoteMessageMap = FlutterFirebaseMessagingUtils.remoteMessageToMap(remoteMessage); - // If no notification map is available in the remote message we override with the one we got + // If no notification map is available in the remote message we override with the one we + // got if (remoteMessage.getNotification() == null && notificationMap != null) { remoteMessageMap.put("notification", notificationMap); } @@ -421,12 +424,12 @@ public void onMethodCall(final MethodCall call, @NonNull final Result result) { Task methodCallTask; switch (call.method) { - // This message is sent when the Dart side of this plugin is told to initialize. - // In response, this (native) side of the plugin needs to spin up a background - // Dart isolate by using the given pluginCallbackHandle, and then setup a background - // method channel to communicate with the new background isolate. Once completed, - // this onMethodCall() method will receive messages from both the primary and background - // method channels. + // This message is sent when the Dart side of this plugin is told to initialize. + // In response, this (native) side of the plugin needs to spin up a background + // Dart isolate by using the given pluginCallbackHandle, and then setup a background + // method channel to communicate with the new background isolate. Once completed, + // this onMethodCall() method will receive messages from both the primary and background + // method channels. case "Messaging#startBackgroundIsolate": @SuppressWarnings("unchecked") Map arguments = ((Map) call.arguments); @@ -496,7 +499,8 @@ public void onMethodCall(final MethodCall call, @NonNull final Result result) { break; case "Messaging#requestPermission": if (Build.VERSION.SDK_INT >= 33) { - // Android version >= Android 13 requires user input if notification permission not set/granted + // Android version >= Android 13 requires user input if notification permission not + // set/granted methodCallTask = requestPermissions(); } else { // Android version < Android 13 doesn't require asking for runtime permissions. diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java index ebaeca9ffe24..f9e10df1bd1c 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingReceiver.java @@ -31,7 +31,8 @@ public void onReceive(Context context, Intent intent) { if (intent.getExtras() == null) { Log.d( TAG, - "broadcast received but intent contained no extras to process RemoteMessage. Operation cancelled."); + "broadcast received but intent contained no extras to process RemoteMessage. Operation" + + " cancelled."); return; } @@ -59,7 +60,8 @@ public void onReceive(Context context, Intent intent) { Parcel parcel = Parcel.obtain(); remoteMessage.writeToParcel(parcel, 0); - // We write to parcel using RemoteMessage.writeToParcel() to pass entire RemoteMessage as array of bytes + // We write to parcel using RemoteMessage.writeToParcel() to pass entire RemoteMessage as array + // of bytes // Which can be read using RemoteMessage.createFromParcel(parcel) API onBackgroundMessageIntent.putExtra( FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, parcel.marshall()); diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java index 6f50bd40bcb2..85d557453646 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java @@ -17,6 +17,7 @@ public void onNewToken(@NonNull String token) { @Override public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { // Added for commenting purposes; - // We don't handle the message here as we already handle it in the receiver and don't want to duplicate. + // We don't handle the message here as we already handle it in the receiver and don't want to + // duplicate. } } diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingStore.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingStore.java index 95573e3b43f0..1dfaf754d023 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingStore.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingStore.java @@ -55,8 +55,10 @@ public void storeFirebaseMessage(RemoteMessage remoteMessage) { setPreferencesStringValue(remoteMessage.getMessageId(), remoteMessageString); // Save new notification id. - // Note that this is using a comma delimited string to preserve ordering. We could use a String Set - // on SharedPreferences but this won't guarantee ordering when we want to remove the oldest added ids. + // Note that this is using a comma delimited string to preserve ordering. We could use a String + // Set + // on SharedPreferences but this won't guarantee ordering when we want to remove the oldest + // added ids. String notifications = getPreferencesStringValue(KEY_NOTIFICATION_IDS, ""); notifications += remoteMessage.getMessageId() + DELIMITER; // append to last diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebasePermissionManager.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebasePermissionManager.java index 9087ef26e847..ad1202bd0b59 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebasePermissionManager.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebasePermissionManager.java @@ -17,7 +17,11 @@ import java.util.ArrayList; class FlutterFirebasePermissionManager implements PluginRegistry.RequestPermissionsResultListener { + private static final String REQUEST_IN_PROGRESS_ERROR = + "A request for permissions is already running, please wait for it to finish before doing " + + "another request."; + private final Object requestLock = new Object(); private final int permissionCode = 240; @Nullable private RequestPermissionsSuccessCallback successCallback; private boolean requestInProgress = false; @@ -30,15 +34,32 @@ interface RequestPermissionsSuccessCallback { @Override public boolean onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (requestInProgress && requestCode == permissionCode && this.successCallback != null) { + final RequestPermissionsSuccessCallback callback; + synchronized (requestLock) { + if (!requestInProgress || requestCode != permissionCode || this.successCallback == null) { + return false; + } + + callback = this.successCallback; + this.successCallback = null; requestInProgress = false; - boolean granted = - grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; + } + + boolean granted = + grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; + callback.onSuccess(granted ? 1 : 0); + return true; + } + + private boolean setRequestInProgress(RequestPermissionsSuccessCallback successCallback) { + synchronized (requestLock) { + if (requestInProgress) { + return false; + } - this.successCallback.onSuccess(granted ? 1 : 0); + requestInProgress = true; + this.successCallback = successCallback; return true; - } else { - return false; } } @@ -47,25 +68,20 @@ public void requestPermissions( Activity activity, RequestPermissionsSuccessCallback successCallback, ErrorCallback errorCallback) { - if (requestInProgress) { - errorCallback.onError( - "A request for permissions is already running, please wait for it to finish before doing another request."); + if (activity == null) { + errorCallback.onError("Unable to detect current Android Activity."); return; } - if (activity == null) { - errorCallback.onError("Unable to detect current Android Activity."); + if (!setRequestInProgress(successCallback)) { + errorCallback.onError(REQUEST_IN_PROGRESS_ERROR); return; } - this.successCallback = successCallback; final ArrayList permissions = new ArrayList(); permissions.add(Manifest.permission.POST_NOTIFICATIONS); final String[] requestNotificationPermission = permissions.toArray(new String[0]); - if (!requestInProgress) { - ActivityCompat.requestPermissions(activity, requestNotificationPermission, permissionCode); - requestInProgress = true; - } + ActivityCompat.requestPermissions(activity, requestNotificationPermission, permissionCode); } } diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/PluginRegistrantException.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/PluginRegistrantException.java index 4787b643784c..67d882312c91 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/PluginRegistrantException.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/PluginRegistrantException.java @@ -8,7 +8,8 @@ class PluginRegistrantException extends RuntimeException { public PluginRegistrantException() { super( - "PluginRegistrantCallback is not set. Did you forget to call " - + "FlutterFirebaseMessagingBackgroundService.setPluginRegistrant? See the documentation for instructions."); + "PluginRegistrantCallback is not set. Did you forget to call" + + " FlutterFirebaseMessagingBackgroundService.setPluginRegistrant? See the" + + " documentation for instructions."); } } diff --git a/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/messaging/example/MainActivity.kt b/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/messaging/example/MainActivity.kt index 2be79f209e91..c5b5e08c88f8 100644 --- a/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/messaging/example/MainActivity.kt +++ b/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/messaging/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.messaging.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/package.json b/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/package.json index 3f3d3ce3102d..7905143e79e1 100644 --- a/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/package.json +++ b/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/package.json @@ -3,7 +3,7 @@ "firebase": "12" }, "devDependencies": { - "esbuild": "^0.25.0" + "esbuild": "^0.28.1" }, "scripts": { "build": "esbuild firebase-messaging-sw.ts --outdir=../web --bundle --sourcemap --minify --format=esm" diff --git a/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/yarn.lock b/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/yarn.lock index 64d0ae8cf139..6a54b940e7bf 100644 --- a/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/yarn.lock +++ b/packages/firebase_messaging/firebase_messaging/example/bundled-service-worker/yarn.lock @@ -2,130 +2,135 @@ # yarn lockfile v1 -"@esbuild/aix-ppc64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64" - integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ== - -"@esbuild/android-arm64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f" - integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g== - -"@esbuild/android-arm@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b" - integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g== - -"@esbuild/android-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163" - integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg== - -"@esbuild/darwin-arm64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c" - integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw== - -"@esbuild/darwin-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a" - integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg== - -"@esbuild/freebsd-arm64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce" - integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w== - -"@esbuild/freebsd-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7" - integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A== - -"@esbuild/linux-arm64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73" - integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg== - -"@esbuild/linux-arm@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3" - integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg== - -"@esbuild/linux-ia32@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19" - integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg== - -"@esbuild/linux-loong64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7" - integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw== - -"@esbuild/linux-mips64el@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1" - integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ== - -"@esbuild/linux-ppc64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951" - integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw== - -"@esbuild/linux-riscv64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987" - integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA== - -"@esbuild/linux-s390x@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4" - integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA== - -"@esbuild/linux-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a" - integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw== - -"@esbuild/netbsd-arm64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b" - integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw== - -"@esbuild/netbsd-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b" - integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA== - -"@esbuild/openbsd-arm64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7" - integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw== - -"@esbuild/openbsd-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde" - integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg== - -"@esbuild/sunos-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92" - integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg== - -"@esbuild/win32-arm64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c" - integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw== - -"@esbuild/win32-ia32@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079" - integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA== - -"@esbuild/win32-x64@0.25.0": - version "0.25.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b" - integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ== +"@esbuild/aix-ppc64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz#7a01a8d2ec2fbb2dac78adad09b0fa781e4082be" + integrity sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ== + +"@esbuild/android-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz#b540a27d14e4afd058496a4dbec4d3f414db110a" + integrity sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg== + +"@esbuild/android-arm@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.28.1.tgz#704bd297de6d762de54eabbeafbf55f6756abe2f" + integrity sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ== + +"@esbuild/android-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.28.1.tgz#d1cb166d34b0fbf0fe8ab460a5594f24a378701e" + integrity sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng== + +"@esbuild/darwin-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz#1034b26457fc886368fe61bbd09f653f6afa8e54" + integrity sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q== + +"@esbuild/darwin-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz#65556a432a1e4d72032d8218c1932fcca1a49772" + integrity sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ== + +"@esbuild/freebsd-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz#2e61e0592f9030d7e3dae18ee25ebc535918aef6" + integrity sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw== + +"@esbuild/freebsd-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz#c95ec289959ef8079c4dca817a1e2c4be66b9bd3" + integrity sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ== + +"@esbuild/linux-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz#40b22175dda06182f3ee8141186c5ff304c4a717" + integrity sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g== + +"@esbuild/linux-arm@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz#c09a0f67917592ac0de892a9be4d3814debd2a6c" + integrity sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ== + +"@esbuild/linux-ia32@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz#a580f9c676797833891e519fc7a1337c8afd8db3" + integrity sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w== + +"@esbuild/linux-loong64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz#46452cf321dc7f9e91c2fa780a56bb56e79cd68b" + integrity sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg== + +"@esbuild/linux-mips64el@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz#4211b3184dd6608f53dcb22e39f5d34ee08852c8" + integrity sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ== + +"@esbuild/linux-ppc64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz#697857c2a61cb9b0b6bb6652e40c1dc5e1ca8e5d" + integrity sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ== + +"@esbuild/linux-riscv64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz#d192943eb146a40ac4c6497d0cf7be35b986bf08" + integrity sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ== + +"@esbuild/linux-s390x@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz#acea0356da0e0ebc08f97cf7b9c2e401e1e648dc" + integrity sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag== + +"@esbuild/linux-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz#6f0c3ce0cb64c534b70c4c45ecb2c16d34e35dfd" + integrity sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA== + +"@esbuild/netbsd-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz#8bcd77077a0dce3378b574fedb26d2a253b73d36" + integrity sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw== + +"@esbuild/netbsd-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz#e7fb2a01e99c830c94e6623cd9fefb4c8fb58347" + integrity sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg== + +"@esbuild/openbsd-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz#c52909372db8b86e2c55e05a8940033b5660a3b2" + integrity sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q== + +"@esbuild/openbsd-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz#c427b9be5a64c262ff9a7eb70b5fbbaadf446c6c" + integrity sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw== + +"@esbuild/openharmony-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz#dc9b147baca2e6c4b3c85571741ef4860a489097" + integrity sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg== + +"@esbuild/sunos-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz#ce866d12df13c15e4c99f073a3d466f6e0649b3a" + integrity sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ== + +"@esbuild/win32-arm64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz#7468e3692d01d629d5941e5d83817bb80f9e39b4" + integrity sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA== + +"@esbuild/win32-ia32@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz#a5bc0063fb2bcab6d0ed63f2a1537958bc269ec6" + integrity sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg== + +"@esbuild/win32-x64@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz#10064ee44f4347b90c9a02b446bbf80a91632b12" + integrity sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A== "@firebase/ai@2.9.0": version "2.9.0" @@ -519,9 +524,9 @@ integrity sha512-+uGNN7rkfn41HLO0vekTFhTxk61eKa8mTpRGLO0QSqlQdKvIoGAvLp3ppdVIWbTGYJWM6Kp0iN+PjMIOcnVqTw== "@grpc/grpc-js@~1.9.0": - version "1.9.15" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.15.tgz#433d7ac19b1754af690ea650ab72190bd700739b" - integrity sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ== + version "1.9.16" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.16.tgz#614f85036ac8e3c957374c1bd1ebb05934a79a1c" + integrity sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ== dependencies: "@grpc/proto-loader" "^0.7.8" "@types/node" ">=12.12.47" @@ -546,34 +551,28 @@ resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== +"@protobufjs/codegen@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.5.tgz#d9315ad7cf3f30aac70bda3c068443dc6f143659" + integrity sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g== -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== +"@protobufjs/eventemitter@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz#d512cb26c0ae026091ee2c1167f1be6faf5c842a" + integrity sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg== -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== +"@protobufjs/fetch@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.1.tgz#4d6fc00c8fb64016a5c81b469d549046350f1065" + integrity sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw== dependencies: "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" "@protobufjs/float@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== - "@protobufjs/path@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" @@ -584,10 +583,10 @@ resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@protobufjs/utf8@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.1.tgz#eaee5900122c110a3dbcb728c0597014a2621774" + integrity sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg== "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "18.8.2" @@ -632,36 +631,37 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -esbuild@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92" - integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw== +esbuild@^0.28.1: + version "0.28.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.28.1.tgz#ef45b4634c9c9d97a296aea4114a5f9840f95578" + integrity sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw== optionalDependencies: - "@esbuild/aix-ppc64" "0.25.0" - "@esbuild/android-arm" "0.25.0" - "@esbuild/android-arm64" "0.25.0" - "@esbuild/android-x64" "0.25.0" - "@esbuild/darwin-arm64" "0.25.0" - "@esbuild/darwin-x64" "0.25.0" - "@esbuild/freebsd-arm64" "0.25.0" - "@esbuild/freebsd-x64" "0.25.0" - "@esbuild/linux-arm" "0.25.0" - "@esbuild/linux-arm64" "0.25.0" - "@esbuild/linux-ia32" "0.25.0" - "@esbuild/linux-loong64" "0.25.0" - "@esbuild/linux-mips64el" "0.25.0" - "@esbuild/linux-ppc64" "0.25.0" - "@esbuild/linux-riscv64" "0.25.0" - "@esbuild/linux-s390x" "0.25.0" - "@esbuild/linux-x64" "0.25.0" - "@esbuild/netbsd-arm64" "0.25.0" - "@esbuild/netbsd-x64" "0.25.0" - "@esbuild/openbsd-arm64" "0.25.0" - "@esbuild/openbsd-x64" "0.25.0" - "@esbuild/sunos-x64" "0.25.0" - "@esbuild/win32-arm64" "0.25.0" - "@esbuild/win32-ia32" "0.25.0" - "@esbuild/win32-x64" "0.25.0" + "@esbuild/aix-ppc64" "0.28.1" + "@esbuild/android-arm" "0.28.1" + "@esbuild/android-arm64" "0.28.1" + "@esbuild/android-x64" "0.28.1" + "@esbuild/darwin-arm64" "0.28.1" + "@esbuild/darwin-x64" "0.28.1" + "@esbuild/freebsd-arm64" "0.28.1" + "@esbuild/freebsd-x64" "0.28.1" + "@esbuild/linux-arm" "0.28.1" + "@esbuild/linux-arm64" "0.28.1" + "@esbuild/linux-ia32" "0.28.1" + "@esbuild/linux-loong64" "0.28.1" + "@esbuild/linux-mips64el" "0.28.1" + "@esbuild/linux-ppc64" "0.28.1" + "@esbuild/linux-riscv64" "0.28.1" + "@esbuild/linux-s390x" "0.28.1" + "@esbuild/linux-x64" "0.28.1" + "@esbuild/netbsd-arm64" "0.28.1" + "@esbuild/netbsd-x64" "0.28.1" + "@esbuild/openbsd-arm64" "0.28.1" + "@esbuild/openbsd-x64" "0.28.1" + "@esbuild/openharmony-arm64" "0.28.1" + "@esbuild/sunos-x64" "0.28.1" + "@esbuild/win32-arm64" "0.28.1" + "@esbuild/win32-ia32" "0.28.1" + "@esbuild/win32-x64" "0.28.1" escalade@^3.1.1: version "3.1.1" @@ -739,23 +739,27 @@ long@^5.0.0: resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w== +long@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" + integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + protobufjs@^7.2.5: - version "7.4.0" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" - integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== + version "7.6.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.6.4.tgz#8bb000300026efd63eb7951d26e5dbb38f5658f2" + integrity sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" + "@protobufjs/codegen" "^2.0.5" + "@protobufjs/eventemitter" "^1.1.1" + "@protobufjs/fetch" "^1.1.1" "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" "@protobufjs/path" "^1.1.2" "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" + "@protobufjs/utf8" "^1.1.1" "@types/node" ">=13.7.0" - long "^5.0.0" + long "^5.3.2" require-directory@^2.1.1: version "2.1.1" diff --git a/packages/firebase_messaging/firebase_messaging/example/lib/main.dart b/packages/firebase_messaging/firebase_messaging/example/lib/main.dart index 128d68951a0b..6175abd772c4 100644 --- a/packages/firebase_messaging/firebase_messaging/example/lib/main.dart +++ b/packages/firebase_messaging/firebase_messaging/example/lib/main.dart @@ -98,10 +98,10 @@ void showFlutterNotification(RemoteMessage message) { AndroidNotification? android = message.notification?.android; if (notification != null && android != null && !kIsWeb) { flutterLocalNotificationsPlugin.show( - notification.hashCode, - notification.title, - notification.body, - NotificationDetails( + id: notification.hashCode, + title: notification.title, + body: notification.body, + notificationDetails: NotificationDetails( android: AndroidNotificationDetails( channel.id, channel.name, diff --git a/packages/firebase_messaging/firebase_messaging/example/pubspec.yaml b/packages/firebase_messaging/firebase_messaging/example/pubspec.yaml index 3b4dd16c1a48..636eb595024b 100644 --- a/packages/firebase_messaging/firebase_messaging/example/pubspec.yaml +++ b/packages/firebase_messaging/firebase_messaging/example/pubspec.yaml @@ -1,16 +1,17 @@ name: firebase_messaging_example description: Demonstrates how to use the firebase_messaging plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_messaging: ^16.1.3 + firebase_core: ^4.11.0 + firebase_messaging: ^16.4.1 flutter: sdk: flutter - flutter_local_notifications: ^17.2.1 + flutter_local_notifications: ^21.0.0 http: ^1.0.0 flutter: diff --git a/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Package.swift b/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Package.swift index 2907358115f9..303f043dab1f 100644 --- a/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Package.swift +++ b/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "16.1.2" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "16.4.1" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_messaging", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-messaging", targets: ["firebase_messaging"]), + .library(name: "firebase-messaging", targets: ["firebase_messaging"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-fcm\""), ] - ), + ) ] ) diff --git a/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/FLTFirebaseMessagingPlugin.m b/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/FLTFirebaseMessagingPlugin.m index 2dd3f536edcb..77f2078991c6 100644 --- a/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/FLTFirebaseMessagingPlugin.m +++ b/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/FLTFirebaseMessagingPlugin.m @@ -226,6 +226,11 @@ - (void)messaging:(nonnull FIRMessaging *)messaging - (void)setupNotificationHandlingWithRemoteNotification: (nullable NSDictionary *)remoteNotification { + [self setupNotificationHandlingWithRemoteNotification:remoteNotification actionIdentifier:nil]; +} + +- (void)setupNotificationHandlingWithRemoteNotification:(nullable NSDictionary *)remoteNotification + actionIdentifier:(nullable NSString *)actionIdentifier { // If notification handling was already set up (e.g. from // application_onDidFinishLaunchingNotification) and we're called again (e.g. from // scene:willConnectToSession:), only process the notification but skip delegate/swizzler @@ -234,7 +239,8 @@ - (void)setupNotificationHandlingWithRemoteNotification: if (_notificationHandlingSetup) { if (remoteNotification != nil) { _initialNotification = - [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification]; + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification + withActionIdentifier:actionIdentifier]; _initialNotificationID = remoteNotification[@"gcm.message_id"]; _initialNotificationGathered = YES; [self initialNotificationCallback]; @@ -255,7 +261,8 @@ - (void)setupNotificationHandlingWithRemoteNotification: if (remoteNotification != nil) { // If remoteNotification exists, it is the notification that opened the app. _initialNotification = - [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification]; + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification + withActionIdentifier:actionIdentifier]; _initialNotificationID = remoteNotification[@"gcm.message_id"]; _initialNotificationGathered = YES; [self initialNotificationCallback]; @@ -263,17 +270,22 @@ - (void)setupNotificationHandlingWithRemoteNotification: // For scene delegates, if no notification was found in connectionOptions, // delay marking as gathered to allow didReceiveRemoteNotification to fire first // for contentAvailable notifications that caused the app to launch - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - if (!self->_initialNotificationGathered) { - self->_initialNotificationGathered = YES; - [self initialNotificationCallback]; - } - }); + [self markInitialNotificationGatheredAfterDelay]; } else { - // For non-scene delegate apps, mark as gathered immediately - _initialNotificationGathered = YES; - [self initialNotificationCallback]; +#if !TARGET_OS_OSX + if (@available(iOS 13.0, *)) { + // Scene delegate launch notification responses arrive after didFinishLaunching. + // Give scene:willConnectToSession:options: a chance to provide the tapped notification + // before resolving getInitialMessage() as nil. + [self markInitialNotificationGatheredAfterDelay]; + } else { +#endif + // For non-scene delegate apps, mark as gathered immediately + _initialNotificationGathered = YES; + [self initialNotificationCallback]; +#if !TARGET_OS_OSX + } +#endif } [GULAppDelegateSwizzler registerAppDelegateInterceptor:self]; @@ -357,6 +369,16 @@ - (void)setupNotificationHandlingWithRemoteNotification: #endif } +- (void)markInitialNotificationGatheredAfterDelay { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + if (!self->_initialNotificationGathered) { + self->_initialNotificationGathered = YES; + [self initialNotificationCallback]; + } + }); +} + - (void)application_onDidFinishLaunchingNotification:(nonnull NSNotification *)notification { // Setup UIApplicationDelegate. #if TARGET_OS_OSX @@ -443,12 +465,20 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center API_AVAILABLE(macos(10.14), ios(10.0)) { NSDictionary *remoteNotification = response.notification.request.content.userInfo; _notificationOpenedAppID = remoteNotification[@"gcm.message_id"]; + NSDictionary *notificationDict = + [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification + withActionIdentifier:response.actionIdentifier]; + + if (_initialNotification != nil && + [_initialNotificationID isEqualToString:_notificationOpenedAppID]) { + _initialNotification = notificationDict; + [self initialNotificationCallback]; + } + // We only want to handle FCM notifications and stop firing `onMessageOpenedApp()` when app is // coming from a terminated state. if (_notificationOpenedAppID != nil && ![_initialNotificationID isEqualToString:_notificationOpenedAppID]) { - NSDictionary *notificationDict = - [FLTFirebaseMessagingPlugin remoteMessageUserInfoToDict:remoteNotification]; [_channel invokeMethod:@"Messaging#onMessageOpenedApp" arguments:notificationDict]; } @@ -627,13 +657,17 @@ - (void)scene:(UIScene *)scene _sceneDidConnect = YES; NSDictionary *remoteNotification = nil; + NSString *actionIdentifier = nil; if (connectionOptions.notificationResponse != nil) { // User tapped the notification. remoteNotification = connectionOptions.notificationResponse.notification.request.content.userInfo; + actionIdentifier = connectionOptions.notificationResponse.actionIdentifier; + _notificationOpenedAppID = remoteNotification[@"gcm.message_id"]; } - [self setupNotificationHandlingWithRemoteNotification:remoteNotification]; + [self setupNotificationHandlingWithRemoteNotification:remoteNotification + actionIdentifier:actionIdentifier]; } #endif @@ -944,11 +978,21 @@ + (NSDictionary *)NSDictionaryFromUNNotification:(UNNotification *)notification } + (NSDictionary *)remoteMessageUserInfoToDict:(NSDictionary *)userInfo { + return [self remoteMessageUserInfoToDict:userInfo withActionIdentifier:nil]; +} + ++ (NSDictionary *)remoteMessageUserInfoToDict:(NSDictionary *)userInfo + withActionIdentifier:(nullable NSString *)actionIdentifier { NSMutableDictionary *message = [[NSMutableDictionary alloc] init]; NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; NSMutableDictionary *notification = [[NSMutableDictionary alloc] init]; NSMutableDictionary *notificationIOS = [[NSMutableDictionary alloc] init]; + // message.actionIdentifier + if (actionIdentifier != nil) { + message[@"actionIdentifier"] = actionIdentifier; + } + // message.data for (id key in userInfo) { // message.messageId diff --git a/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/include/FLTFirebaseMessagingPlugin.h b/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/include/FLTFirebaseMessagingPlugin.h index d233a4889764..23125d5dff3a 100644 --- a/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/include/FLTFirebaseMessagingPlugin.h +++ b/packages/firebase_messaging/firebase_messaging/ios/firebase_messaging/Sources/firebase_messaging/include/FLTFirebaseMessagingPlugin.h @@ -54,7 +54,7 @@ API_AVAILABLE(ios(10.0)) FLTFirebasePlugin, FIRMessagingDelegate, UIApplicationDelegate -#if __has_include() || defined(FlutterSceneLifeCycleDelegate) +#if __has_include() , FlutterSceneLifeCycleDelegate #endif @@ -63,7 +63,7 @@ API_AVAILABLE(ios(10.0)) @interface FLTFirebaseMessagingPlugin : FLTFirebasePlugin ) || defined(FlutterSceneLifeCycleDelegate) +#if __has_include() , FlutterSceneLifeCycleDelegate #endif diff --git a/packages/firebase_messaging/firebase_messaging/lib/firebase_messaging.dart b/packages/firebase_messaging/firebase_messaging/lib/firebase_messaging.dart index 6df5ee9d223a..1783d4654c91 100644 --- a/packages/firebase_messaging/firebase_messaging/lib/firebase_messaging.dart +++ b/packages/firebase_messaging/firebase_messaging/lib/firebase_messaging.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart'; export 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart' diff --git a/packages/firebase_messaging/firebase_messaging/lib/src/messaging.dart b/packages/firebase_messaging/firebase_messaging/lib/src/messaging.dart index 4205bdd61bc1..b9f6a0ccea45 100644 --- a/packages/firebase_messaging/firebase_messaging/lib/src/messaging.dart +++ b/packages/firebase_messaging/firebase_messaging/lib/src/messaging.dart @@ -8,7 +8,7 @@ part of '../firebase_messaging.dart'; /// The [FirebaseMessaging] entry point. /// /// To get a new instance, call [FirebaseMessaging.instance]. -class FirebaseMessaging extends FirebasePluginPlatform { +class FirebaseMessaging extends FirebasePlugin { // Cached and lazily loaded instance of [FirebaseMessagingPlatform] to avoid // creating a [MethodChannelFirebaseMessaging] when not needed or creating an // instance with the default app before a user specifies an app. @@ -114,11 +114,17 @@ class FirebaseMessaging extends FirebasePluginPlatform { /// Returns the default FCM token for this device. /// /// On web, a [vapidKey] is required. + /// + /// On web, a custom messaging service worker can be registered with + /// [serviceWorkerScriptPath]. This must point to a JavaScript file in the + /// root of the app's `web` directory. Future getToken({ String? vapidKey, + String? serviceWorkerScriptPath, }) { return _delegate.getToken( vapidKey: vapidKey, + serviceWorkerScriptPath: serviceWorkerScriptPath, ); } diff --git a/packages/firebase_messaging/firebase_messaging/macos/firebase_messaging/Package.swift b/packages/firebase_messaging/firebase_messaging/macos/firebase_messaging/Package.swift index e475afc47cb2..c60187265b2e 100644 --- a/packages/firebase_messaging/firebase_messaging/macos/firebase_messaging/Package.swift +++ b/packages/firebase_messaging/firebase_messaging/macos/firebase_messaging/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "16.1.2" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "16.4.1" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_messaging", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-messaging", targets: ["firebase_messaging"]), + .library(name: "firebase-messaging", targets: ["firebase_messaging"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-fcm\""), ] - ), + ) ] ) diff --git a/packages/firebase_messaging/firebase_messaging/pubspec.yaml b/packages/firebase_messaging/firebase_messaging/pubspec.yaml index 16621ec0dd97..63fef2fddcbd 100644 --- a/packages/firebase_messaging/firebase_messaging/pubspec.yaml +++ b/packages/firebase_messaging/firebase_messaging/pubspec.yaml @@ -3,7 +3,8 @@ description: Flutter plugin for Firebase Cloud Messaging, a cross-platform messaging solution that lets you reliably deliver messages on Android and iOS. homepage: https://firebase.google.com/docs/cloud-messaging repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_messaging/firebase_messaging -version: 16.1.3 +version: 16.4.1 +resolution: workspace topics: - firebase - messaging @@ -13,14 +14,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_messaging_platform_interface: ^4.7.8 - firebase_messaging_web: ^4.1.4 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_messaging_platform_interface: ^4.9.0 + firebase_messaging_web: ^4.2.1 flutter: sdk: flutter meta: ^1.8.0 diff --git a/packages/firebase_messaging/firebase_messaging/test/firebase_messaging_test.dart b/packages/firebase_messaging/firebase_messaging/test/firebase_messaging_test.dart index af53b2720741..f3e1dd4204bb 100644 --- a/packages/firebase_messaging/firebase_messaging/test/firebase_messaging_test.dart +++ b/packages/firebase_messaging/firebase_messaging/test/firebase_messaging_test.dart @@ -99,12 +99,35 @@ void main() { group('getToken', () { test('verify delegate method is called with correct args', () async { const vapidKey = 'test-vapid-key'; - when(kMockMessagingPlatform.getToken(vapidKey: anyNamed('vapidKey'))) - .thenAnswer((_) => Future.value('')); + when(kMockMessagingPlatform.getToken( + vapidKey: anyNamed('vapidKey'), + serviceWorkerScriptPath: anyNamed('serviceWorkerScriptPath'), + )).thenAnswer((_) => Future.value('')); await messaging!.getToken(vapidKey: vapidKey); - verify(kMockMessagingPlatform.getToken(vapidKey: vapidKey)); + verify(kMockMessagingPlatform.getToken( + vapidKey: vapidKey, + serviceWorkerScriptPath: null, + )); + }); + + test('verify delegate method is called with service worker path', + () async { + const serviceWorkerScriptPath = 'custom-messaging-sw.js'; + when(kMockMessagingPlatform.getToken( + vapidKey: anyNamed('vapidKey'), + serviceWorkerScriptPath: anyNamed('serviceWorkerScriptPath'), + )).thenAnswer((_) => Future.value('')); + + await messaging!.getToken( + serviceWorkerScriptPath: serviceWorkerScriptPath, + ); + + verify(kMockMessagingPlatform.getToken( + vapidKey: null, + serviceWorkerScriptPath: serviceWorkerScriptPath, + )); }); }); diff --git a/packages/firebase_messaging/firebase_messaging/test/mock.dart b/packages/firebase_messaging/firebase_messaging/test/mock.dart index 2493e87ee85b..4555c8d77d30 100644 --- a/packages/firebase_messaging/firebase_messaging/test/mock.dart +++ b/packages/firebase_messaging/firebase_messaging/test/mock.dart @@ -96,9 +96,12 @@ class MockFirebaseMessaging extends Mock } @override - Future getToken({String? vapidKey}) { + Future getToken({String? vapidKey, String? serviceWorkerScriptPath}) { return super.noSuchMethod( - Invocation.method(#getToken, [], {#vapidKey: vapidKey}), + Invocation.method(#getToken, [], { + #vapidKey: vapidKey, + #serviceWorkerScriptPath: serviceWorkerScriptPath + }), returnValue: Future.value(''), returnValueForMissingStub: Future.value('')); } diff --git a/packages/firebase_messaging/firebase_messaging_platform_interface/CHANGELOG.md b/packages/firebase_messaging/firebase_messaging_platform_interface/CHANGELOG.md index e3055087b231..819ee4094334 100644 --- a/packages/firebase_messaging/firebase_messaging_platform_interface/CHANGELOG.md +++ b/packages/firebase_messaging/firebase_messaging_platform_interface/CHANGELOG.md @@ -1,3 +1,23 @@ +## 4.9.0 + + - **FEAT**(messaging,ios): add support for actionIdentifier on iOS devices ([#18357](https://github.com/firebase/flutterfire/issues/18357)). ([d60af4d9](https://github.com/firebase/flutterfire/commit/d60af4d9e1345c113490e875c85bd9ac62dad935)) + +## 4.8.0 + + - **FEAT**(messaging,web): add support for custom service worker script path in `getToken` method ([#18290](https://github.com/firebase/flutterfire/issues/18290)). ([b37722db](https://github.com/firebase/flutterfire/commit/b37722db13548aca57b3a24ba0f27b5de021be02)) + +## 4.7.11 + + - Update a dependency to the latest release. + +## 4.7.10 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 4.7.9 + + - Update a dependency to the latest release. + ## 4.7.8 - Update a dependency to the latest release. diff --git a/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/method_channel/method_channel_messaging.dart b/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/method_channel/method_channel_messaging.dart index ffaac715c5b2..73788d05c3de 100644 --- a/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/method_channel/method_channel_messaging.dart +++ b/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/method_channel/method_channel_messaging.dart @@ -240,6 +240,7 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform { @override Future getToken({ String? vapidKey, // not used yet; web only property + String? serviceWorkerScriptPath, // web only property }) async { await _APNSTokenCheck(); diff --git a/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/platform_interface/platform_interface_messaging.dart b/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/platform_interface/platform_interface_messaging.dart index 3e0ffa7d90d8..44d20d32ed5b 100644 --- a/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/platform_interface/platform_interface_messaging.dart +++ b/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/platform_interface/platform_interface_messaging.dart @@ -173,6 +173,7 @@ abstract class FirebaseMessagingPlatform extends PlatformInterface { /// Returns the default FCM token for this device and optionally [senderId]. Future getToken({ String? vapidKey, + String? serviceWorkerScriptPath, }) { throw UnimplementedError('getToken() is not implemented'); } diff --git a/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/remote_message.dart b/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/remote_message.dart index cc596a496ba0..1b39fad8d387 100644 --- a/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/remote_message.dart +++ b/packages/firebase_messaging/firebase_messaging_platform_interface/lib/src/remote_message.dart @@ -11,6 +11,7 @@ class RemoteMessage { const RemoteMessage( {this.senderId, this.category, + this.actionIdentifier, this.collapseKey, this.contentAvailable = false, this.data = const {}, @@ -28,6 +29,7 @@ class RemoteMessage { return RemoteMessage( senderId: map['senderId'], category: map['category'], + actionIdentifier: map['actionIdentifier'], collapseKey: map['collapseKey'], contentAvailable: map['contentAvailable'] ?? false, data: map['data'] == null @@ -57,6 +59,7 @@ class RemoteMessage { return { 'senderId': senderId, 'category': category, + 'actionIdentifier': actionIdentifier, 'collapseKey': collapseKey, 'contentAvailable': contentAvailable, 'data': data, @@ -77,6 +80,9 @@ class RemoteMessage { /// The iOS category this notification is assigned to. final String? category; + /// The Apple notification action identifier used to open the app. + final String? actionIdentifier; + /// The collapse key a message was sent with. Used to override existing messages with the same key. final String? collapseKey; diff --git a/packages/firebase_messaging/firebase_messaging_platform_interface/pubspec.yaml b/packages/firebase_messaging/firebase_messaging_platform_interface/pubspec.yaml index 28109d03b51b..898d483ec248 100644 --- a/packages/firebase_messaging/firebase_messaging_platform_interface/pubspec.yaml +++ b/packages/firebase_messaging/firebase_messaging_platform_interface/pubspec.yaml @@ -1,23 +1,24 @@ name: firebase_messaging_platform_interface description: A common platform interface for the firebase_messaging plugin. -version: 4.7.8 +version: 4.9.0 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_messaging/firebase_messaging_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_messaging/firebase_messaging_platform_interface environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_messaging/firebase_messaging_platform_interface/test/remote_message_test.dart b/packages/firebase_messaging/firebase_messaging_platform_interface/test/remote_message_test.dart index 0868d2eab3dd..b676e3c1ea0a 100644 --- a/packages/firebase_messaging/firebase_messaging_platform_interface/test/remote_message_test.dart +++ b/packages/firebase_messaging/firebase_messaging_platform_interface/test/remote_message_test.dart @@ -16,6 +16,7 @@ void main() { mockMessageMap = { 'senderId': 'senderId', 'category': 'category', + 'actionIdentifier': 'actionIdentifier', 'collapseKey': 'collapseKey', 'contentAvailable': true, 'data': { @@ -38,6 +39,7 @@ void main() { mockNullableMessageMap = { 'senderId': null, 'category': null, + 'actionIdentifier': null, 'collapseKey': null, 'data': null, 'from': null, @@ -55,6 +57,7 @@ void main() { expect(message.senderId, mockMessageMap!['senderId']); expect(message.category, mockMessageMap!['category']); + expect(message.actionIdentifier, mockMessageMap!['actionIdentifier']); expect(message.collapseKey, mockMessageMap!['collapseKey']); expect(message.contentAvailable, mockMessageMap!['contentAvailable']); expect(message.data, mockMessageMap!['data']); @@ -85,6 +88,8 @@ void main() { expect(message.senderId, mockNullableMessageMap['senderId']); expect(message.category, mockNullableMessageMap['category']); + expect( + message.actionIdentifier, mockNullableMessageMap['actionIdentifier']); expect(message.collapseKey, mockNullableMessageMap['collapseKey']); expect(message.contentAvailable, false); expect(message.data, {}); @@ -105,6 +110,7 @@ void main() { final message = RemoteMessage( senderId: mockMessageMap!['senderId'], category: mockMessageMap!['category'], + actionIdentifier: mockMessageMap!['actionIdentifier'], collapseKey: mockMessageMap!['collapseKey'], contentAvailable: mockMessageMap!['contentAvailable'], data: mockMessageMap!['data'], @@ -120,6 +126,7 @@ void main() { expect(message.senderId, mockMessageMap!['senderId']); expect(message.category, mockMessageMap!['category']); + expect(message.actionIdentifier, mockMessageMap!['actionIdentifier']); expect(message.collapseKey, mockMessageMap!['collapseKey']); expect(message.contentAvailable, mockMessageMap!['contentAvailable']); expect(message.data, mockMessageMap!['data']); @@ -141,6 +148,7 @@ void main() { mockNullableMessageMap = { 'senderId': null, 'category': null, + 'actionIdentifier': null, 'collapseKey': null, 'data': null, 'from': null, @@ -156,6 +164,8 @@ void main() { expect(message.senderId, mockNullableMessageMap['senderId']); expect(message.category, mockNullableMessageMap['category']); + expect( + message.actionIdentifier, mockNullableMessageMap['actionIdentifier']); expect(message.collapseKey, mockNullableMessageMap['collapseKey']); expect(message.contentAvailable, false); expect(message.data, {}); @@ -173,6 +183,7 @@ void main() { final RemoteMessage remoteMessage = RemoteMessage( senderId: 'senderId', category: 'category', + actionIdentifier: 'actionIdentifier', collapseKey: 'collapseKey', contentAvailable: true, data: {}, @@ -193,6 +204,7 @@ void main() { expect(map['senderId'], remoteMessage.senderId); expect(map['category'], remoteMessage.category); + expect(map['actionIdentifier'], remoteMessage.actionIdentifier); expect(map['collapseKey'], remoteMessage.collapseKey); expect(map['contentAvailable'], remoteMessage.contentAvailable); expect(map['data'], remoteMessage.data); diff --git a/packages/firebase_messaging/firebase_messaging_web/CHANGELOG.md b/packages/firebase_messaging/firebase_messaging_web/CHANGELOG.md index 682a008f94c7..63be15929d2b 100644 --- a/packages/firebase_messaging/firebase_messaging_web/CHANGELOG.md +++ b/packages/firebase_messaging/firebase_messaging_web/CHANGELOG.md @@ -1,3 +1,23 @@ +## 4.2.1 + + - Update a dependency to the latest release. + +## 4.2.0 + + - **FEAT**(messaging,web): add support for custom service worker script path in `getToken` method ([#18290](https://github.com/firebase/flutterfire/issues/18290)). ([b37722db](https://github.com/firebase/flutterfire/commit/b37722db13548aca57b3a24ba0f27b5de021be02)) + +## 4.1.7 + + - Update a dependency to the latest release. + +## 4.1.6 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 4.1.5 + + - Update a dependency to the latest release. + ## 4.1.4 - Update a dependency to the latest release. diff --git a/packages/firebase_messaging/firebase_messaging_web/lib/firebase_messaging_web.dart b/packages/firebase_messaging/firebase_messaging_web/lib/firebase_messaging_web.dart index 1443a65d2fbd..7825b227fa1c 100644 --- a/packages/firebase_messaging/firebase_messaging_web/lib/firebase_messaging_web.dart +++ b/packages/firebase_messaging/firebase_messaging_web/lib/firebase_messaging_web.dart @@ -112,7 +112,8 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform { } @override - Future getToken({String? vapidKey}) async { + Future getToken( + {String? vapidKey, String? serviceWorkerScriptPath}) async { _delegate; if (!_initialized) { @@ -121,7 +122,8 @@ class FirebaseMessagingWeb extends FirebaseMessagingPlatform { } return convertWebExceptions( - () => _delegate.getToken(vapidKey: vapidKey), + () => _delegate.getToken( + vapidKey: vapidKey, serviceWorkerScriptPath: serviceWorkerScriptPath), ); } diff --git a/packages/firebase_messaging/firebase_messaging_web/lib/src/firebase_messaging_version.dart b/packages/firebase_messaging/firebase_messaging_web/lib/src/firebase_messaging_version.dart index 518fb0119f1c..63e6b5e50558 100644 --- a/packages/firebase_messaging/firebase_messaging_web/lib/src/firebase_messaging_version.dart +++ b/packages/firebase_messaging/firebase_messaging_web/lib/src/firebase_messaging_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '16.1.3'; +const packageVersion = '16.4.1'; diff --git a/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging.dart b/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging.dart index d3a0e2673b4a..265724c2c288 100644 --- a/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging.dart +++ b/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging.dart @@ -9,7 +9,7 @@ import 'dart:async'; import 'dart:js_interop'; import 'package:firebase_core_web/firebase_core_web_interop.dart'; - +import 'package:web/web.dart' as web; import 'messaging_interop.dart' as messaging_interop; export 'messaging_interop.dart'; @@ -43,15 +43,24 @@ class Messaging extends JsObjectWrapper { /// After calling [requestPermission] you can call this method to get an FCM registration token /// that can be used to send push messages to this user. - Future getToken({String? vapidKey}) async { + Future getToken( + {String? vapidKey, String? serviceWorkerScriptPath}) async { try { + web.ServiceWorkerRegistration? serviceWorkerRegistration; + if (serviceWorkerScriptPath != null) { + serviceWorkerRegistration = await web.window.navigator.serviceWorker + .register(serviceWorkerScriptPath.toJS) + .toDart; + } final token = (await messaging_interop .getToken( jsObject, - vapidKey == null + vapidKey == null && serviceWorkerRegistration == null ? null : messaging_interop.GetTokenOptions( - vapidKey: vapidKey.toJS)) + vapidKey: vapidKey?.toJS, + serviceWorkerRegistration: serviceWorkerRegistration, + )) .toDart) .toDart; return token; @@ -62,7 +71,10 @@ class Messaging extends JsObjectWrapper { if (err.toString().toLowerCase().contains('no active service worker') && firstGetTokenCall) { firstGetTokenCall = false; - return getToken(vapidKey: vapidKey); + return getToken( + vapidKey: vapidKey, + serviceWorkerScriptPath: serviceWorkerScriptPath, + ); } rethrow; } diff --git a/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging_interop.dart b/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging_interop.dart index 66e1fe87e49e..9603e421c961 100644 --- a/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging_interop.dart +++ b/packages/firebase_messaging/firebase_messaging_web/lib/src/interop/messaging_interop.dart @@ -9,6 +9,7 @@ library; import 'dart:js_interop'; +import 'package:web/web.dart' as web; import 'package:firebase_core_web/firebase_core_web_interop.dart'; @@ -50,8 +51,10 @@ extension type GetTokenOptions._(JSObject _) implements JSObject { external factory GetTokenOptions({ JSString? vapidKey, /*dynamic serviceWorkerRegistration */ + web.ServiceWorkerRegistration? serviceWorkerRegistration, }); external JSString get vapidKey; + external web.ServiceWorkerRegistration get serviceWorkerRegistration; } extension type NotificationPayloadJsImpl._(JSObject _) implements JSObject { diff --git a/packages/firebase_messaging/firebase_messaging_web/pubspec.yaml b/packages/firebase_messaging/firebase_messaging_web/pubspec.yaml index 7252cac92640..c01c9de8ade4 100644 --- a/packages/firebase_messaging/firebase_messaging_web/pubspec.yaml +++ b/packages/firebase_messaging/firebase_messaging_web/pubspec.yaml @@ -2,17 +2,18 @@ name: firebase_messaging_web description: The web implementation of firebase_messaging homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_messaging/firebase_messaging_web repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_messaging/firebase_messaging_web -version: 4.1.4 +version: 4.2.1 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 - firebase_messaging_platform_interface: ^4.7.8 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 + firebase_messaging_platform_interface: ^4.9.0 flutter: sdk: flutter flutter_web_plugins: @@ -21,7 +22,7 @@ dependencies: web: ^1.0.0 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/CHANGELOG.md b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/CHANGELOG.md index 271df45e21de..117f4710460a 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/CHANGELOG.md +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.4.2+4 + + - Update a dependency to the latest release. + +## 0.4.2+3 + + - Update a dependency to the latest release. + +## 0.4.2+2 + + - Update a dependency to the latest release. + +## 0.4.2+1 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.4.2 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 0.4.1 - **FEAT**(ios): migrate iOS to UIScene lifecycle ([#18054](https://github.com/firebase/flutterfire/issues/18054)). ([3ffa4110](https://github.com/firebase/flutterfire/commit/3ffa411098132fd5182a84be4e7a226106bc7451)) diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt index 57b5fca33169..88c6950b89c7 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.tests import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/ios/Runner/AppDelegate.swift b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/ios/Runner/AppDelegate.swift index b6363034812b..626664468b89 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/ios/Runner/AppDelegate.swift +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/ios/Runner/AppDelegate.swift @@ -1,5 +1,5 @@ -import UIKit import Flutter +import UIKit @main @objc class AppDelegate: FlutterAppDelegate { diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/lib/main.dart b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/lib/main.dart index d8bd1bca91c1..0d996951cb39 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/lib/main.dart +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/lib/main.dart @@ -35,7 +35,7 @@ class _MyAppState extends State { FirebaseCustomModel? model; /// Initially get the lcoal model if found, and asynchronously get the latest one in background. - initWithLocalModel() async { + Future initWithLocalModel() async { final newModel = await FirebaseModelDownloader.instance.getModel( kModelName, FirebaseModelDownloadType.localModelUpdateInBackground); diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/pubspec.yaml b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/pubspec.yaml index 2dc0801a060e..59798dc7aed1 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/pubspec.yaml +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example/pubspec.yaml @@ -1,20 +1,22 @@ name: firebase_ml_model_downloader_example description: Demonstrates how to use the firebase_ml_model_downloader plugin. +resolution: workspace publish_to: 'none' environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: flutter: sdk: flutter - firebase_core: ^4.6.0 - firebase_ml_model_downloader: ^0.4.1 + firebase_core: ^4.11.0 + firebase_ml_model_downloader: ^0.4.2+4 dev_dependencies: - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 flutter: uses-material-design: true diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Package.swift b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Package.swift index 401ec8921a0d..269a6407d7e9 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Package.swift +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_ml_model_downloader", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-ml-model-downloader", targets: ["firebase_ml_model_downloader"]), + .library(name: "firebase-ml-model-downloader", targets: ["firebase_ml_model_downloader"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/Constants.swift b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/Constants.swift index 96fe962609a3..2e6a3b8195d8 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/Constants.swift +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/Constants.swift @@ -3,4 +3,4 @@ // found in the LICENSE file. /// Auto-generated file. Do not edit. -public let versionNumber = "0.4.1" +public let versionNumber = "0.4.2+4" diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/FirebaseModelDownloaderPlugin.swift b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/FirebaseModelDownloaderPlugin.swift index 3d512ac1457a..94cc55407399 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/FirebaseModelDownloaderPlugin.swift +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/ios/firebase_ml_model_downloader/Sources/firebase_ml_model_downloader/FirebaseModelDownloaderPlugin.swift @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebaseCore +import FirebaseMLModelDownloader + #if canImport(FlutterMacOS) import FlutterMacOS #else import Flutter #endif -import FirebaseCore -import FirebaseMLModelDownloader - #if canImport(firebase_core) import firebase_core #else @@ -85,21 +85,26 @@ public class FirebaseModelDownloaderPlugin: NSObject, FLTFirebasePluginProtocol, var errorDetails = [String: Any?]() errorDetails["code"] = code ?? self.mapErrorCodes(error: error! as NSError) - errorDetails["message"] = message ?? error? + errorDetails["message"] = + message ?? error? .localizedDescription ?? "An unknown error has occurred." - errorDetails["additionalData"] = details ?? - ["code": errorDetails["code"], "message": errorDetails["message"]] + errorDetails["additionalData"] = + details ?? ["code": errorDetails["code"], "message": errorDetails["message"]] if code == "unknown" { NSLog("FLTFirebaseModelDownloader: An error occurred while calling method %@", call.method) } - result(FLTFirebasePlugin.createFlutterError(fromCode: errorDetails["code"] as! String, - message: errorDetails["message"] as! String, - optionalDetails: errorDetails[ - "additionalData" - ] as? [AnyHashable: Any], - andOptionalNSError: nil)) + result( + FLTFirebasePlugin.createFlutterError( + fromCode: errorDetails["code"] as! String, + message: errorDetails["message"] as! String, + optionalDetails: errorDetails[ + "additionalData" + ] as? [AnyHashable: Any], + andOptionalNSError: nil + ) + ) } let result = FLTFirebaseMethodCallResult.create(success: result, andErrorBlock: errorBlock) @@ -114,13 +119,15 @@ public class FirebaseModelDownloaderPlugin: NSObject, FLTFirebasePluginProtocol, } } - func listDownloadedModels(arguments: [String: Any], - result: FLTFirebaseMethodCallResult) { + func listDownloadedModels( + arguments: [String: Any], + result: FLTFirebaseMethodCallResult + ) { let modelDownloader = modelDownloaderFromArguments(arguments: arguments) modelDownloader?.listDownloadedModels { response in switch response { - case let .success(customModel): + case .success(let customModel): let responseList: [[String: Any]] = customModel.map { [ "filePath": $0.path, @@ -130,7 +137,7 @@ public class FirebaseModelDownloaderPlugin: NSObject, FLTFirebasePluginProtocol, ] } result.success(responseList) - case let .failure(error): + case .failure(let error): result.error(nil, nil, nil, error) } } @@ -160,21 +167,23 @@ public class FirebaseModelDownloaderPlugin: NSObject, FLTFirebasePluginProtocol, conditions: modelDownloadConditions ) { response in switch response { - case let .success(customModel): + case .success(let customModel): result.success([ "filePath": customModel.path, "size": customModel.size, "hash": customModel.hash, "name": customModel.name, ]) - case let .failure(error): + case .failure(let error): result.error(nil, nil, nil, error) } } } - func deleteDownloadedModel(arguments: [String: Any], - result: FLTFirebaseMethodCallResult) { + func deleteDownloadedModel( + arguments: [String: Any], + result: FLTFirebaseMethodCallResult + ) { let modelDownloader = modelDownloaderFromArguments(arguments: arguments) let modelName = arguments["modelName"] @@ -182,7 +191,7 @@ public class FirebaseModelDownloaderPlugin: NSObject, FLTFirebasePluginProtocol, switch response { case .success(): result.success(nil) - case let .failure(error): + case .failure(let error): result.error(nil, nil, nil, error) } } diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/firebase_ml_model_downloader.dart b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/firebase_ml_model_downloader.dart index 6fbe5b541f75..6c741e8e81f9 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/firebase_ml_model_downloader.dart +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/firebase_ml_model_downloader.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:firebase_ml_model_downloader_platform_interface/firebase_ml_model_downloader_platform_interface.dart'; import 'package:flutter/foundation.dart'; diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/src/firebase_ml_model_downloader.dart b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/src/firebase_ml_model_downloader.dart index 699f318e53dd..211e751234c4 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/src/firebase_ml_model_downloader.dart +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/lib/src/firebase_ml_model_downloader.dart @@ -4,7 +4,7 @@ part of '../firebase_ml_model_downloader.dart'; -class FirebaseModelDownloader extends FirebasePluginPlatform { +class FirebaseModelDownloader extends FirebasePlugin { FirebaseModelDownloader._({required this.app}) : super(app.name, 'plugins.flutter.io/firebase_ml_model_downloader'); diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/macos/firebase_ml_model_downloader/Package.swift b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/macos/firebase_ml_model_downloader/Package.swift index 5f35240de0e7..864e62e3cbdd 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/macos/firebase_ml_model_downloader/Package.swift +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/macos/firebase_ml_model_downloader/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_ml_model_downloader", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-ml-model-downloader", targets: ["firebase_ml_model_downloader"]), + .library(name: "firebase-ml-model-downloader", targets: ["firebase_ml_model_downloader"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/pubspec.yaml b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/pubspec.yaml index 3bbef56da010..26f00b2fa077 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/pubspec.yaml +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader/pubspec.yaml @@ -1,6 +1,7 @@ name: firebase_ml_model_downloader description: A Flutter plugin allowing you to use Firebase Ml Model Downloader. -version: 0.4.1 +version: 0.4.2+4 +resolution: workspace homepage: https://firebase.google.com/docs/ml/flutter/use-custom-models repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_ml_model_downloader/firebase_ml_model_downloader topics: @@ -13,13 +14,13 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_ml_model_downloader_platform_interface: ^0.1.5+19 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_ml_model_downloader_platform_interface: ^0.1.5+24 flutter: sdk: flutter diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/CHANGELOG.md b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/CHANGELOG.md index 9756d23eb3f6..5965a9ea6644 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/CHANGELOG.md +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.1.5+24 + + - Update a dependency to the latest release. + +## 0.1.5+23 + + - Update a dependency to the latest release. + +## 0.1.5+22 + + - Update a dependency to the latest release. + +## 0.1.5+21 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.1.5+20 + + - Update a dependency to the latest release. + ## 0.1.5+19 - Update a dependency to the latest release. diff --git a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/pubspec.yaml b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/pubspec.yaml index bd5c46b04b70..d1dc2f74d473 100644 --- a/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/pubspec.yaml +++ b/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface/pubspec.yaml @@ -1,22 +1,23 @@ name: firebase_ml_model_downloader_platform_interface description: A common platform interface for the firebase_ml_model_downloader plugin. -version: 0.1.5+19 +version: 0.1.5+24 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_performance/analysis_options.yaml b/packages/firebase_performance/analysis_options.yaml index a9341971bf6a..e8ee91e2edaa 100644 --- a/packages/firebase_performance/analysis_options.yaml +++ b/packages/firebase_performance/analysis_options.yaml @@ -7,4 +7,5 @@ include: ../../analysis_options.yaml analyzer: exclude: - firebase_performance_platform_interface/lib/src/pigeon/messages.pigeon.dart - - firebase_performance_platform_interface/test/pigeon/test_api.dart \ No newline at end of file + - firebase_performance_platform_interface/test/pigeon/test_api.dart + - firebase_performance_platform_interface/pigeons/messages.dart \ No newline at end of file diff --git a/packages/firebase_performance/firebase_performance/CHANGELOG.md b/packages/firebase_performance/firebase_performance/CHANGELOG.md index f086b1346d31..b8507b5bf931 100644 --- a/packages/firebase_performance/firebase_performance/CHANGELOG.md +++ b/packages/firebase_performance/firebase_performance/CHANGELOG.md @@ -1,3 +1,24 @@ +## 0.11.4+3 + + - Update a dependency to the latest release. + +## 0.11.4+2 + + - Update a dependency to the latest release. + +## 0.11.4+1 + + - Update a dependency to the latest release. + +## 0.11.4 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 0.11.3 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 0.11.2 - **FIX**(android): remove kotlin-android since AGP 9 supports it ([#18059](https://github.com/firebase/flutterfire/issues/18059)). ([1e39ad1f](https://github.com/firebase/flutterfire/commit/1e39ad1f146ce23742731ceeb30ff36c440b816f)) diff --git a/packages/firebase_performance/firebase_performance/android/build.gradle b/packages/firebase_performance/firebase_performance/android/build.gradle index 6d38c5620117..5f74989e1448 100644 --- a/packages/firebase_performance/firebase_performance/android/build.gradle +++ b/packages/firebase_performance/firebase_performance/android/build.gradle @@ -4,14 +4,18 @@ version '1.0-SNAPSHOT' apply plugin: 'com.android.library' apply from: file("local-config.gradle") -// AGP 9+ has built-in Kotlin support; older versions need the plugin explicitly. +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int -if (agpMajor < 9) { +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { apply plugin: 'kotlin-android' } buildscript { - ext.kotlin_version = "1.8.22" + ext.kotlin_version = "2.0.0" repositories { google() @@ -60,12 +64,6 @@ android { targetCompatibility project.ext.javaVersion } - if (agpMajor < 9) { - kotlinOptions { - jvmTarget = project.ext.javaVersion - } - } - sourceSets { main.java.srcDirs += "src/main/kotlin" test.java.srcDirs += "src/test/kotlin" @@ -87,4 +85,12 @@ android { } } +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } + } +} + apply from: file("./user-agent.gradle") diff --git a/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebaseAppRegistrar.kt b/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebaseAppRegistrar.kt index c4f81db754bb..e6695bbc968f 100644 --- a/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebaseAppRegistrar.kt +++ b/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebaseAppRegistrar.kt @@ -12,7 +12,6 @@ import com.google.firebase.platforminfo.LibraryVersionComponent class FlutterFirebaseAppRegistrar : ComponentRegistrar { override fun getComponents(): List> { return listOf( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION) - ) + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION)) } } diff --git a/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebasePerformancePlugin.kt b/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebasePerformancePlugin.kt index dfc913d0700e..4353a5fa833f 100644 --- a/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebasePerformancePlugin.kt +++ b/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/FlutterFirebasePerformancePlugin.kt @@ -18,18 +18,14 @@ import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry /** * Flutter plugin accessing Firebase Performance API. * - * * Instantiate this in an add to app scenario to gracefully handle activity and context changes. */ -class FlutterFirebasePerformancePlugin - : FlutterFirebasePlugin, FlutterPlugin, FirebasePerformanceHostApi { +class FlutterFirebasePerformancePlugin : + FlutterFirebasePlugin, FlutterPlugin, FirebasePerformanceHostApi { private var binaryMessenger: BinaryMessenger? = null private fun initInstance(messenger: BinaryMessenger) { - FlutterFirebasePluginRegistry.registerPlugin( - METHOD_CHANNEL_NAME, - this - ) + FlutterFirebasePluginRegistry.registerPlugin(METHOD_CHANNEL_NAME, this) binaryMessenger = messenger FirebasePerformanceHostApi.setUp(messenger, this) } @@ -79,7 +75,11 @@ class FlutterFirebasePerformancePlugin } } - override fun stopTrace(handle: Long, attributes: TraceAttributes, callback: (Result) -> Unit) { + override fun stopTrace( + handle: Long, + attributes: TraceAttributes, + callback: (Result) -> Unit + ) { FlutterFirebasePlugin.cachedThreadPool.execute { try { val trace = _traces[handle.toInt()] @@ -88,13 +88,9 @@ class FlutterFirebasePerformancePlugin return@execute } - attributes.attributes?.forEach { (key, value) -> - trace.putAttribute(key, value) - } + attributes.attributes?.forEach { (key, value) -> trace.putAttribute(key, value) } - attributes.metrics?.forEach { (key, value) -> - trace.putMetric(key, value) - } + attributes.metrics?.forEach { (key, value) -> trace.putMetric(key, value) } trace.stop() _traces.remove(handle.toInt()) @@ -109,10 +105,7 @@ class FlutterFirebasePerformancePlugin FlutterFirebasePlugin.cachedThreadPool.execute { try { val httpMethod = parseHttpMethod(options.httpMethod) - val httpMetric = FirebasePerformance.getInstance().newHttpMetric( - options.url, - httpMethod - ) + val httpMetric = FirebasePerformance.getInstance().newHttpMetric(options.url, httpMethod) httpMetric.start() val httpMetricHandle = _httpMetricHandle++ _httpMetrics[httpMetricHandle] = httpMetric @@ -123,7 +116,11 @@ class FlutterFirebasePerformancePlugin } } - override fun stopHttpMetric(handle: Long, attributes: HttpMetricAttributes, callback: (Result) -> Unit) { + override fun stopHttpMetric( + handle: Long, + attributes: HttpMetricAttributes, + callback: (Result) -> Unit + ) { FlutterFirebasePlugin.cachedThreadPool.execute { try { val httpMetric = _httpMetrics[handle.toInt()] @@ -137,9 +134,7 @@ class FlutterFirebasePerformancePlugin attributes.responseContentType?.let { httpMetric.setResponseContentType(it) } attributes.responsePayloadSize?.let { httpMetric.setResponsePayloadSize(it) } - attributes.attributes?.forEach { (key, value) -> - httpMetric.putAttribute(key, value) - } + attributes.attributes?.forEach { (key, value) -> httpMetric.putAttribute(key, value) } httpMetric.stop() _httpMetrics.remove(handle.toInt()) @@ -150,9 +145,8 @@ class FlutterFirebasePerformancePlugin } } - private fun handleFailure (callback: (Result) -> Unit, exception: Exception?) { - val message = - if (exception != null) exception.message else "An unknown error occurred" + private fun handleFailure(callback: (Result) -> Unit, exception: Exception?) { + val message = if (exception != null) exception.message else "An unknown error occurred" callback(Result.failure(FlutterError("firebase_performance", message, null))) } diff --git a/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/GeneratedAndroidFirebasePerformance.g.kt b/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/GeneratedAndroidFirebasePerformance.g.kt index 39423870b952..ce9d86e85190 100644 --- a/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/GeneratedAndroidFirebasePerformance.g.kt +++ b/packages/firebase_performance/firebase_performance/android/src/main/kotlin/io/flutter/plugins/firebase/performance/GeneratedAndroidFirebasePerformance.g.kt @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -10,12 +10,11 @@ package io.flutter.plugins.firebase.performance import android.util.Log import io.flutter.plugin.common.BasicMessageChannel import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec import io.flutter.plugin.common.StandardMessageCodec import java.io.ByteArrayOutputStream import java.nio.ByteBuffer + private object GeneratedAndroidFirebasePerformancePigeonUtils { fun wrapResult(result: Any?): List { @@ -24,62 +23,172 @@ private object GeneratedAndroidFirebasePerformancePigeonUtils { fun wrapError(exception: Throwable): List { return if (exception is FlutterError) { - listOf( - exception.code, - exception.message, - exception.details - ) + listOf(exception.code, exception.message, exception.details) } else { listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) } } + + fun doubleEquals(a: Double, b: Double): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN()) + } + + fun floatEquals(a: Float, b: Float): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN()) + } + + fun doubleHash(d: Double): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (d == 0.0) 0.0 else d + val bits = java.lang.Double.doubleToLongBits(normalized) + return (bits xor (bits ushr 32)).toInt() + } + + fun floatHash(f: Float): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (f == 0.0f) 0.0f else f + return java.lang.Float.floatToIntBits(normalized) + } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a === b) { + return true + } + if (a == null || b == null) { + return false + } if (a is ByteArray && b is ByteArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is IntArray && b is IntArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is LongArray && b is LongArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is DoubleArray && b is DoubleArray) { - return a.contentEquals(b) + if (a.size != b.size) return false + for (i in a.indices) { + if (!doubleEquals(a[i], b[i])) return false + } + return true + } + if (a is FloatArray && b is FloatArray) { + if (a.size != b.size) return false + for (i in a.indices) { + if (!floatEquals(a[i], b[i])) return false + } + return true } if (a is Array<*> && b is Array<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + for (i in a.indices) { + if (!deepEquals(a[i], b[i])) return false + } + return true } if (a is List<*> && b is List<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + val iterA = a.iterator() + val iterB = b.iterator() + while (iterA.hasNext() && iterB.hasNext()) { + if (!deepEquals(iterA.next(), iterB.next())) return false + } + return true } if (a is Map<*, *> && b is Map<*, *>) { - return a.size == b.size && a.all { - (b as Map).containsKey(it.key) && - deepEquals(it.value, b[it.key]) + if (a.size != b.size) return false + for (entry in a) { + val key = entry.key + var found = false + for (bEntry in b) { + if (deepEquals(key, bEntry.key)) { + if (deepEquals(entry.value, bEntry.value)) { + found = true + break + } else { + return false + } + } + } + if (!found) return false } + return true + } + if (a is Double && b is Double) { + return doubleEquals(a, b) + } + if (a is Float && b is Float) { + return floatEquals(a, b) } return a == b } - + + fun deepHash(value: Any?): Int { + return when (value) { + null -> 0 + is ByteArray -> value.contentHashCode() + is IntArray -> value.contentHashCode() + is LongArray -> value.contentHashCode() + is DoubleArray -> { + var result = 1 + for (item in value) { + result = 31 * result + doubleHash(item) + } + result + } + is FloatArray -> { + var result = 1 + for (item in value) { + result = 31 * result + floatHash(item) + } + result + } + is Array<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is List<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is Map<*, *> -> { + var result = 0 + for (entry in value) { + result += ((deepHash(entry.key) * 31) xor deepHash(entry.value)) + } + result + } + is Double -> doubleHash(value) + is Float -> floatHash(value) + else -> value.hashCode() + } + } } /** * 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 FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() enum class HttpMethod(val raw: Int) { CONNECT(0), @@ -100,11 +209,7 @@ enum class HttpMethod(val raw: Int) { } /** Generated class from Pigeon that represents data sent in messages. */ -data class HttpMetricOptions ( - val url: String, - val httpMethod: HttpMethod -) - { +data class HttpMetricOptions(val url: String, val httpMethod: HttpMethod) { companion object { fun fromList(pigeonVar_list: List): HttpMetricOptions { val url = pigeonVar_list[0] as String @@ -112,33 +217,42 @@ data class HttpMetricOptions ( return HttpMetricOptions(url, httpMethod) } } + fun toList(): List { return listOf( - url, - httpMethod, + url, + httpMethod, ) } + override fun equals(other: Any?): Boolean { - if (other !is HttpMetricOptions) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as HttpMetricOptions + return GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(this.url, other.url) && + GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(this.httpMethod, other.httpMethod) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.url) + result = 31 * result + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.httpMethod) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class HttpMetricAttributes ( - val httpResponseCode: Long? = null, - val requestPayloadSize: Long? = null, - val responsePayloadSize: Long? = null, - val responseContentType: String? = null, - val attributes: Map? = null -) - { +data class HttpMetricAttributes( + val httpResponseCode: Long? = null, + val requestPayloadSize: Long? = null, + val responsePayloadSize: Long? = null, + val responseContentType: String? = null, + val attributes: Map? = null +) { companion object { fun fromList(pigeonVar_list: List): HttpMetricAttributes { val httpResponseCode = pigeonVar_list[0] as Long? @@ -146,36 +260,67 @@ data class HttpMetricAttributes ( val responsePayloadSize = pigeonVar_list[2] as Long? val responseContentType = pigeonVar_list[3] as String? val attributes = pigeonVar_list[4] as Map? - return HttpMetricAttributes(httpResponseCode, requestPayloadSize, responsePayloadSize, responseContentType, attributes) + return HttpMetricAttributes( + httpResponseCode, + requestPayloadSize, + responsePayloadSize, + responseContentType, + attributes) } } + fun toList(): List { return listOf( - httpResponseCode, - requestPayloadSize, - responsePayloadSize, - responseContentType, - attributes, + httpResponseCode, + requestPayloadSize, + responsePayloadSize, + responseContentType, + attributes, ) } + override fun equals(other: Any?): Boolean { - if (other !is HttpMetricAttributes) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as HttpMetricAttributes + return GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals( + this.httpResponseCode, other.httpResponseCode) && + GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals( + this.requestPayloadSize, other.requestPayloadSize) && + GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals( + this.responsePayloadSize, other.responsePayloadSize) && + GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals( + this.responseContentType, other.responseContentType) && + GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(this.attributes, other.attributes) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = + 31 * result + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.httpResponseCode) + result = + 31 * result + + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.requestPayloadSize) + result = + 31 * result + + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.responsePayloadSize) + result = + 31 * result + + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.responseContentType) + result = 31 * result + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.attributes) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class TraceAttributes ( - val metrics: Map? = null, - val attributes: Map? = null -) - { +data class TraceAttributes( + val metrics: Map? = null, + val attributes: Map? = null +) { companion object { fun fromList(pigeonVar_list: List): TraceAttributes { val metrics = pigeonVar_list[0] as Map? @@ -183,54 +328,58 @@ data class TraceAttributes ( return TraceAttributes(metrics, attributes) } } + fun toList(): List { return listOf( - metrics, - attributes, + metrics, + attributes, ) } + override fun equals(other: Any?): Boolean { - if (other !is TraceAttributes) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as TraceAttributes + return GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(this.metrics, other.metrics) && + GeneratedAndroidFirebasePerformancePigeonUtils.deepEquals(this.attributes, other.attributes) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.metrics) + result = 31 * result + GeneratedAndroidFirebasePerformancePigeonUtils.deepHash(this.attributes) + return result + } } + private open class GeneratedAndroidFirebasePerformancePigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 129.toByte() -> { - return (readValue(buffer) as Long?)?.let { - HttpMethod.ofRaw(it.toInt()) - } + return (readValue(buffer) as Long?)?.let { HttpMethod.ofRaw(it.toInt()) } } 130.toByte() -> { - return (readValue(buffer) as? List)?.let { - HttpMetricOptions.fromList(it) - } + return (readValue(buffer) as? List)?.let { HttpMetricOptions.fromList(it) } } 131.toByte() -> { - return (readValue(buffer) as? List)?.let { - HttpMetricAttributes.fromList(it) - } + return (readValue(buffer) as? List)?.let { HttpMetricAttributes.fromList(it) } } 132.toByte() -> { - return (readValue(buffer) as? List)?.let { - TraceAttributes.fromList(it) - } + return (readValue(buffer) as? List)?.let { TraceAttributes.fromList(it) } } else -> super.readValueOfType(type, buffer) } } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { is HttpMethod -> { stream.write(129) - writeValue(stream, value.raw) + writeValue(stream, value.raw.toLong()) } is HttpMetricOptions -> { stream.write(130) @@ -249,27 +398,45 @@ private open class GeneratedAndroidFirebasePerformancePigeonCodec : StandardMess } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface FirebasePerformanceHostApi { fun setPerformanceCollectionEnabled(enabled: Boolean, callback: (Result) -> Unit) + fun isPerformanceCollectionEnabled(callback: (Result) -> Unit) + fun startTrace(name: String, callback: (Result) -> Unit) + fun stopTrace(handle: Long, attributes: TraceAttributes, callback: (Result) -> Unit) + fun startHttpMetric(options: HttpMetricOptions, callback: (Result) -> Unit) - fun stopHttpMetric(handle: Long, attributes: HttpMetricAttributes, callback: (Result) -> Unit) + + fun stopHttpMetric( + handle: Long, + attributes: HttpMetricAttributes, + callback: (Result) -> Unit + ) companion object { /** The codec used by FirebasePerformanceHostApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidFirebasePerformancePigeonCodec() - } - /** Sets up an instance of `FirebasePerformanceHostApi` to handle messages through the `binaryMessenger`. */ + val codec: MessageCodec by lazy { GeneratedAndroidFirebasePerformancePigeonCodec() } + /** + * Sets up an instance of `FirebasePerformanceHostApi` to handle messages through the + * `binaryMessenger`. + */ @JvmOverloads - fun setUp(binaryMessenger: BinaryMessenger, api: FirebasePerformanceHostApi?, messageChannelSuffix: String = "") { - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebasePerformanceHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -288,10 +455,14 @@ interface FirebasePerformanceHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.isPerformanceCollectionEnabled$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.isPerformanceCollectionEnabled$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.isPerformanceCollectionEnabled{ result: Result -> + api.isPerformanceCollectionEnabled { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebasePerformancePigeonUtils.wrapError(error)) @@ -306,7 +477,11 @@ interface FirebasePerformanceHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -326,7 +501,11 @@ interface FirebasePerformanceHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -346,7 +525,11 @@ interface FirebasePerformanceHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -366,7 +549,11 @@ interface FirebasePerformanceHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List diff --git a/packages/firebase_performance/firebase_performance/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt b/packages/firebase_performance/firebase_performance/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt index 57b5fca33169..88c6950b89c7 100644 --- a/packages/firebase_performance/firebase_performance/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt +++ b/packages/firebase_performance/firebase_performance/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/tests/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.tests import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_performance/firebase_performance/example/pubspec.yaml b/packages/firebase_performance/firebase_performance/example/pubspec.yaml index 10805d183ef7..a461975acb21 100644 --- a/packages/firebase_performance/firebase_performance/example/pubspec.yaml +++ b/packages/firebase_performance/firebase_performance/example/pubspec.yaml @@ -1,14 +1,16 @@ name: firebase_performance_example description: Demonstrates how to use the firebase_performance plugin. version: 0.0.1 +resolution: workspace publish_to: none environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_performance: ^0.11.2 + firebase_core: ^4.11.0 + firebase_performance: ^0.11.4+3 flutter: sdk: flutter http: ^1.0.0 diff --git a/packages/firebase_performance/firebase_performance/ios/firebase_performance/Package.swift b/packages/firebase_performance/firebase_performance/ios/firebase_performance/Package.swift index 8ca91978ac83..0c98e15d060a 100644 --- a/packages/firebase_performance/firebase_performance/ios/firebase_performance/Package.swift +++ b/packages/firebase_performance/firebase_performance/ios/firebase_performance/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "0.11.1-5" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "0.11.4-3" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_performance", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-performance", targets: ["firebase_performance"]), + .library(name: "firebase-performance", targets: ["firebase_performance"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-perf\""), ] - ), + ) ] ) diff --git a/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformanceMessages.g.swift b/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformanceMessages.g.swift index 2ff69619ed50..d0bf9252d08f 100644 --- a/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformanceMessages.g.swift +++ b/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformanceMessages.g.swift @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -52,7 +52,7 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } @@ -66,6 +66,19 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +private func doubleEqualsFirebasePerformanceMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirebasePerformanceMessages(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8_0000_0000_0000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } +} + func deepEqualsFirebasePerformanceMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? @@ -76,59 +89,90 @@ func deepEqualsFirebasePerformanceMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { case (nil, _), (_, nil): return false - case is (Void, Void): + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: return true - case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): - return cleanLhsHashable == cleanRhsHashable + case is (Void, Void): + return true - case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): - guard cleanLhsArray.count == cleanRhsArray.count else { return false } - for (index, element) in cleanLhsArray.enumerated() { - if !deepEqualsFirebasePerformanceMessages(element, cleanRhsArray[index]) { + case let (lhsArray, rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsFirebasePerformanceMessages(element, rhsArray[index]) { return false } } return true - case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): - guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } - for (key, cleanLhsValue) in cleanLhsDictionary { - guard cleanRhsDictionary.index(forKey: key) != nil else { return false } - if !deepEqualsFirebasePerformanceMessages(cleanLhsValue, cleanRhsDictionary[key]!) { + case let (lhsArray, rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsFirebasePerformanceMessages(element, rhsArray[index]) { return false } } return true + case let (lhsDictionary, rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEqualsFirebasePerformanceMessages(lhsKey, rhsKey) { + if deepEqualsFirebasePerformanceMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirebasePerformanceMessages(lhs, rhs) + + case let (lhsHashable, rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + default: - // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be - // untrue. return false } } func deepHashFirebasePerformanceMessages(value: Any?, hasher: inout Hasher) { - if let valueList = value as? [AnyHashable] { - for item in valueList { - deepHashFirebasePerformanceMessages(value: item, hasher: &hasher) - } - return - } - - if let valueDict = value as? [AnyHashable: AnyHashable] { - for key in valueDict.keys { - hasher.combine(key) - deepHashFirebasePerformanceMessages(value: valueDict[key]!, hasher: &hasher) + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirebasePerformanceMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirebasePerformanceMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirebasePerformanceMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirebasePerformanceMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirebasePerformanceMessages(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) } - return - } - - if let hashableValue = value as? AnyHashable { - hasher.combine(hashableValue.hashValue) + } else { + hasher.combine(0) } - - return hasher.combine(String(describing: value)) } enum HttpMethod: Int { @@ -167,11 +211,20 @@ struct HttpMetricOptions: Hashable { } static func == (lhs: HttpMetricOptions, rhs: HttpMetricOptions) -> Bool { - deepEqualsFirebasePerformanceMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebasePerformanceMessages(lhs.url, rhs.url) + && deepEqualsFirebasePerformanceMessages( + lhs.httpMethod, + rhs.httpMethod + ) } func hash(into hasher: inout Hasher) { - deepHashFirebasePerformanceMessages(value: toList(), hasher: &hasher) + hasher.combine("HttpMetricOptions") + deepHashFirebasePerformanceMessages(value: url, hasher: &hasher) + deepHashFirebasePerformanceMessages(value: httpMethod, hasher: &hasher) } } @@ -211,11 +264,31 @@ struct HttpMetricAttributes: Hashable { } static func == (lhs: HttpMetricAttributes, rhs: HttpMetricAttributes) -> Bool { - deepEqualsFirebasePerformanceMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebasePerformanceMessages(lhs.httpResponseCode, rhs.httpResponseCode) + && deepEqualsFirebasePerformanceMessages( + lhs.requestPayloadSize, + rhs.requestPayloadSize + ) + && deepEqualsFirebasePerformanceMessages( + lhs.responsePayloadSize, + rhs.responsePayloadSize + ) + && deepEqualsFirebasePerformanceMessages( + lhs.responseContentType, + rhs.responseContentType + ) && deepEqualsFirebasePerformanceMessages(lhs.attributes, rhs.attributes) } func hash(into hasher: inout Hasher) { - deepHashFirebasePerformanceMessages(value: toList(), hasher: &hasher) + hasher.combine("HttpMetricAttributes") + deepHashFirebasePerformanceMessages(value: httpResponseCode, hasher: &hasher) + deepHashFirebasePerformanceMessages(value: requestPayloadSize, hasher: &hasher) + deepHashFirebasePerformanceMessages(value: responsePayloadSize, hasher: &hasher) + deepHashFirebasePerformanceMessages(value: responseContentType, hasher: &hasher) + deepHashFirebasePerformanceMessages(value: attributes, hasher: &hasher) } } @@ -243,11 +316,20 @@ struct TraceAttributes: Hashable { } static func == (lhs: TraceAttributes, rhs: TraceAttributes) -> Bool { - deepEqualsFirebasePerformanceMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebasePerformanceMessages(lhs.metrics, rhs.metrics) + && deepEqualsFirebasePerformanceMessages( + lhs.attributes, + rhs.attributes + ) } func hash(into hasher: inout Hasher) { - deepHashFirebasePerformanceMessages(value: toList(), hasher: &hasher) + hasher.combine("TraceAttributes") + deepHashFirebasePerformanceMessages(value: metrics, hasher: &hasher) + deepHashFirebasePerformanceMessages(value: attributes, hasher: &hasher) } } @@ -311,16 +393,20 @@ class FirebasePerformanceMessagesPigeonCodec: FlutterStandardMessageCodec, @unch /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol FirebasePerformanceHostApi { - func setPerformanceCollectionEnabled(enabled: Bool, - completion: @escaping (Result) -> Void) + func setPerformanceCollectionEnabled( + enabled: Bool, + completion: @escaping (Result) -> Void) func isPerformanceCollectionEnabled(completion: @escaping (Result) -> Void) func startTrace(name: String, completion: @escaping (Result) -> Void) - func stopTrace(handle: Int64, attributes: TraceAttributes, - completion: @escaping (Result) -> Void) - func startHttpMetric(options: HttpMetricOptions, - completion: @escaping (Result) -> Void) - func stopHttpMetric(handle: Int64, attributes: HttpMetricAttributes, - completion: @escaping (Result) -> Void) + func stopTrace( + handle: Int64, attributes: TraceAttributes, + completion: @escaping (Result) -> Void) + func startHttpMetric( + options: HttpMetricOptions, + completion: @escaping (Result) -> Void) + func stopHttpMetric( + handle: Int64, attributes: HttpMetricAttributes, + completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -331,11 +417,14 @@ class FirebasePerformanceHostApiSetup { /// Sets up an instance of `FirebasePerformanceHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebasePerformanceHostApi?, - messageChannelSuffix: String = "") { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebasePerformanceHostApi?, + messageChannelSuffix: String = "" + ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let setPerformanceCollectionEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -347,7 +436,7 @@ class FirebasePerformanceHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -356,7 +445,8 @@ class FirebasePerformanceHostApiSetup { setPerformanceCollectionEnabledChannel.setMessageHandler(nil) } let isPerformanceCollectionEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.isPerformanceCollectionEnabled\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.isPerformanceCollectionEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -364,9 +454,9 @@ class FirebasePerformanceHostApiSetup { isPerformanceCollectionEnabledChannel.setMessageHandler { _, reply in api.isPerformanceCollectionEnabled { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -375,7 +465,8 @@ class FirebasePerformanceHostApiSetup { isPerformanceCollectionEnabledChannel.setMessageHandler(nil) } let startTraceChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -385,9 +476,9 @@ class FirebasePerformanceHostApiSetup { let nameArg = args[0] as! String api.startTrace(name: nameArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -396,7 +487,8 @@ class FirebasePerformanceHostApiSetup { startTraceChannel.setMessageHandler(nil) } let stopTraceChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -409,7 +501,7 @@ class FirebasePerformanceHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -418,7 +510,8 @@ class FirebasePerformanceHostApiSetup { stopTraceChannel.setMessageHandler(nil) } let startHttpMetricChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -428,9 +521,9 @@ class FirebasePerformanceHostApiSetup { let optionsArg = args[0] as! HttpMetricOptions api.startHttpMetric(options: optionsArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -439,7 +532,8 @@ class FirebasePerformanceHostApiSetup { startHttpMetricChannel.setMessageHandler(nil) } let stopHttpMetricChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -452,7 +546,7 @@ class FirebasePerformanceHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } diff --git a/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformancePlugin.swift b/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformancePlugin.swift index 6d0ac5c39979..3c7d8f50b620 100644 --- a/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformancePlugin.swift +++ b/packages/firebase_performance/firebase_performance/ios/firebase_performance/Sources/firebase_performance/FirebasePerformancePlugin.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebasePerformance + #if canImport(FlutterMacOS) import FlutterMacOS #else @@ -13,14 +15,15 @@ #else import firebase_core_shared #endif -import FirebasePerformance +// swift-format-ignore: AlwaysUseLowerCamelCase let FirebasePerformanceChannelName = "plugins.flutter.io/firebase_performance" extension FlutterError: Error {} public class FirebasePerformancePlugin: NSObject, FlutterPlugin, FLTFirebasePluginProtocol, - FirebasePerformanceHostApi { + FirebasePerformanceHostApi +{ public func didReinitializeFirebaseCore(_ completion: @escaping () -> Void) { completion() } @@ -58,8 +61,10 @@ public class FirebasePerformancePlugin: NSObject, FlutterPlugin, FLTFirebasePlug FirebasePerformanceHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance) } - public func setPerformanceCollectionEnabled(enabled: Bool, - completion: @escaping (Result) -> Void) { + public func setPerformanceCollectionEnabled( + enabled: Bool, + completion: @escaping (Result) -> Void + ) { Performance.sharedInstance().isDataCollectionEnabled = enabled completion(.success(())) } @@ -77,8 +82,10 @@ public class FirebasePerformancePlugin: NSObject, FlutterPlugin, FLTFirebasePlug completion(.success(Int64(traceHandle))) } - func stopTrace(handle: Int64, attributes: TraceAttributes, - completion: @escaping (Result) -> Void) { + func stopTrace( + handle: Int64, attributes: TraceAttributes, + completion: @escaping (Result) -> Void + ) { guard let trace = traces[Int(handle)] else { completion(.success(())) return @@ -101,19 +108,25 @@ public class FirebasePerformancePlugin: NSObject, FlutterPlugin, FLTFirebasePlug completion(.success(())) } - func startHttpMetric(options: HttpMetricOptions, - completion: @escaping (Result) -> Void) { + func startHttpMetric( + options: HttpMetricOptions, + completion: @escaping (Result) -> Void + ) { guard let url = URL(string: options.url) else { completion(.failure(FlutterError(code: "invalid-url", message: "Invalid url", details: nil))) return } guard let httpMethod = parseHttpMethod(options.httpMethod) else { - completion(.failure(FlutterError( - code: "invalid-argument", - message: "Invalid httpMethod", - details: nil - ))) + completion( + .failure( + FlutterError( + code: "invalid-argument", + message: "Invalid httpMethod", + details: nil + ) + ) + ) return } @@ -124,8 +137,10 @@ public class FirebasePerformancePlugin: NSObject, FlutterPlugin, FLTFirebasePlug completion(.success(Int64(httpMetricHandle))) } - func stopHttpMetric(handle: Int64, attributes: HttpMetricAttributes, - completion: @escaping (Result) -> Void) { + func stopHttpMetric( + handle: Int64, attributes: HttpMetricAttributes, + completion: @escaping (Result) -> Void + ) { guard let httpMetric = httpMetrics[Int(handle)] else { completion(.success(())) return diff --git a/packages/firebase_performance/firebase_performance/lib/src/firebase_performance.dart b/packages/firebase_performance/firebase_performance/lib/src/firebase_performance.dart index fb044c723939..5061983318c5 100644 --- a/packages/firebase_performance/firebase_performance/lib/src/firebase_performance.dart +++ b/packages/firebase_performance/firebase_performance/lib/src/firebase_performance.dart @@ -7,7 +7,7 @@ part of '../firebase_performance.dart'; /// The Firebase Performance API. /// /// You can get an instance by calling [FirebasePerformance.instance]. -class FirebasePerformance extends FirebasePluginPlatform { +class FirebasePerformance extends FirebasePlugin { FirebasePerformance._({required this.app}) : super(app.name, 'plugins.flutter.io/firebase_performance'); diff --git a/packages/firebase_performance/firebase_performance/pubspec.yaml b/packages/firebase_performance/firebase_performance/pubspec.yaml index 39a1de29b9d9..64487a310848 100644 --- a/packages/firebase_performance/firebase_performance/pubspec.yaml +++ b/packages/firebase_performance/firebase_performance/pubspec.yaml @@ -5,7 +5,8 @@ description: iOS. homepage: https://firebase.google.com/docs/perf-mon repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_performance/firebase_performance -version: 0.11.2 +version: 0.11.4+3 +resolution: workspace topics: - firebase - performance @@ -16,14 +17,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_performance_platform_interface: ^0.1.6+6 - firebase_performance_web: ^0.1.8+4 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_performance_platform_interface: ^0.2.0+3 + firebase_performance_web: ^0.1.8+9 flutter: sdk: flutter diff --git a/packages/firebase_performance/firebase_performance/windows/messages.g.cpp b/packages/firebase_performance/firebase_performance/windows/messages.g.cpp index f5934afda061..4efec43167f6 100644 --- a/packages/firebase_performance/firebase_performance/windows/messages.g.cpp +++ b/packages/firebase_performance/firebase_performance/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace firebase_performance_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,6 +33,212 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace // HttpMetricOptions HttpMetricOptions::HttpMetricOptions(const std::string& url, @@ -67,6 +275,24 @@ HttpMetricOptions HttpMetricOptions::FromEncodableList( return decoded; } +bool HttpMetricOptions::operator==(const HttpMetricOptions& other) const { + return PigeonInternalDeepEquals(url_, other.url_) && + PigeonInternalDeepEquals(http_method_, other.http_method_); +} + +bool HttpMetricOptions::operator!=(const HttpMetricOptions& other) const { + return !(*this == other); +} + +size_t HttpMetricOptions::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(url_); + result = result * 31 + PigeonInternalDeepHash(http_method_); + return result; +} + +size_t PigeonInternalDeepHash(const HttpMetricOptions& v) { return v.Hash(); } + // HttpMetricAttributes HttpMetricAttributes::HttpMetricAttributes() {} @@ -204,6 +430,36 @@ HttpMetricAttributes HttpMetricAttributes::FromEncodableList( return decoded; } +bool HttpMetricAttributes::operator==(const HttpMetricAttributes& other) const { + return PigeonInternalDeepEquals(http_response_code_, + other.http_response_code_) && + PigeonInternalDeepEquals(request_payload_size_, + other.request_payload_size_) && + PigeonInternalDeepEquals(response_payload_size_, + other.response_payload_size_) && + PigeonInternalDeepEquals(response_content_type_, + other.response_content_type_) && + PigeonInternalDeepEquals(attributes_, other.attributes_); +} + +bool HttpMetricAttributes::operator!=(const HttpMetricAttributes& other) const { + return !(*this == other); +} + +size_t HttpMetricAttributes::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(http_response_code_); + result = result * 31 + PigeonInternalDeepHash(request_payload_size_); + result = result * 31 + PigeonInternalDeepHash(response_payload_size_); + result = result * 31 + PigeonInternalDeepHash(response_content_type_); + result = result * 31 + PigeonInternalDeepHash(attributes_); + return result; +} + +size_t PigeonInternalDeepHash(const HttpMetricAttributes& v) { + return v.Hash(); +} + // TraceAttributes TraceAttributes::TraceAttributes() {} @@ -260,10 +516,28 @@ TraceAttributes TraceAttributes::FromEncodableList(const EncodableList& list) { return decoded; } +bool TraceAttributes::operator==(const TraceAttributes& other) const { + return PigeonInternalDeepEquals(metrics_, other.metrics_) && + PigeonInternalDeepEquals(attributes_, other.attributes_); +} + +bool TraceAttributes::operator!=(const TraceAttributes& other) const { + return !(*this == other); +} + +size_t TraceAttributes::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(metrics_); + result = result * 31 + PigeonInternalDeepHash(attributes_); + return result; +} + +size_t PigeonInternalDeepHash(const TraceAttributes& v) { return v.Hash(); } + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { case 129: { const auto& encodable_enum_arg = ReadValue(stream); @@ -287,12 +561,12 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( std::get(ReadValue(stream)))); } default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { if (custom_value->type() == typeid(HttpMethod)) { @@ -326,25 +600,26 @@ void PigeonInternalCodecSerializer::WriteValue( return; } } - flutter::StandardCodecSerializer::WriteValue(value, stream); + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebasePerformanceHostApi. -const flutter::StandardMessageCodec& FirebasePerformanceHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebasePerformanceHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebasePerformanceHostApi` to handle messages // through the `binary_messenger`. void FirebasePerformanceHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, + ::flutter::BinaryMessenger* binary_messenger, FirebasePerformanceHostApi* api) { FirebasePerformanceHostApi::SetUp(binary_messenger, api, ""); } void FirebasePerformanceHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, FirebasePerformanceHostApi* api, + ::flutter::BinaryMessenger* binary_messenger, + FirebasePerformanceHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = message_channel_suffix.length() > 0 @@ -360,7 +635,7 @@ void FirebasePerformanceHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_enabled_arg = args.at(0); @@ -397,7 +672,7 @@ void FirebasePerformanceHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { api->IsPerformanceCollectionEnabled( [reply](ErrorOr&& output) { @@ -428,7 +703,7 @@ void FirebasePerformanceHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_name_arg = args.at(0); @@ -465,7 +740,7 @@ void FirebasePerformanceHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_handle_arg = args.at(0); @@ -510,7 +785,7 @@ void FirebasePerformanceHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_options_arg = args.at(0); @@ -549,7 +824,7 @@ void FirebasePerformanceHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_handle_arg = args.at(0); diff --git a/packages/firebase_performance/firebase_performance/windows/messages.g.h b/packages/firebase_performance/firebase_performance/windows/messages.g.h index a9802f4c703a..cf8d9bfd9ebb 100644 --- a/packages/firebase_performance/firebase_performance/windows/messages.g.h +++ b/packages/firebase_performance/firebase_performance/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -83,10 +83,22 @@ class HttpMetricOptions { const HttpMethod& http_method() const; void set_http_method(const HttpMethod& value_arg); + bool operator==(const HttpMetricOptions& other) const; + bool operator!=(const HttpMetricOptions& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static HttpMetricOptions FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebasePerformanceHostApi; friend class PigeonInternalCodecSerializer; std::string url_; @@ -104,7 +116,7 @@ class HttpMetricAttributes { const int64_t* request_payload_size, const int64_t* response_payload_size, const std::string* response_content_type, - const flutter::EncodableMap* attributes); + const ::flutter::EncodableMap* attributes); const int64_t* http_response_code() const; void set_http_response_code(const int64_t* value_arg); @@ -122,21 +134,33 @@ class HttpMetricAttributes { void set_response_content_type(const std::string_view* value_arg); void set_response_content_type(std::string_view value_arg); - const flutter::EncodableMap* attributes() const; - void set_attributes(const flutter::EncodableMap* value_arg); - void set_attributes(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* attributes() const; + void set_attributes(const ::flutter::EncodableMap* value_arg); + void set_attributes(const ::flutter::EncodableMap& value_arg); + + bool operator==(const HttpMetricAttributes& other) const; + bool operator!=(const HttpMetricAttributes& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; private: static HttpMetricAttributes FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebasePerformanceHostApi; friend class PigeonInternalCodecSerializer; std::optional http_response_code_; std::optional request_payload_size_; std::optional response_payload_size_; std::optional response_content_type_; - std::optional attributes_; + std::optional<::flutter::EncodableMap> attributes_; }; // Generated class from Pigeon that represents data sent in messages. @@ -146,27 +170,41 @@ class TraceAttributes { TraceAttributes(); // Constructs an object setting all fields. - explicit TraceAttributes(const flutter::EncodableMap* metrics, - const flutter::EncodableMap* attributes); + explicit TraceAttributes(const ::flutter::EncodableMap* metrics, + const ::flutter::EncodableMap* attributes); + + const ::flutter::EncodableMap* metrics() const; + void set_metrics(const ::flutter::EncodableMap* value_arg); + void set_metrics(const ::flutter::EncodableMap& value_arg); + + const ::flutter::EncodableMap* attributes() const; + void set_attributes(const ::flutter::EncodableMap* value_arg); + void set_attributes(const ::flutter::EncodableMap& value_arg); - const flutter::EncodableMap* metrics() const; - void set_metrics(const flutter::EncodableMap* value_arg); - void set_metrics(const flutter::EncodableMap& value_arg); + bool operator==(const TraceAttributes& other) const; + bool operator!=(const TraceAttributes& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; - const flutter::EncodableMap* attributes() const; - void set_attributes(const flutter::EncodableMap* value_arg); - void set_attributes(const flutter::EncodableMap& value_arg); + private: + static TraceAttributes FromEncodableList( + const ::flutter::EncodableList& list); + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static TraceAttributes FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebasePerformanceHostApi; friend class PigeonInternalCodecSerializer; - std::optional metrics_; - std::optional attributes_; + std::optional<::flutter::EncodableMap> metrics_; + std::optional<::flutter::EncodableMap> attributes_; }; -class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -174,12 +212,12 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -209,16 +247,16 @@ class FirebasePerformanceHostApi { std::function reply)> result) = 0; // The codec used by FirebasePerformanceHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebasePerformanceHostApi` to handle messages // through the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebasePerformanceHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebasePerformanceHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebasePerformanceHostApi() = default; diff --git a/packages/firebase_performance/firebase_performance_platform_interface/CHANGELOG.md b/packages/firebase_performance/firebase_performance_platform_interface/CHANGELOG.md index e24542e89480..4108e365daca 100644 --- a/packages/firebase_performance/firebase_performance_platform_interface/CHANGELOG.md +++ b/packages/firebase_performance/firebase_performance_platform_interface/CHANGELOG.md @@ -1,3 +1,26 @@ +## 0.2.0+3 + + - Update a dependency to the latest release. + +## 0.2.0+2 + + - Update a dependency to the latest release. + +## 0.2.0+1 + + - Update a dependency to the latest release. + +## 0.2.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 0.1.6+7 + + - Update a dependency to the latest release. + ## 0.1.6+6 - **REFACTOR**: fix formatting and analysis issues across the repo ([#18124](https://github.com/firebase/flutterfire/issues/18124)). ([ab79fd93](https://github.com/firebase/flutterfire/commit/ab79fd93ee4ccfeb478687623134b1cf8ab71c74)) diff --git a/packages/firebase_performance/firebase_performance_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_performance/firebase_performance_platform_interface/lib/src/pigeon/messages.pigeon.dart index b01f643749c4..df055af51a47 100644 --- a/packages/firebase_performance/firebase_performance_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_performance/firebase_performance_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,21 +1,40 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.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, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -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".', - ); +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } List wrapResponse( @@ -30,20 +49,67 @@ List wrapResponse( } bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } if (a is List && b is List) { return a.length == b.length && a.indexed .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; } return a == b; } +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + enum HttpMethod { connect, delete, @@ -94,12 +160,13 @@ class HttpMetricOptions { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(url, other.url) && + _deepEquals(httpMethod, other.httpMethod); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class HttpMetricAttributes { @@ -155,12 +222,16 @@ class HttpMetricAttributes { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(httpResponseCode, other.httpResponseCode) && + _deepEquals(requestPayloadSize, other.requestPayloadSize) && + _deepEquals(responsePayloadSize, other.responsePayloadSize) && + _deepEquals(responseContentType, other.responseContentType) && + _deepEquals(attributes, other.attributes); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class TraceAttributes { @@ -201,12 +272,13 @@ class TraceAttributes { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(metrics, other.metrics) && + _deepEquals(attributes, other.attributes); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class _PigeonCodec extends StandardMessageCodec { @@ -237,7 +309,7 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - final int? value = readValue(buffer) as int?; + final value = readValue(buffer) as int?; return value == null ? null : HttpMethod.values[value]; case 130: return HttpMetricOptions.decode(readValue(buffer)!); @@ -267,173 +339,119 @@ class FirebasePerformanceHostApi { final String pigeonVar_messageChannelSuffix; Future setPerformanceCollectionEnabled(bool enabled) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([enabled]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future isPerformanceCollectionEnabled() async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.isPerformanceCollectionEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as bool; } Future startTrace(String name) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([name]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as int; } Future stopTrace(int handle, TraceAttributes attributes) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([handle, attributes]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future startHttpMetric(HttpMetricOptions options) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([options]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as int; } Future stopHttpMetric( int handle, HttpMetricAttributes attributes) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([handle, attributes]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } } diff --git a/packages/firebase_performance/firebase_performance_platform_interface/pubspec.yaml b/packages/firebase_performance/firebase_performance_platform_interface/pubspec.yaml index bd67b00c8b1f..a2bd31242d8a 100644 --- a/packages/firebase_performance/firebase_performance_platform_interface/pubspec.yaml +++ b/packages/firebase_performance/firebase_performance_platform_interface/pubspec.yaml @@ -1,21 +1,23 @@ name: firebase_performance_platform_interface description: A common platform interface for the firebase_performance plugin. -version: 0.1.6+6 +version: 0.2.0+3 +resolution: workspace homepage: https://firebase.google.com/docs/perf-mon/flutter/get-started environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 flutter: sdk: flutter + meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter - pigeon: 25.3.2 + pigeon: 26.3.4 diff --git a/packages/firebase_performance/firebase_performance_platform_interface/test/pigeon/test_api.dart b/packages/firebase_performance/firebase_performance_platform_interface/test/pigeon/test_api.dart index 4426bc6d19af..e1bf4fe97659 100644 --- a/packages/firebase_performance/firebase_performance_platform_interface/test/pigeon/test_api.dart +++ b/packages/firebase_performance/firebase_performance_platform_interface/test/pigeon/test_api.dart @@ -1,9 +1,9 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers +// 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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -41,7 +41,7 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - final int? value = readValue(buffer) as int?; + final value = readValue(buffer) as int?; return value == null ? null : HttpMethod.values[value]; case 130: return HttpMetricOptions.decode(readValue(buffer)!); @@ -80,9 +80,7 @@ abstract class TestFirebasePerformanceHostApi { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -93,14 +91,10 @@ abstract class TestFirebasePerformanceHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled was null.'); - final List args = (message as List?)!; - final bool? arg_enabled = (args[0] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.setPerformanceCollectionEnabled was null, expected non-null bool.'); + final List args = message! as List; + final bool arg_enabled = args[0]! as bool; try { - await api.setPerformanceCollectionEnabled(arg_enabled!); + await api.setPerformanceCollectionEnabled(arg_enabled); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -112,9 +106,7 @@ abstract class TestFirebasePerformanceHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.isPerformanceCollectionEnabled$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -138,9 +130,7 @@ abstract class TestFirebasePerformanceHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -151,14 +141,10 @@ abstract class TestFirebasePerformanceHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace was null.'); - final List args = (message as List?)!; - final String? arg_name = (args[0] as String?); - assert(arg_name != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startTrace was null, expected non-null String.'); + final List args = message! as List; + final String arg_name = args[0]! as String; try { - final int output = await api.startTrace(arg_name!); + final int output = await api.startTrace(arg_name); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -170,9 +156,7 @@ abstract class TestFirebasePerformanceHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -183,17 +167,11 @@ abstract class TestFirebasePerformanceHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace was null.'); - final List args = (message as List?)!; - final int? arg_handle = (args[0] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace was null, expected non-null int.'); - final TraceAttributes? arg_attributes = (args[1] as TraceAttributes?); - assert(arg_attributes != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopTrace was null, expected non-null TraceAttributes.'); + final List args = message! as List; + final int arg_handle = args[0]! as int; + final TraceAttributes arg_attributes = args[1]! as TraceAttributes; try { - await api.stopTrace(arg_handle!, arg_attributes!); + await api.stopTrace(arg_handle, arg_attributes); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -205,9 +183,7 @@ abstract class TestFirebasePerformanceHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -218,15 +194,10 @@ abstract class TestFirebasePerformanceHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric was null.'); - final List args = (message as List?)!; - final HttpMetricOptions? arg_options = - (args[0] as HttpMetricOptions?); - assert(arg_options != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.startHttpMetric was null, expected non-null HttpMetricOptions.'); + final List args = message! as List; + final HttpMetricOptions arg_options = args[0]! as HttpMetricOptions; try { - final int output = await api.startHttpMetric(arg_options!); + final int output = await api.startHttpMetric(arg_options); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); @@ -238,9 +209,7 @@ abstract class TestFirebasePerformanceHostApi { } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); @@ -251,18 +220,12 @@ abstract class TestFirebasePerformanceHostApi { _testBinaryMessengerBinding!.defaultBinaryMessenger .setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric was null.'); - final List args = (message as List?)!; - final int? arg_handle = (args[0] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric was null, expected non-null int.'); - final HttpMetricAttributes? arg_attributes = - (args[1] as HttpMetricAttributes?); - assert(arg_attributes != null, - 'Argument for dev.flutter.pigeon.firebase_performance_platform_interface.FirebasePerformanceHostApi.stopHttpMetric was null, expected non-null HttpMetricAttributes.'); + final List args = message! as List; + final int arg_handle = args[0]! as int; + final HttpMetricAttributes arg_attributes = + args[1]! as HttpMetricAttributes; try { - await api.stopHttpMetric(arg_handle!, arg_attributes!); + await api.stopHttpMetric(arg_handle, arg_attributes); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/firebase_performance/firebase_performance_web/CHANGELOG.md b/packages/firebase_performance/firebase_performance_web/CHANGELOG.md index ffc36e7d5ef0..366e8731fb1d 100644 --- a/packages/firebase_performance/firebase_performance_web/CHANGELOG.md +++ b/packages/firebase_performance/firebase_performance_web/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.1.8+9 + + - Update a dependency to the latest release. + +## 0.1.8+8 + + - Update a dependency to the latest release. + +## 0.1.8+7 + + - Update a dependency to the latest release. + +## 0.1.8+6 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 0.1.8+5 + + - Update a dependency to the latest release. + ## 0.1.8+4 - Update a dependency to the latest release. diff --git a/packages/firebase_performance/firebase_performance_web/lib/src/firebase_performance_version.dart b/packages/firebase_performance/firebase_performance_web/lib/src/firebase_performance_version.dart index bad4a118632f..462fc9d7e82a 100644 --- a/packages/firebase_performance/firebase_performance_web/lib/src/firebase_performance_version.dart +++ b/packages/firebase_performance/firebase_performance_web/lib/src/firebase_performance_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '0.11.2'; +const packageVersion = '0.11.4+3'; diff --git a/packages/firebase_performance/firebase_performance_web/pubspec.yaml b/packages/firebase_performance/firebase_performance_web/pubspec.yaml index b53bce4989fe..3ec46cc78e2a 100644 --- a/packages/firebase_performance/firebase_performance_web/pubspec.yaml +++ b/packages/firebase_performance/firebase_performance_web/pubspec.yaml @@ -1,17 +1,18 @@ name: firebase_performance_web description: Web implementation of Firebase Performance monitoring. homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_performance/firebase_performance_web -version: 0.1.8+4 +version: 0.1.8+9 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 - firebase_performance_platform_interface: ^0.1.6+6 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 + firebase_performance_platform_interface: ^0.2.0+3 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/firebase_remote_config/analysis_options.yaml b/packages/firebase_remote_config/analysis_options.yaml index 75a21d21f70b..02c4d6a7e39a 100644 --- a/packages/firebase_remote_config/analysis_options.yaml +++ b/packages/firebase_remote_config/analysis_options.yaml @@ -8,3 +8,4 @@ analyzer: exclude: - firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart - firebase_remote_config_platform_interface/test/pigeon/test_api.dart + - firebase_remote_config_platform_interface/pigeons/messages.dart diff --git a/packages/firebase_remote_config/firebase_remote_config/CHANGELOG.md b/packages/firebase_remote_config/firebase_remote_config/CHANGELOG.md index a4dbc97f0f9e..dcf9d7e976e6 100644 --- a/packages/firebase_remote_config/firebase_remote_config/CHANGELOG.md +++ b/packages/firebase_remote_config/firebase_remote_config/CHANGELOG.md @@ -1,3 +1,24 @@ +## 6.5.3 + + - Update a dependency to the latest release. + +## 6.5.2 + + - Update a dependency to the latest release. + +## 6.5.1 + + - Update a dependency to the latest release. + +## 6.5.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 6.4.0 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 6.3.0 - **FIX**(remote-config,ios): fix hot reload issue ([#18062](https://github.com/firebase/flutterfire/issues/18062)). ([5db57711](https://github.com/firebase/flutterfire/commit/5db577116139d469bcdf38dd58f69c1e5f61c87e)) diff --git a/packages/firebase_remote_config/firebase_remote_config/android/build.gradle b/packages/firebase_remote_config/firebase_remote_config/android/build.gradle index a03f8e444446..d2a0aa74e23c 100644 --- a/packages/firebase_remote_config/firebase_remote_config/android/build.gradle +++ b/packages/firebase_remote_config/firebase_remote_config/android/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'com.android.library' apply from: file("local-config.gradle") buildscript { - ext.kotlin_version = "1.8.22" + ext.kotlin_version = "2.0.0" repositories { google() mavenCentral() @@ -19,9 +19,13 @@ rootProject.allprojects { } } -// AGP 9+ has built-in Kotlin support; older versions need the plugin explicitly. +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int -if (agpMajor < 9) { +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { apply plugin: 'kotlin-android' } @@ -51,12 +55,6 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - if (agpMajor < 9) { - kotlinOptions { - jvmTarget = project.ext.javaVersion - } - } - compileOptions { sourceCompatibility project.ext.javaVersion targetCompatibility project.ext.javaVersion @@ -83,4 +81,12 @@ android { } } +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } + } +} + apply from: file("./user-agent.gradle") diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt index 2700a45258cb..e1a3917abda3 100644 --- a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt +++ b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt @@ -20,7 +20,6 @@ import com.google.firebase.remoteconfig.FirebaseRemoteConfigFetchThrottledExcept import com.google.firebase.remoteconfig.FirebaseRemoteConfigServerException import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue -import io.flutter.Log import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding import io.flutter.plugin.common.BinaryMessenger @@ -30,11 +29,9 @@ import io.flutter.plugins.firebase.core.FlutterFirebasePlugin import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry import java.util.Objects -/** FirebaseRemoteConfigPlugin */ -class FirebaseRemoteConfigPlugin - - : FlutterFirebasePlugin, FlutterPlugin, - EventChannel.StreamHandler, FirebaseRemoteConfigHostApi { +/** FirebaseRemoteConfigPlugin */ +class FirebaseRemoteConfigPlugin : + FlutterFirebasePlugin, FlutterPlugin, EventChannel.StreamHandler, FirebaseRemoteConfigHostApi { private val listenersMap: MutableMap = HashMap() private var eventChannel: EventChannel? = null @@ -56,8 +53,7 @@ class FirebaseRemoteConfigPlugin try { val remoteConfig = FirebaseRemoteConfig.getInstance(firebaseApp) val configProperties = getConfigProperties(remoteConfig) - val configValues: MutableMap = - HashMap(configProperties) + val configValues: MutableMap = HashMap(configProperties) configValues["parameters"] = parseParameters(remoteConfig.all) taskCompletionSource.setResult(configValues) @@ -73,7 +69,7 @@ class FirebaseRemoteConfigPlugin val configProperties: MutableMap = HashMap() configProperties["fetchTimeout"] = remoteConfig.info.configSettings.fetchTimeoutInSeconds configProperties["minimumFetchInterval"] = - remoteConfig.info.configSettings.minimumFetchIntervalInSeconds + remoteConfig.info.configSettings.minimumFetchIntervalInSeconds configProperties["lastFetchTime"] = remoteConfig.info.fetchTimeMillis configProperties["lastFetchStatus"] = mapLastFetchStatus(remoteConfig.info.lastFetchStatus) return configProperties @@ -96,10 +92,7 @@ class FirebaseRemoteConfigPlugin private fun setupChannel(messenger: BinaryMessenger) { FirebaseRemoteConfigHostApi.setUp(messenger, this) - FlutterFirebasePluginRegistry.registerPlugin( - METHOD_CHANNEL, - this - ) + FlutterFirebasePluginRegistry.registerPlugin(METHOD_CHANNEL, this) eventChannel = EventChannel(messenger, EVENT_CHANNEL) eventChannel!!.setStreamHandler(this) @@ -122,7 +115,8 @@ class FirebaseRemoteConfigPlugin } private fun setCustomSignals( - remoteConfig: FirebaseRemoteConfig, customSignalsArguments: Map + remoteConfig: FirebaseRemoteConfig, + customSignalsArguments: Map ): Task { val taskCompletionSource = TaskCompletionSource() FlutterFirebasePlugin.cachedThreadPool.execute { @@ -150,18 +144,18 @@ class FirebaseRemoteConfigPlugin return taskCompletionSource.task } - private fun parseParameters(parameters: Map): Map { + private fun parseParameters( + parameters: Map + ): Map { val parsedParameters: MutableMap = HashMap() for (key in parameters.keys) { - parsedParameters[key] = createRemoteConfigValueMap( - parameters[key]!! - ) + parsedParameters[key] = createRemoteConfigValueMap(parameters[key]!!) } return parsedParameters } private fun createRemoteConfigValueMap( - remoteConfigValue: FirebaseRemoteConfigValue + remoteConfigValue: FirebaseRemoteConfigValue ): Map { val valueMap: MutableMap = HashMap() valueMap["value"] = remoteConfigValue.asByteArray() @@ -193,23 +187,24 @@ class FirebaseRemoteConfigPlugin val appName = Objects.requireNonNull(argumentsMap["appName"]) as String val remoteConfig = getRemoteConfig(appName) - listenersMap[appName] = remoteConfig.addOnConfigUpdateListener( - object : ConfigUpdateListener { - override fun onUpdate(configUpdate: ConfigUpdate) { - val updatedKeys = ArrayList(configUpdate.updatedKeys) - mainThreadHandler.post { events.success(updatedKeys) } - } - - override fun onError(error: FirebaseRemoteConfigException) { - events.error("firebase_remote_config", error.message, null) - } - }) + listenersMap[appName] = + remoteConfig.addOnConfigUpdateListener( + object : ConfigUpdateListener { + override fun onUpdate(configUpdate: ConfigUpdate) { + val updatedKeys = ArrayList(configUpdate.updatedKeys) + mainThreadHandler.post { events.success(updatedKeys) } + } + + override fun onError(error: FirebaseRemoteConfigException) { + events.error("firebase_remote_config", error.message, null) + } + }) } override fun onCancel(arguments: Any?) { - // arguments will be null on hot restart, so we will clean up listeners in didReinitializeFirebaseCore() - val argumentsMap = arguments as? Map - ?: return + // arguments will be null on hot restart, so we will clean up listeners in + // didReinitializeFirebaseCore() + val argumentsMap = arguments as? Map ?: return val appName = Objects.requireNonNull(argumentsMap["appName"]) as String val listener = listenersMap[appName] @@ -219,7 +214,7 @@ class FirebaseRemoteConfigPlugin } } - /** Remove all registered listeners. */ + /** Remove all registered listeners. */ private fun removeEventListeners() { for (listener in listenersMap.values) { listener.remove() @@ -227,9 +222,8 @@ class FirebaseRemoteConfigPlugin listenersMap.clear() } - private fun handleFailure (callback: (Result) -> Unit, exception: Exception?) { - val details: MutableMap = - HashMap() + private fun handleFailure(callback: (Result) -> Unit, exception: Exception?) { + val details: MutableMap = HashMap() if (exception is FirebaseRemoteConfigFetchThrottledException) { details["code"] = "throttled" details["message"] = "frequency of requests exceeds throttled limits" @@ -252,9 +246,7 @@ class FirebaseRemoteConfigPlugin details["code"] = "unknown" details["message"] = "unknown remote config error" } - callback(Result.failure(FlutterError( "firebase_remote_config", - exception?.message, - details))) + callback(Result.failure(FlutterError("firebase_remote_config", exception?.message, details))) } companion object { @@ -265,10 +257,9 @@ class FirebaseRemoteConfigPlugin override fun fetch(appName: String, callback: (Result) -> Unit) { getRemoteConfig(appName).fetch().addOnCompleteListener { task -> - if(task.isSuccessful){ + if (task.isSuccessful) { callback(Result.success(Unit)) - } - else { + } else { handleFailure(callback, task.exception) } } @@ -276,10 +267,9 @@ class FirebaseRemoteConfigPlugin override fun fetchAndActivate(appName: String, callback: (Result) -> Unit) { getRemoteConfig(appName).fetchAndActivate().addOnCompleteListener { task -> - if(task.isSuccessful){ + if (task.isSuccessful) { callback(Result.success(task.result)) - } - else { + } else { handleFailure(callback, task.exception) } } @@ -287,43 +277,42 @@ class FirebaseRemoteConfigPlugin override fun activate(appName: String, callback: (Result) -> Unit) { getRemoteConfig(appName).activate().addOnCompleteListener { task -> - if(task.isSuccessful){ + if (task.isSuccessful) { callback(Result.success(task.result)) - } - else { + } else { handleFailure(callback, task.exception) } } } override fun setConfigSettings( - appName: String, - settings: RemoteConfigPigeonSettings, - callback: (Result) -> Unit + appName: String, + settings: RemoteConfigPigeonSettings, + callback: (Result) -> Unit ) { val configSettings = - FirebaseRemoteConfigSettings.Builder() - .setFetchTimeoutInSeconds(settings.fetchTimeoutSeconds) - .setMinimumFetchIntervalInSeconds(settings.minimumFetchIntervalSeconds) - .build() + FirebaseRemoteConfigSettings.Builder() + .setFetchTimeoutInSeconds(settings.fetchTimeoutSeconds) + .setMinimumFetchIntervalInSeconds(settings.minimumFetchIntervalSeconds) + .build() getRemoteConfig(appName).setConfigSettingsAsync(configSettings).addOnCompleteListener { task -> - if(task.isSuccessful){ + if (task.isSuccessful) { callback(Result.success(Unit)) - } - else { + } else { handleFailure(callback, task.exception) } } } - override fun setDefaults(appName: String, defaultParameters: Map, callback: (Result) -> Unit) { - getRemoteConfig( - appName - ).setDefaultsAsync(defaultParameters).addOnCompleteListener { task -> - if(task.isSuccessful){ + override fun setDefaults( + appName: String, + defaultParameters: Map, + callback: (Result) -> Unit + ) { + getRemoteConfig(appName).setDefaultsAsync(defaultParameters).addOnCompleteListener { task -> + if (task.isSuccessful) { callback(Result.success(Unit)) - } - else { + } else { handleFailure(callback, task.exception) } } @@ -331,22 +320,24 @@ class FirebaseRemoteConfigPlugin override fun ensureInitialized(appName: String, callback: (Result) -> Unit) { getRemoteConfig(appName).ensureInitialized().addOnCompleteListener { task -> - if(task.isSuccessful){ + if (task.isSuccessful) { callback(Result.success(Unit)) - } - else { + } else { handleFailure(callback, task.exception) } } } - override fun setCustomSignals(appName: String, customSignals: Map, callback: (Result) -> Unit) { + override fun setCustomSignals( + appName: String, + customSignals: Map, + callback: (Result) -> Unit + ) { val remoteConfig = getRemoteConfig(appName) - setCustomSignals(remoteConfig, customSignals).addOnCompleteListener {task-> - if(task.isSuccessful){ + setCustomSignals(remoteConfig, customSignals).addOnCompleteListener { task -> + if (task.isSuccessful) { callback(Result.success(Unit)) - } - else { + } else { handleFailure(callback, task.exception) } } @@ -357,10 +348,7 @@ class FirebaseRemoteConfigPlugin callback(Result.success(parseParameters(remoteConfig.all))) } - override fun getProperties( - appName: String, - callback: (Result>) -> Unit - ) { + override fun getProperties(appName: String, callback: (Result>) -> Unit) { val remoteConfig = getRemoteConfig(appName) val configProperties = getConfigProperties(remoteConfig) callback(Result.success(configProperties)) diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt index d758db99adda..e343fc60d5d1 100644 --- a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt +++ b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt @@ -12,7 +12,6 @@ import com.google.firebase.platforminfo.LibraryVersionComponent class FlutterFirebaseAppRegistrar : ComponentRegistrar { override fun getComponents(): List> { return listOf( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION) - ) + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION)) } } diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt index 40c1056cf62c..840d3090a3bc 100644 --- a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt +++ b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -10,12 +10,11 @@ package io.flutter.plugins.firebase.firebaseremoteconfig import android.util.Log import io.flutter.plugin.common.BasicMessageChannel import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec import io.flutter.plugin.common.StandardMessageCodec import java.io.ByteArrayOutputStream import java.nio.ByteBuffer + private object GeneratedAndroidFirebaseRemoteConfigPigeonUtils { fun wrapResult(result: Any?): List { @@ -24,69 +23,178 @@ private object GeneratedAndroidFirebaseRemoteConfigPigeonUtils { fun wrapError(exception: Throwable): List { return if (exception is FlutterError) { - listOf( - exception.code, - exception.message, - exception.details - ) + listOf(exception.code, exception.message, exception.details) } else { listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) } } + + fun doubleEquals(a: Double, b: Double): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN()) + } + + fun floatEquals(a: Float, b: Float): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN()) + } + + fun doubleHash(d: Double): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (d == 0.0) 0.0 else d + val bits = java.lang.Double.doubleToLongBits(normalized) + return (bits xor (bits ushr 32)).toInt() + } + + fun floatHash(f: Float): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (f == 0.0f) 0.0f else f + return java.lang.Float.floatToIntBits(normalized) + } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a === b) { + return true + } + if (a == null || b == null) { + return false + } if (a is ByteArray && b is ByteArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is IntArray && b is IntArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is LongArray && b is LongArray) { - return a.contentEquals(b) + return a.contentEquals(b) } if (a is DoubleArray && b is DoubleArray) { - return a.contentEquals(b) + if (a.size != b.size) return false + for (i in a.indices) { + if (!doubleEquals(a[i], b[i])) return false + } + return true + } + if (a is FloatArray && b is FloatArray) { + if (a.size != b.size) return false + for (i in a.indices) { + if (!floatEquals(a[i], b[i])) return false + } + return true } if (a is Array<*> && b is Array<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + for (i in a.indices) { + if (!deepEquals(a[i], b[i])) return false + } + return true } if (a is List<*> && b is List<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } + if (a.size != b.size) return false + val iterA = a.iterator() + val iterB = b.iterator() + while (iterA.hasNext() && iterB.hasNext()) { + if (!deepEquals(iterA.next(), iterB.next())) return false + } + return true } if (a is Map<*, *> && b is Map<*, *>) { - return a.size == b.size && a.all { - (b as Map).containsKey(it.key) && - deepEquals(it.value, b[it.key]) + if (a.size != b.size) return false + for (entry in a) { + val key = entry.key + var found = false + for (bEntry in b) { + if (deepEquals(key, bEntry.key)) { + if (deepEquals(entry.value, bEntry.value)) { + found = true + break + } else { + return false + } + } + } + if (!found) return false } + return true + } + if (a is Double && b is Double) { + return doubleEquals(a, b) + } + if (a is Float && b is Float) { + return floatEquals(a, b) } return a == b } - + + fun deepHash(value: Any?): Int { + return when (value) { + null -> 0 + is ByteArray -> value.contentHashCode() + is IntArray -> value.contentHashCode() + is LongArray -> value.contentHashCode() + is DoubleArray -> { + var result = 1 + for (item in value) { + result = 31 * result + doubleHash(item) + } + result + } + is FloatArray -> { + var result = 1 + for (item in value) { + result = 31 * result + floatHash(item) + } + result + } + is Array<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is List<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is Map<*, *> -> { + var result = 0 + for (entry in value) { + result += ((deepHash(entry.key) * 31) xor deepHash(entry.value)) + } + result + } + is Double -> doubleHash(value) + is Float -> floatHash(value) + else -> value.hashCode() + } + } } /** * 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 FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() /** Generated class from Pigeon that represents data sent in messages. */ -data class RemoteConfigPigeonSettings ( - val fetchTimeoutSeconds: Long, - val minimumFetchIntervalSeconds: Long -) - { +data class RemoteConfigPigeonSettings( + val fetchTimeoutSeconds: Long, + val minimumFetchIntervalSeconds: Long +) { companion object { fun fromList(pigeonVar_list: List): RemoteConfigPigeonSettings { val fetchTimeoutSeconds = pigeonVar_list[0] as Long @@ -94,35 +202,52 @@ data class RemoteConfigPigeonSettings ( return RemoteConfigPigeonSettings(fetchTimeoutSeconds, minimumFetchIntervalSeconds) } } + fun toList(): List { return listOf( - fetchTimeoutSeconds, - minimumFetchIntervalSeconds, + fetchTimeoutSeconds, + minimumFetchIntervalSeconds, ) } + override fun equals(other: Any?): Boolean { - if (other !is RemoteConfigPigeonSettings) { + if (other == null || other.javaClass != javaClass) { return false } if (this === other) { return true } - return GeneratedAndroidFirebaseRemoteConfigPigeonUtils.deepEquals(toList(), other.toList()) } + val other = other as RemoteConfigPigeonSettings + return GeneratedAndroidFirebaseRemoteConfigPigeonUtils.deepEquals( + this.fetchTimeoutSeconds, other.fetchTimeoutSeconds) && + GeneratedAndroidFirebaseRemoteConfigPigeonUtils.deepEquals( + this.minimumFetchIntervalSeconds, other.minimumFetchIntervalSeconds) + } - override fun hashCode(): Int = toList().hashCode() + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = + 31 * result + + GeneratedAndroidFirebaseRemoteConfigPigeonUtils.deepHash(this.fetchTimeoutSeconds) + result = + 31 * result + + GeneratedAndroidFirebaseRemoteConfigPigeonUtils.deepHash( + this.minimumFetchIntervalSeconds) + return result + } } + private open class GeneratedAndroidFirebaseRemoteConfigPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - RemoteConfigPigeonSettings.fromList(it) - } + return (readValue(buffer) as? List)?.let { RemoteConfigPigeonSettings.fromList(it) } } else -> super.readValueOfType(type, buffer) } } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { is RemoteConfigPigeonSettings -> { stream.write(129) @@ -133,30 +258,59 @@ private open class GeneratedAndroidFirebaseRemoteConfigPigeonCodec : StandardMes } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface FirebaseRemoteConfigHostApi { fun fetch(appName: String, callback: (Result) -> Unit) + fun fetchAndActivate(appName: String, callback: (Result) -> Unit) + fun activate(appName: String, callback: (Result) -> Unit) - fun setConfigSettings(appName: String, settings: RemoteConfigPigeonSettings, callback: (Result) -> Unit) - fun setDefaults(appName: String, defaultParameters: Map, callback: (Result) -> Unit) + + fun setConfigSettings( + appName: String, + settings: RemoteConfigPigeonSettings, + callback: (Result) -> Unit + ) + + fun setDefaults( + appName: String, + defaultParameters: Map, + callback: (Result) -> Unit + ) + fun ensureInitialized(appName: String, callback: (Result) -> Unit) - fun setCustomSignals(appName: String, customSignals: Map, callback: (Result) -> Unit) + + fun setCustomSignals( + appName: String, + customSignals: Map, + callback: (Result) -> Unit + ) + fun getAll(appName: String, callback: (Result>) -> Unit) + fun getProperties(appName: String, callback: (Result>) -> Unit) companion object { /** The codec used by FirebaseRemoteConfigHostApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidFirebaseRemoteConfigPigeonCodec() - } - /** Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages through the `binaryMessenger`. */ + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseRemoteConfigPigeonCodec() } + /** + * Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages through the + * `binaryMessenger`. + */ @JvmOverloads - fun setUp(binaryMessenger: BinaryMessenger, api: FirebaseRemoteConfigHostApi?, messageChannelSuffix: String = "") { - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebaseRemoteConfigHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -175,7 +329,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -195,7 +353,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -215,7 +377,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -235,7 +401,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -255,7 +425,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -274,7 +448,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -294,7 +472,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -314,7 +496,11 @@ interface FirebaseRemoteConfigHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List diff --git a/packages/firebase_remote_config/firebase_remote_config/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/remoteconfig/example/MainActivity.kt b/packages/firebase_remote_config/firebase_remote_config/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/remoteconfig/example/MainActivity.kt index f93b81d6fbde..30304afd1029 100644 --- a/packages/firebase_remote_config/firebase_remote_config/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/remoteconfig/example/MainActivity.kt +++ b/packages/firebase_remote_config/firebase_remote_config/example/android/app/src/main/kotlin/io/flutter/plugins/firebase/remoteconfig/example/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebase.remoteconfig.example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_remote_config/firebase_remote_config/example/pubspec.yaml b/packages/firebase_remote_config/firebase_remote_config/example/pubspec.yaml index 97afc16174d2..d78f440ce15b 100644 --- a/packages/firebase_remote_config/firebase_remote_config/example/pubspec.yaml +++ b/packages/firebase_remote_config/firebase_remote_config/example/pubspec.yaml @@ -1,15 +1,16 @@ name: firebase_remote_config_example description: Demonstrates how to use the firebase_remote_config plugin. +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - firebase_core: ^4.6.0 - firebase_remote_config: ^6.3.0 + firebase_core: ^4.11.0 + firebase_remote_config: ^6.5.3 flutter: sdk: flutter diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Package.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Package.swift index 804e4fdb9967..43a620865941 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Package.swift +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_remote_config", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-remote-config", targets: ["firebase_remote_config"]), + .library(name: "firebase-remote-config", targets: ["firebase_remote_config"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift index d2c573f04e70..a80378388ef9 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift @@ -3,4 +3,4 @@ // found in the LICENSE file. /// Auto-generated file. Do not edit. -public let versionNumber = "5.4.7" +public let versionNumber = "6.5.3" diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift index ac749f8cdb7f..f7b031c0c6e3 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -52,7 +52,7 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } @@ -66,6 +66,19 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +private func doubleEqualsFirebaseRemoteConfigMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirebaseRemoteConfigMessages(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8_0000_0000_0000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } +} + func deepEqualsFirebaseRemoteConfigMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { let cleanLhs = nilOrValue(lhs) as Any? let cleanRhs = nilOrValue(rhs) as Any? @@ -76,59 +89,90 @@ func deepEqualsFirebaseRemoteConfigMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { case (nil, _), (_, nil): return false - case is (Void, Void): + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: return true - case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): - return cleanLhsHashable == cleanRhsHashable + case is (Void, Void): + return true - case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): - guard cleanLhsArray.count == cleanRhsArray.count else { return false } - for (index, element) in cleanLhsArray.enumerated() { - if !deepEqualsFirebaseRemoteConfigMessages(element, cleanRhsArray[index]) { + case let (lhsArray, rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsFirebaseRemoteConfigMessages(element, rhsArray[index]) { return false } } return true - case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): - guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } - for (key, cleanLhsValue) in cleanLhsDictionary { - guard cleanRhsDictionary.index(forKey: key) != nil else { return false } - if !deepEqualsFirebaseRemoteConfigMessages(cleanLhsValue, cleanRhsDictionary[key]!) { + case let (lhsArray, rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsFirebaseRemoteConfigMessages(element, rhsArray[index]) { return false } } return true + case let (lhsDictionary, rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEqualsFirebaseRemoteConfigMessages(lhsKey, rhsKey) { + if deepEqualsFirebaseRemoteConfigMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirebaseRemoteConfigMessages(lhs, rhs) + + case let (lhsHashable, rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + default: - // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be - // untrue. return false } } func deepHashFirebaseRemoteConfigMessages(value: Any?, hasher: inout Hasher) { - if let valueList = value as? [AnyHashable] { - for item in valueList { - deepHashFirebaseRemoteConfigMessages(value: item, hasher: &hasher) - } - return - } - - if let valueDict = value as? [AnyHashable: AnyHashable] { - for key in valueDict.keys { - hasher.combine(key) - deepHashFirebaseRemoteConfigMessages(value: valueDict[key]!, hasher: &hasher) + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirebaseRemoteConfigMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirebaseRemoteConfigMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirebaseRemoteConfigMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirebaseRemoteConfigMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirebaseRemoteConfigMessages(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) } - return - } - - if let hashableValue = value as? AnyHashable { - hasher.combine(hashableValue.hashValue) + } else { + hasher.combine(0) } - - return hasher.combine(String(describing: value)) } /// Generated class from Pigeon that represents data sent in messages. @@ -155,11 +199,23 @@ struct RemoteConfigPigeonSettings: Hashable { } static func == (lhs: RemoteConfigPigeonSettings, rhs: RemoteConfigPigeonSettings) -> Bool { - deepEqualsFirebaseRemoteConfigMessages(lhs.toList(), rhs.toList()) + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseRemoteConfigMessages( + lhs.fetchTimeoutSeconds, + rhs.fetchTimeoutSeconds + ) + && deepEqualsFirebaseRemoteConfigMessages( + lhs.minimumFetchIntervalSeconds, + rhs.minimumFetchIntervalSeconds + ) } func hash(into hasher: inout Hasher) { - deepHashFirebaseRemoteConfigMessages(value: toList(), hasher: &hasher) + hasher.combine("RemoteConfigPigeonSettings") + deepHashFirebaseRemoteConfigMessages(value: fetchTimeoutSeconds, hasher: &hasher) + deepHashFirebaseRemoteConfigMessages(value: minimumFetchIntervalSeconds, hasher: &hasher) } } @@ -207,13 +263,16 @@ protocol FirebaseRemoteConfigHostApi { func fetch(appName: String, completion: @escaping (Result) -> Void) func fetchAndActivate(appName: String, completion: @escaping (Result) -> Void) func activate(appName: String, completion: @escaping (Result) -> Void) - func setConfigSettings(appName: String, settings: RemoteConfigPigeonSettings, - completion: @escaping (Result) -> Void) - func setDefaults(appName: String, defaultParameters: [String: Any?], - completion: @escaping (Result) -> Void) + func setConfigSettings( + appName: String, settings: RemoteConfigPigeonSettings, + completion: @escaping (Result) -> Void) + func setDefaults( + appName: String, defaultParameters: [String: Any?], + completion: @escaping (Result) -> Void) func ensureInitialized(appName: String, completion: @escaping (Result) -> Void) - func setCustomSignals(appName: String, customSignals: [String: Any?], - completion: @escaping (Result) -> Void) + func setCustomSignals( + appName: String, customSignals: [String: Any?], + completion: @escaping (Result) -> Void) func getAll(appName: String, completion: @escaping (Result<[String: Any?], Error>) -> Void) func getProperties(appName: String, completion: @escaping (Result<[String: Any], Error>) -> Void) } @@ -226,11 +285,14 @@ class FirebaseRemoteConfigHostApiSetup { /// Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseRemoteConfigHostApi?, - messageChannelSuffix: String = "") { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseRemoteConfigHostApi?, + messageChannelSuffix: String = "" + ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let fetchChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -242,7 +304,7 @@ class FirebaseRemoteConfigHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -251,7 +313,8 @@ class FirebaseRemoteConfigHostApiSetup { fetchChannel.setMessageHandler(nil) } let fetchAndActivateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -261,9 +324,9 @@ class FirebaseRemoteConfigHostApiSetup { let appNameArg = args[0] as! String api.fetchAndActivate(appName: appNameArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -272,7 +335,8 @@ class FirebaseRemoteConfigHostApiSetup { fetchAndActivateChannel.setMessageHandler(nil) } let activateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -282,9 +346,9 @@ class FirebaseRemoteConfigHostApiSetup { let appNameArg = args[0] as! String api.activate(appName: appNameArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -293,7 +357,8 @@ class FirebaseRemoteConfigHostApiSetup { activateChannel.setMessageHandler(nil) } let setConfigSettingsChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -306,7 +371,7 @@ class FirebaseRemoteConfigHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -315,7 +380,8 @@ class FirebaseRemoteConfigHostApiSetup { setConfigSettingsChannel.setMessageHandler(nil) } let setDefaultsChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -328,7 +394,7 @@ class FirebaseRemoteConfigHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -337,7 +403,8 @@ class FirebaseRemoteConfigHostApiSetup { setDefaultsChannel.setMessageHandler(nil) } let ensureInitializedChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -349,7 +416,7 @@ class FirebaseRemoteConfigHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -358,7 +425,8 @@ class FirebaseRemoteConfigHostApiSetup { ensureInitializedChannel.setMessageHandler(nil) } let setCustomSignalsChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -371,7 +439,7 @@ class FirebaseRemoteConfigHostApiSetup { switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -380,7 +448,8 @@ class FirebaseRemoteConfigHostApiSetup { setCustomSignalsChannel.setMessageHandler(nil) } let getAllChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -390,9 +459,9 @@ class FirebaseRemoteConfigHostApiSetup { let appNameArg = args[0] as! String api.getAll(appName: appNameArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -401,7 +470,8 @@ class FirebaseRemoteConfigHostApiSetup { getAllChannel.setMessageHandler(nil) } let getPropertiesChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties\(channelSuffix)", + name: + "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) @@ -411,9 +481,9 @@ class FirebaseRemoteConfigHostApiSetup { let appNameArg = args[0] as! String api.getProperties(appName: appNameArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift index 10a80253147c..aa923738980a 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import FirebaseRemoteConfig + #if canImport(FlutterMacOS) import FlutterMacOS #else @@ -13,7 +15,6 @@ #else import firebase_core_shared #endif -import FirebaseRemoteConfig let kFirebaseRemoteConfigChannelName = "plugins.flutter.io/firebase_remote_config" let kFirebaseRemoteConfigUpdatedChannelName = "plugins.flutter.io/firebase_remote_config_updated" @@ -21,7 +22,8 @@ let kFirebaseRemoteConfigUpdatedChannelName = "plugins.flutter.io/firebase_remot extension FlutterError: Error {} public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, - FLTFirebasePluginProtocol, FirebaseRemoteConfigHostApi { + FLTFirebasePluginProtocol, FirebaseRemoteConfigHostApi +{ private var listenersMap: [String: ConfigUpdateListenerRegistration] = [:] private var fetchAndActivateRetry = false @@ -106,8 +108,10 @@ public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamH } } - func setConfigSettings(appName: String, settings: RemoteConfigPigeonSettings, - completion: @escaping (Result) -> Void) { + func setConfigSettings( + appName: String, settings: RemoteConfigPigeonSettings, + completion: @escaping (Result) -> Void + ) { let fetchTimeout = settings.fetchTimeoutSeconds let minFetchInterval = settings.minimumFetchIntervalSeconds let configSettings = RemoteConfigSettings() @@ -117,8 +121,10 @@ public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamH completion(.success(())) } - func setDefaults(appName: String, defaultParameters: [String: Any?], - completion: @escaping (Result) -> Void) { + func setDefaults( + appName: String, defaultParameters: [String: Any?], + completion: @escaping (Result) -> Void + ) { var filtered: [String: NSObject] = [:] for (key, value) in defaultParameters { @@ -141,8 +147,10 @@ public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamH } } - func setCustomSignals(appName: String, customSignals: [String: Any?], - completion: @escaping (Result) -> Void) { + func setCustomSignals( + appName: String, customSignals: [String: Any?], + completion: @escaping (Result) -> Void + ) { let signalValues = convertToCustomSignalValues(customSignals) Task { do { @@ -171,8 +179,10 @@ public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamH completion(.success(parameters)) } - func getProperties(appName: String, - completion: @escaping (Result<[String: Any], any Error>) -> Void) { + func getProperties( + appName: String, + completion: @escaping (Result<[String: Any], any Error>) -> Void + ) { let config = getRemoteConfig(from: appName) completion(.success(configProperties(for: config))) } @@ -189,8 +199,10 @@ public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamH kFirebaseRemoteConfigChannelName } - public func onListen(withArguments arguments: Any?, - eventSink events: @escaping FlutterEventSink) -> FlutterError? { + public func onListen( + withArguments arguments: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { guard let args = arguments as? [String: Any], let appName = args["appName"] as? String else { return nil } @@ -236,7 +248,8 @@ public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamH } private func createRemoteConfigValueDict(_ remoteConfigValue: RemoteConfigValue) - -> [String: Any] { + -> [String: Any] + { [ "value": FlutterStandardTypedData(bytes: remoteConfigValue.dataValue), "source": mapSource(remoteConfigValue.source), diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift index 983987589c41..45065769736b 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift @@ -11,7 +11,8 @@ class FLTFirebaseRemoteConfigUtils { switch error.code { case RemoteConfigError.internalError.rawValue: if let description = error.userInfo[NSLocalizedDescriptionKey] as? String, - description.contains("403") { + description.contains("403") + { // See PR for details: https://github.com/firebase/flutterfire/pull/9629 codeAndMessage["code"] = "forbidden" let updateMessage = @@ -19,7 +20,8 @@ class FLTFirebaseRemoteConfigUtils { codeAndMessage["message"] = updateMessage } else { codeAndMessage["code"] = "internal" - codeAndMessage["message"] = error + codeAndMessage["message"] = + error .userInfo[NSLocalizedDescriptionKey] as? String ?? "Internal error" } diff --git a/packages/firebase_remote_config/firebase_remote_config/lib/firebase_remote_config.dart b/packages/firebase_remote_config/firebase_remote_config/lib/firebase_remote_config.dart index ba8c7fdcc5d0..5a23bdbb8672 100644 --- a/packages/firebase_remote_config/firebase_remote_config/lib/firebase_remote_config.dart +++ b/packages/firebase_remote_config/firebase_remote_config/lib/firebase_remote_config.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:firebase_remote_config_platform_interface/firebase_remote_config_platform_interface.dart'; export 'package:firebase_remote_config_platform_interface/firebase_remote_config_platform_interface.dart' diff --git a/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart b/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart index 6d5a5845cddb..d7ed0f77fde0 100644 --- a/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart +++ b/packages/firebase_remote_config/firebase_remote_config/lib/src/firebase_remote_config.dart @@ -9,7 +9,7 @@ part of '../firebase_remote_config.dart'; /// You can get an instance by calling [FirebaseRemoteConfig.instance]. Note /// [FirebaseRemoteConfig.instance] is async. // ignore: prefer_mixin -class FirebaseRemoteConfig extends FirebasePluginPlatform { +class FirebaseRemoteConfig extends FirebasePlugin { FirebaseRemoteConfig._({required this.app}) : super(app.name, 'plugins.flutter.io/firebase_remote_config'); diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Package.swift b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Package.swift index 1a3e14ec5488..a5e6bcd78225 100644 --- a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Package.swift +++ b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Package.swift @@ -7,18 +7,18 @@ import PackageDescription -let firebase_sdk_version: Version = "12.9.0" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_remote_config", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-remote-config", targets: ["firebase_remote_config"]), + .library(name: "firebase-remote-config", targets: ["firebase_remote_config"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -29,8 +29,8 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml b/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml index e460e7389ba2..ebd5e1e14f2d 100644 --- a/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml +++ b/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml @@ -4,7 +4,8 @@ description: re-releasing. homepage: https://firebase.google.com/docs/remote-config repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_remote_config/firebase_remote_config -version: 6.3.0 +version: 6.5.3 +resolution: workspace topics: - firebase - remote @@ -15,14 +16,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_remote_config_platform_interface: ^2.1.1 - firebase_remote_config_web: ^1.10.5 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_remote_config_platform_interface: ^3.0.3 + firebase_remote_config_web: ^1.10.10 flutter: sdk: flutter diff --git a/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp index f8f200289fff..bf13f7b7ad47 100644 --- a/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp +++ b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace firebase_remote_config_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,6 +33,212 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace // RemoteConfigPigeonSettings RemoteConfigPigeonSettings::RemoteConfigPigeonSettings( @@ -70,22 +278,47 @@ RemoteConfigPigeonSettings RemoteConfigPigeonSettings::FromEncodableList( return decoded; } +bool RemoteConfigPigeonSettings::operator==( + const RemoteConfigPigeonSettings& other) const { + return PigeonInternalDeepEquals(fetch_timeout_seconds_, + other.fetch_timeout_seconds_) && + PigeonInternalDeepEquals(minimum_fetch_interval_seconds_, + other.minimum_fetch_interval_seconds_); +} + +bool RemoteConfigPigeonSettings::operator!=( + const RemoteConfigPigeonSettings& other) const { + return !(*this == other); +} + +size_t RemoteConfigPigeonSettings::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(fetch_timeout_seconds_); + result = + result * 31 + PigeonInternalDeepHash(minimum_fetch_interval_seconds_); + return result; +} + +size_t PigeonInternalDeepHash(const RemoteConfigPigeonSettings& v) { + return v.Hash(); +} + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { case 129: { return CustomEncodableValue(RemoteConfigPigeonSettings::FromEncodableList( std::get(ReadValue(stream)))); } default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { if (custom_value->type() == typeid(RemoteConfigPigeonSettings)) { @@ -97,25 +330,25 @@ void PigeonInternalCodecSerializer::WriteValue( return; } } - flutter::StandardCodecSerializer::WriteValue(value, stream); + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebaseRemoteConfigHostApi. -const flutter::StandardMessageCodec& FirebaseRemoteConfigHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( +const ::flutter::StandardMessageCodec& FirebaseRemoteConfigHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages // through the `binary_messenger`. void FirebaseRemoteConfigHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, + ::flutter::BinaryMessenger* binary_messenger, FirebaseRemoteConfigHostApi* api) { FirebaseRemoteConfigHostApi::SetUp(binary_messenger, api, ""); } void FirebaseRemoteConfigHostApi::SetUp( - flutter::BinaryMessenger* binary_messenger, + ::flutter::BinaryMessenger* binary_messenger, FirebaseRemoteConfigHostApi* api, const std::string& message_channel_suffix) { const std::string prepended_suffix = @@ -132,7 +365,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -170,7 +403,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -209,7 +442,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -247,7 +480,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -294,7 +527,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -339,7 +572,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -377,7 +610,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -423,7 +656,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); @@ -462,7 +695,7 @@ void FirebaseRemoteConfigHostApi::SetUp( if (api != nullptr) { channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_name_arg = args.at(0); diff --git a/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h index c325943b6d22..43ddff65edaa 100644 --- a/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h +++ b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -71,17 +71,30 @@ class RemoteConfigPigeonSettings { int64_t minimum_fetch_interval_seconds() const; void set_minimum_fetch_interval_seconds(int64_t value_arg); + bool operator==(const RemoteConfigPigeonSettings& other) const; + bool operator!=(const RemoteConfigPigeonSettings& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + private: static RemoteConfigPigeonSettings FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseRemoteConfigHostApi; friend class PigeonInternalCodecSerializer; int64_t fetch_timeout_seconds_; int64_t minimum_fetch_interval_seconds_; }; -class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -89,12 +102,12 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -118,32 +131,33 @@ class FirebaseRemoteConfigHostApi { std::function reply)> result) = 0; virtual void SetDefaults( const std::string& app_name, - const flutter::EncodableMap& default_parameters, + const ::flutter::EncodableMap& default_parameters, std::function reply)> result) = 0; virtual void EnsureInitialized( const std::string& app_name, std::function reply)> result) = 0; virtual void SetCustomSignals( - const std::string& app_name, const flutter::EncodableMap& custom_signals, + const std::string& app_name, + const ::flutter::EncodableMap& custom_signals, std::function reply)> result) = 0; virtual void GetAll( const std::string& app_name, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void GetProperties( const std::string& app_name, - std::function reply)> result) = 0; + std::function reply)> result) = 0; // The codec used by FirebaseRemoteConfigHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages // through the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseRemoteConfigHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseRemoteConfigHostApi* api, const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseRemoteConfigHostApi() = default; diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/CHANGELOG.md b/packages/firebase_remote_config/firebase_remote_config_platform_interface/CHANGELOG.md index 6f5c4eed2cb5..69f88aa1c97b 100644 --- a/packages/firebase_remote_config/firebase_remote_config_platform_interface/CHANGELOG.md +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/CHANGELOG.md @@ -1,3 +1,26 @@ +## 3.0.3 + + - Update a dependency to the latest release. + +## 3.0.2 + + - Update a dependency to the latest release. + +## 3.0.1 + + - Update a dependency to the latest release. + +## 3.0.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 2.1.2 + + - Update a dependency to the latest release. + ## 2.1.1 - Update a dependency to the latest release. diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart index c9ed1ec45a6a..f1d5c1686e7d 100644 --- a/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,49 +1,104 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.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, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; -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]; +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } - return [error.code, error.message, error.details]; + return replyList.firstOrNull; } bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } if (a is List && b is List) { return a.length == b.length && a.indexed .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; } return a == b; } +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + class RemoteConfigPigeonSettings { RemoteConfigPigeonSettings({ required this.fetchTimeoutSeconds, @@ -83,12 +138,14 @@ class RemoteConfigPigeonSettings { if (identical(this, other)) { return true; } - return _deepEquals(encode(), other.encode()); + return _deepEquals(fetchTimeoutSeconds, other.fetchTimeoutSeconds) && + _deepEquals( + minimumFetchIntervalSeconds, other.minimumFetchIntervalSeconds); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => _deepHash([runtimeType, ..._toList()]); } class _PigeonCodec extends StandardMessageCodec { @@ -133,261 +190,182 @@ class FirebaseRemoteConfigHostApi { final String pigeonVar_messageChannelSuffix; Future fetch(String appName) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future fetchAndActivate(String appName) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as bool; } Future activate(String appName) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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?)!; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as bool; } Future setConfigSettings( String appName, RemoteConfigPigeonSettings settings) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, settings]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setDefaults( String appName, Map defaultParameters) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, defaultParameters]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future ensureInitialized(String appName) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setCustomSignals( String appName, Map customSignals) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, customSignals]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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; - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future> getAll(String appName) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 Map?)! - .cast(); - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as Map) + .cast(); } Future> getProperties(String appName) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture 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 Map?)! - .cast(); - } + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as Map) + .cast(); } } diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml index 8d806b02ec00..cd08bec8ce61 100644 --- a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml @@ -4,23 +4,24 @@ homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_re repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_remote_config/firebase_remote_config_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.1 +version: 3.0.3 +resolution: workspace environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 - pigeon: 25.3.2 + pigeon: 26.3.4 diff --git a/packages/firebase_remote_config/firebase_remote_config_web/CHANGELOG.md b/packages/firebase_remote_config/firebase_remote_config_web/CHANGELOG.md index 43c872ef7a3f..a6ac5f7f5ece 100644 --- a/packages/firebase_remote_config/firebase_remote_config_web/CHANGELOG.md +++ b/packages/firebase_remote_config/firebase_remote_config_web/CHANGELOG.md @@ -1,3 +1,23 @@ +## 1.10.10 + + - Update a dependency to the latest release. + +## 1.10.9 + + - Update a dependency to the latest release. + +## 1.10.8 + + - Update a dependency to the latest release. + +## 1.10.7 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 1.10.6 + + - Update a dependency to the latest release. + ## 1.10.5 - Update a dependency to the latest release. diff --git a/packages/firebase_remote_config/firebase_remote_config_web/lib/src/firebase_remote_config_version.dart b/packages/firebase_remote_config/firebase_remote_config_web/lib/src/firebase_remote_config_version.dart index ab9c93b832a8..3b927bd0b786 100644 --- a/packages/firebase_remote_config/firebase_remote_config_web/lib/src/firebase_remote_config_version.dart +++ b/packages/firebase_remote_config/firebase_remote_config_web/lib/src/firebase_remote_config_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '6.3.0'; +const packageVersion = '6.5.3'; diff --git a/packages/firebase_remote_config/firebase_remote_config_web/pubspec.yaml b/packages/firebase_remote_config/firebase_remote_config_web/pubspec.yaml index dc4c2ada5299..23aac942e5ea 100644 --- a/packages/firebase_remote_config/firebase_remote_config_web/pubspec.yaml +++ b/packages/firebase_remote_config/firebase_remote_config_web/pubspec.yaml @@ -3,17 +3,18 @@ description: The web implementation of firebase_remote_config homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_remote_config/firebase_remote_config_web repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_remote_config/firebase_remote_config_web -version: 1.10.5 +version: 1.10.10 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 - firebase_remote_config_platform_interface: ^2.1.1 + _flutterfire_internals: ^1.3.73 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 + firebase_remote_config_platform_interface: ^3.0.3 flutter: sdk: flutter flutter_web_plugins: @@ -21,7 +22,7 @@ dependencies: dev_dependencies: build_runner: ^2.3.3 - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/firebase_storage/analysis_options.yaml b/packages/firebase_storage/analysis_options.yaml index 5004d8c56d8c..ad032c876e0e 100644 --- a/packages/firebase_storage/analysis_options.yaml +++ b/packages/firebase_storage/analysis_options.yaml @@ -10,6 +10,10 @@ analyzer: # We explicitly enabled even conflicting rules and are fixing the conflict # in this file included_file_warning: ignore + exclude: + - firebase_storage_platform_interface/lib/src/pigeon/messages.pigeon.dart + - firebase_storage_platform_interface/test/pigeon/test_api.dart + - firebase_storage_platform_interface/pigeons/messages.dart linter: rules: diff --git a/packages/firebase_storage/firebase_storage/CHANGELOG.md b/packages/firebase_storage/firebase_storage/CHANGELOG.md index 5a830e1f383d..195f5e8984c0 100644 --- a/packages/firebase_storage/firebase_storage/CHANGELOG.md +++ b/packages/firebase_storage/firebase_storage/CHANGELOG.md @@ -1,3 +1,25 @@ +## 13.4.3 + + - Update a dependency to the latest release. + +## 13.4.2 + + - Update a dependency to the latest release. + +## 13.4.1 + + - Update a dependency to the latest release. + +## 13.4.0 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FIX**(storage,android): fix an issue that could happen when app would get detached from the engine ([#18255](https://github.com/firebase/flutterfire/issues/18255)). ([2771f550](https://github.com/firebase/flutterfire/commit/2771f5505ff0a53cc1bfb41afec3a0eb8781b8f8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 13.3.0 + + - **FEAT**: use local firebase_core instead of remote SPM dependency ([#18141](https://github.com/firebase/flutterfire/issues/18141)). ([995caf40](https://github.com/firebase/flutterfire/commit/995caf400df80c0fde7151c651ccc6c0f756e381)) + ## 13.2.0 - **FIX**(storage,iOS): guard `useStorageEmulator` to prevent crash on hot restart ([#18116](https://github.com/firebase/flutterfire/issues/18116)). ([9919bf03](https://github.com/firebase/flutterfire/commit/9919bf035226a4b066ac1ef52859d5349eff61c6)) diff --git a/packages/firebase_storage/firebase_storage/android/build.gradle b/packages/firebase_storage/firebase_storage/android/build.gradle index c9e5b5e5940d..f041bc40275b 100755 --- a/packages/firebase_storage/firebase_storage/android/build.gradle +++ b/packages/firebase_storage/firebase_storage/android/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'com.android.library' apply from: file("local-config.gradle") buildscript { - ext.kotlin_version = "1.8.22" + ext.kotlin_version = "2.0.0" repositories { google() mavenCentral() @@ -44,9 +44,13 @@ def getRootProjectExtOrCoreProperty(name, firebaseCoreProject) { return rootProject.ext.get('FlutterFire').get(name) } -// AGP 9+ has built-in Kotlin support; older versions need the plugin explicitly. +// AGP 9+ has built-in Kotlin support unless Flutter opts out via android.builtInKotlin=false. def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int -if (agpMajor < 9) { +def builtInKotlin = providers.gradleProperty("android.builtInKotlin") + .map { it.toBoolean() } + .orElse(agpMajor >= 9) + .get() +if (agpMajor < 9 || !builtInKotlin) { apply plugin: 'kotlin-android' } @@ -63,12 +67,6 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - if (agpMajor < 9) { - kotlinOptions { - jvmTarget = project.ext.javaVersion - } - } - compileOptions { sourceCompatibility project.ext.javaVersion targetCompatibility project.ext.javaVersion @@ -95,4 +93,12 @@ android { } } +plugins.withId("org.jetbrains.kotlin.android") { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.fromTarget(project.ext.javaVersion.toString()) + } + } +} + apply from: file("./user-agent.gradle") diff --git a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseAppRegistrar.kt b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseAppRegistrar.kt index 424f5d7c8ffb..3b59c3d7c919 100644 --- a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseAppRegistrar.kt +++ b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseAppRegistrar.kt @@ -14,9 +14,6 @@ import com.google.firebase.platforminfo.LibraryVersionComponent class FlutterFirebaseAppRegistrar : ComponentRegistrar { override fun getComponents(): List> { return listOf( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION) - ) + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION)) } } - - diff --git a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageException.kt b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageException.kt index db671035ee96..50cb7ce1d949 100644 --- a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageException.kt +++ b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageException.kt @@ -12,11 +12,7 @@ internal object FlutterFirebaseStorageException { @JvmStatic fun parserExceptionToFlutter(@Nullable nativeException: Exception?): FlutterError { if (nativeException == null) { - return FlutterError( - "UNKNOWN", - "An unknown error occurred", - null - ) + return FlutterError("UNKNOWN", "An unknown error occurred", null) } var code = "UNKNOWN" @@ -55,16 +51,19 @@ internal object FlutterFirebaseStorageException { StorageException.ERROR_OBJECT_NOT_FOUND -> "No object exists at the desired reference." StorageException.ERROR_BUCKET_NOT_FOUND -> "No bucket is configured for Firebase Storage." StorageException.ERROR_PROJECT_NOT_FOUND -> "No project is configured for Firebase Storage." - StorageException.ERROR_QUOTA_EXCEEDED -> "Quota on your Firebase Storage bucket has been exceeded." - StorageException.ERROR_NOT_AUTHENTICATED -> "User is unauthenticated. Authenticate and try again." - StorageException.ERROR_NOT_AUTHORIZED -> "User is not authorized to perform the desired action." - StorageException.ERROR_RETRY_LIMIT_EXCEEDED -> "The maximum time limit on an operation (upload, download, delete, etc.) has been exceeded." - StorageException.ERROR_INVALID_CHECKSUM -> "File on the client does not match the checksum of the file received by the server." + StorageException.ERROR_QUOTA_EXCEEDED -> + "Quota on your Firebase Storage bucket has been exceeded." + StorageException.ERROR_NOT_AUTHENTICATED -> + "User is unauthenticated. Authenticate and try again." + StorageException.ERROR_NOT_AUTHORIZED -> + "User is not authorized to perform the desired action." + StorageException.ERROR_RETRY_LIMIT_EXCEEDED -> + "The maximum time limit on an operation (upload, download, delete, etc.) has been exceeded." + StorageException.ERROR_INVALID_CHECKSUM -> + "File on the client does not match the checksum of the file received by the server." StorageException.ERROR_CANCELED -> "User cancelled the operation." StorageException.ERROR_UNKNOWN -> "An unknown error occurred" else -> "An unknown error occurred" } } } - - diff --git a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStoragePlugin.kt b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStoragePlugin.kt index 7453d5040713..8a632ceb75f2 100644 --- a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStoragePlugin.kt +++ b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStoragePlugin.kt @@ -7,8 +7,6 @@ package io.flutter.plugins.firebase.storage import android.net.Uri import android.util.Base64 -import androidx.annotation.NonNull -import androidx.annotation.Nullable import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.TaskCompletionSource import com.google.firebase.FirebaseApp @@ -53,7 +51,11 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb this.messenger = messenger } - private fun registerEventChannel(prefix: String, identifier: String, handler: StreamHandler): String { + private fun registerEventChannel( + prefix: String, + identifier: String, + handler: StreamHandler + ): String { val channelName = "$prefix/$identifier" val channel = EventChannel(messenger, channelName) channel.setStreamHandler(handler) @@ -81,45 +83,46 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } } - private fun getStorageFromPigeon(app: PigeonStorageFirebaseApp): FirebaseStorage { + private fun getStorageFromPigeon(app: InternalStorageFirebaseApp): FirebaseStorage { val androidApp = FirebaseApp.getInstance(app.appName) return FirebaseStorage.getInstance(androidApp, "gs://${app.bucket}") } private fun getReferenceFromPigeon( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference + app: InternalStorageFirebaseApp, + reference: InternalStorageReference ): StorageReference { val androidStorage = getStorageFromPigeon(app) return androidStorage.getReference(reference.fullPath) } - private fun convertToPigeonReference(reference: StorageReference): PigeonStorageReference { - return PigeonStorageReference( - bucket = reference.bucket, - fullPath = reference.path, - name = reference.name - ) + private fun convertToPigeonReference(reference: StorageReference): InternalStorageReference { + return InternalStorageReference( + bucket = reference.bucket, fullPath = reference.path, name = reference.name) } - private fun convertToPigeonMetaData(storageMetadata: StorageMetadata?): PigeonFullMetaData { - return PigeonFullMetaData(metadata = parseMetadataToMap(storageMetadata)) + private fun convertToPigeonMetaData(storageMetadata: StorageMetadata?): InternalFullMetaData { + return InternalFullMetaData(metadata = parseMetadataToMap(storageMetadata)) } - private fun convertToPigeonListResult(listResult: ListResult): PigeonListResult { + private fun convertToPigeonListResult(listResult: ListResult): InternalListResult { val items = listResult.items.map { convertToPigeonReference(it) } val prefixes = listResult.prefixes.map { convertToPigeonReference(it) } - return PigeonListResult(items = items, pageToken = listResult.pageToken, prefixs = prefixes) + return InternalListResult(items = items, pageToken = listResult.pageToken, prefixs = prefixes) } - private fun getMetaDataFromPigeon(pigeonSettableMetatdata: PigeonSettableMetadata): StorageMetadata { + private fun getMetaDataFromPigeon( + pigeonSettableMetatdata: InternalSettableMetadata + ): StorageMetadata { val builder = StorageMetadata.Builder() pigeonSettableMetatdata.contentType?.let { builder.setContentType(it) } pigeonSettableMetatdata.cacheControl?.let { builder.setCacheControl(it) } pigeonSettableMetatdata.contentDisposition?.let { builder.setContentDisposition(it) } pigeonSettableMetatdata.contentEncoding?.let { builder.setContentEncoding(it) } pigeonSettableMetatdata.contentLanguage?.let { builder.setContentLanguage(it) } - pigeonSettableMetatdata.customMetadata?.forEach { (k, v) -> if (k != null && v != null) builder.setCustomMetadata(k, v) } + pigeonSettableMetatdata.customMetadata?.forEach { (k, v) -> + if (k != null && v != null) builder.setCustomMetadata(k, v) + } return builder.build() } @@ -132,20 +135,20 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } override fun getReferencebyPath( - app: PigeonStorageFirebaseApp, - path: String, - bucket: String?, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + path: String, + bucket: String?, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(path) callback(Result.success(convertToPigeonReference(androidReference))) } override fun useStorageEmulator( - app: PigeonStorageFirebaseApp, - host: String, - port: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + host: String, + port: Long, + callback: (Result) -> Unit ) { try { val storage = getStorageFromPigeon(app) @@ -157,185 +160,223 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } override fun referenceDelete( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(reference.fullPath) androidReference.delete().addOnCompleteListener { task -> if (task.isSuccessful) callback(Result.success(Unit)) - else callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) + else + callback( + Result.failure( + FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) } } override fun referenceGetDownloadURL( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(reference.fullPath) androidReference.downloadUrl.addOnCompleteListener { task -> if (task.isSuccessful) callback(Result.success(task.result.toString())) - else callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) + else + callback( + Result.failure( + FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) } } override fun referenceGetData( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - maxSize: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + maxSize: Long, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(reference.fullPath) androidReference.getBytes(maxSize).addOnCompleteListener { task -> if (task.isSuccessful) callback(Result.success(task.result)) - else callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) + else + callback( + Result.failure( + FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) } } override fun referenceGetMetaData( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(reference.fullPath) androidReference.metadata.addOnCompleteListener { task -> if (task.isSuccessful) callback(Result.success(convertToPigeonMetaData(task.result))) - else callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) + else + callback( + Result.failure( + FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) } } override fun referenceList( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - options: PigeonListOptions, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + options: InternalListOptions, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(reference.fullPath) - val task = if (options.pageToken != null) { - androidReference.list(options.maxResults.toInt(), options.pageToken) - } else { - androidReference.list(options.maxResults.toInt()) - } + val task = + if (options.pageToken != null) { + androidReference.list(options.maxResults.toInt(), options.pageToken) + } else { + androidReference.list(options.maxResults.toInt()) + } task.addOnCompleteListener { t -> if (t.isSuccessful) callback(Result.success(convertToPigeonListResult(t.result))) - else callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(t.exception))) + else + callback( + Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(t.exception))) } } override fun referenceListAll( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(reference.fullPath) androidReference.listAll().addOnCompleteListener { task -> if (task.isSuccessful) callback(Result.success(convertToPigeonListResult(task.result))) - else callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) + else + callback( + Result.failure( + FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) } } override fun referenceUpdateMetadata( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - metadata: PigeonSettableMetadata, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + metadata: InternalSettableMetadata, + callback: (Result) -> Unit ) { val androidReference = getStorageFromPigeon(app).getReference(reference.fullPath) androidReference.updateMetadata(getMetaDataFromPigeon(metadata)).addOnCompleteListener { task -> if (task.isSuccessful) callback(Result.success(convertToPigeonMetaData(task.result))) - else callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) + else + callback( + Result.failure( + FlutterFirebaseStorageException.parserExceptionToFlutter(task.exception))) } } override fun referencePutData( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - data: ByteArray, - settableMetaData: PigeonSettableMetadata, - handle: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + data: ByteArray, + settableMetaData: InternalSettableMetadata, + handle: Long, + callback: (Result) -> Unit ) { val androidReference = getReferenceFromPigeon(app, reference) val androidMetaData = getMetaDataFromPigeon(settableMetaData) - val storageTask = FlutterFirebaseStorageTask.uploadBytes(handle.toInt(), androidReference, data, androidMetaData) + val storageTask = + FlutterFirebaseStorageTask.uploadBytes( + handle.toInt(), androidReference, data, androidMetaData) try { val identifier = UUID.randomUUID().toString().lowercase(Locale.US) val handler = storageTask.startTaskWithMethodChannel(channel!!, identifier) - callback(Result.success(registerEventChannel("$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) + callback( + Result.success( + registerEventChannel( + "$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) } catch (e: Exception) { callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(e))) } } override fun referencePutString( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - data: String, - format: Long, - settableMetaData: PigeonSettableMetadata, - handle: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + data: String, + format: Long, + settableMetaData: InternalSettableMetadata, + handle: Long, + callback: (Result) -> Unit ) { val androidReference = getReferenceFromPigeon(app, reference) val androidMetaData = getMetaDataFromPigeon(settableMetaData) val bytes = stringToByteData(data, format.toInt()) - val storageTask = FlutterFirebaseStorageTask.uploadBytes(handle.toInt(), androidReference, bytes!!, androidMetaData) + val storageTask = + FlutterFirebaseStorageTask.uploadBytes( + handle.toInt(), androidReference, bytes!!, androidMetaData) try { val identifier = UUID.randomUUID().toString().lowercase(Locale.US) val handler = storageTask.startTaskWithMethodChannel(channel!!, identifier) - callback(Result.success(registerEventChannel("$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) + callback( + Result.success( + registerEventChannel( + "$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) } catch (e: Exception) { callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(e))) } } override fun referencePutFile( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - filePath: String, - settableMetaData: PigeonSettableMetadata?, - handle: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + filePath: String, + settableMetaData: InternalSettableMetadata?, + handle: Long, + callback: (Result) -> Unit ) { val androidReference = getReferenceFromPigeon(app, reference) - val storageTask = FlutterFirebaseStorageTask.uploadFile( - handle.toInt(), - androidReference, - Uri.fromFile(File(filePath)), - settableMetaData?.let { getMetaDataFromPigeon(it) } - ) + val storageTask = + FlutterFirebaseStorageTask.uploadFile( + handle.toInt(), + androidReference, + Uri.fromFile(File(filePath)), + settableMetaData?.let { getMetaDataFromPigeon(it) }) try { val identifier = UUID.randomUUID().toString().lowercase(Locale.US) val handler = storageTask.startTaskWithMethodChannel(channel!!, identifier) - callback(Result.success(registerEventChannel("$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) + callback( + Result.success( + registerEventChannel( + "$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) } catch (e: Exception) { callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(e))) } } override fun referenceDownloadFile( - app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference, - filePath: String, - handle: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + filePath: String, + handle: Long, + callback: (Result) -> Unit ) { val androidReference = getReferenceFromPigeon(app, reference) - val storageTask = FlutterFirebaseStorageTask.downloadFile(handle.toInt(), androidReference, File(filePath)) + val storageTask = + FlutterFirebaseStorageTask.downloadFile(handle.toInt(), androidReference, File(filePath)) try { val identifier = UUID.randomUUID().toString().lowercase(Locale.US) val handler = storageTask.startTaskWithMethodChannel(channel!!, identifier) - callback(Result.success(registerEventChannel("$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) + callback( + Result.success( + registerEventChannel( + "$STORAGE_METHOD_CHANNEL_NAME/$STORAGE_TASK_EVENT_NAME", identifier, handler))) } catch (e: Exception) { callback(Result.failure(FlutterFirebaseStorageException.parserExceptionToFlutter(e))) } } override fun taskPause( - app: PigeonStorageFirebaseApp, - handle: Long, - callback: (Result>) -> Unit + app: InternalStorageFirebaseApp, + handle: Long, + callback: (Result>) -> Unit ) { val storageTask = FlutterFirebaseStorageTask.getInProgressTaskForHandle(handle.toInt()) if (storageTask == null) { @@ -352,7 +393,8 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb val statusMap = HashMap() statusMap["status"] = paused if (paused) { - statusMap["snapshot"] = FlutterFirebaseStorageTask.parseTaskSnapshot(storageTask.getSnapshot()) + statusMap["snapshot"] = + FlutterFirebaseStorageTask.parseTaskSnapshot(storageTask.getSnapshot()) } callback(Result.success(statusMap)) } catch (e: Exception) { @@ -361,9 +403,9 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } override fun taskResume( - app: PigeonStorageFirebaseApp, - handle: Long, - callback: (Result>) -> Unit + app: InternalStorageFirebaseApp, + handle: Long, + callback: (Result>) -> Unit ) { val storageTask = FlutterFirebaseStorageTask.getInProgressTaskForHandle(handle.toInt()) if (storageTask == null) { @@ -380,7 +422,8 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb val statusMap = HashMap() statusMap["status"] = resumed if (resumed) { - statusMap["snapshot"] = FlutterFirebaseStorageTask.parseTaskSnapshot(storageTask.getSnapshot()) + statusMap["snapshot"] = + FlutterFirebaseStorageTask.parseTaskSnapshot(storageTask.getSnapshot()) } callback(Result.success(statusMap)) } catch (e: Exception) { @@ -389,9 +432,9 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } override fun taskCancel( - app: PigeonStorageFirebaseApp, - handle: Long, - callback: (Result>) -> Unit + app: InternalStorageFirebaseApp, + handle: Long, + callback: (Result>) -> Unit ) { val storageTask = FlutterFirebaseStorageTask.getInProgressTaskForHandle(handle.toInt()) if (storageTask == null) { @@ -405,7 +448,8 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb val statusMap = HashMap() statusMap["status"] = canceled if (canceled) { - statusMap["snapshot"] = FlutterFirebaseStorageTask.parseTaskSnapshot(storageTask.getSnapshot()) + statusMap["snapshot"] = + FlutterFirebaseStorageTask.parseTaskSnapshot(storageTask.getSnapshot()) } callback(Result.success(statusMap)) } catch (e: Exception) { @@ -414,9 +458,9 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } override fun setMaxOperationRetryTime( - app: PigeonStorageFirebaseApp, - time: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + time: Long, + callback: (Result) -> Unit ) { val storage = getStorageFromPigeon(app) storage.maxOperationRetryTimeMillis = time @@ -424,9 +468,9 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } override fun setMaxUploadRetryTime( - app: PigeonStorageFirebaseApp, - time: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + time: Long, + callback: (Result) -> Unit ) { val storage = getStorageFromPigeon(app) storage.maxUploadRetryTimeMillis = time @@ -434,16 +478,18 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } override fun setMaxDownloadRetryTime( - app: PigeonStorageFirebaseApp, - time: Long, - callback: (Result) -> Unit + app: InternalStorageFirebaseApp, + time: Long, + callback: (Result) -> Unit ) { val storage = getStorageFromPigeon(app) storage.maxDownloadRetryTimeMillis = time callback(Result.success(Unit)) } - override fun getPluginConstantsForFirebaseApp(firebaseApp: FirebaseApp?): Task> { + override fun getPluginConstantsForFirebaseApp( + firebaseApp: FirebaseApp? + ): Task> { val taskCompletionSource = TaskCompletionSource>() cachedThreadPool.execute { val obj = HashMap() @@ -504,5 +550,3 @@ class FlutterFirebaseStoragePlugin : FlutterFirebasePlugin, FlutterPlugin, Fireb } } } - - diff --git a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageTask.kt b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageTask.kt index 1727bbde949c..f8cd77d739a8 100644 --- a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageTask.kt +++ b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/FlutterFirebaseStorageTask.kt @@ -8,7 +8,6 @@ package io.flutter.plugins.firebase.storage import android.net.Uri import android.util.SparseArray import androidx.annotation.NonNull -import androidx.annotation.Nullable import com.google.firebase.storage.FileDownloadTask import com.google.firebase.storage.StorageMetadata import com.google.firebase.storage.StorageReference @@ -18,13 +17,14 @@ import io.flutter.plugin.common.MethodChannel import java.io.File import java.util.HashMap -internal class FlutterFirebaseStorageTask private constructor( - private val type: FlutterFirebaseStorageTaskType, - private val handle: Int, - private val reference: StorageReference, - private val bytes: ByteArray?, - private val fileUri: Uri?, - private val metadata: StorageMetadata? +internal class FlutterFirebaseStorageTask +private constructor( + private val type: FlutterFirebaseStorageTaskType, + private val handle: Int, + private val reference: StorageReference, + private val bytes: ByteArray?, + private val fileUri: Uri?, + private val metadata: StorageMetadata? ) { private val pauseSyncObject = Object() private val resumeSyncObject = Object() @@ -36,12 +36,20 @@ internal class FlutterFirebaseStorageTask private constructor( synchronized(inProgressTasks) { inProgressTasks.put(handle, this) } } - fun startTaskWithMethodChannel(@NonNull channel: MethodChannel, @NonNull identifier: String): TaskStateChannelStreamHandler { - storageTask = when (type) { - FlutterFirebaseStorageTaskType.BYTES -> if (metadata == null) reference.putBytes(bytes!!) else reference.putBytes(bytes!!, metadata) - FlutterFirebaseStorageTaskType.FILE -> if (metadata == null) reference.putFile(fileUri!!) else reference.putFile(fileUri!!, metadata) - FlutterFirebaseStorageTaskType.DOWNLOAD -> reference.getFile(fileUri!!) - } + fun startTaskWithMethodChannel( + @NonNull channel: MethodChannel, + @NonNull identifier: String + ): TaskStateChannelStreamHandler { + storageTask = + when (type) { + FlutterFirebaseStorageTaskType.BYTES -> + if (metadata == null) reference.putBytes(bytes!!) + else reference.putBytes(bytes!!, metadata) + FlutterFirebaseStorageTaskType.FILE -> + if (metadata == null) reference.putFile(fileUri!!) + else reference.putFile(fileUri!!, metadata) + FlutterFirebaseStorageTaskType.DOWNLOAD -> reference.getFile(fileUri!!) + } return TaskStateChannelStreamHandler(this, reference.storage, storageTask as Any, identifier) } @@ -62,9 +70,17 @@ internal class FlutterFirebaseStorageTask private constructor( fun isDestroyed(): Boolean = destroyed - fun notifyResumeObjects() { synchronized(resumeSyncObject) { resumeSyncObject.notifyAll() } } - fun notifyCancelObjects() { synchronized(cancelSyncObject) { cancelSyncObject.notifyAll() } } - fun notifyPauseObjects() { synchronized(pauseSyncObject) { pauseSyncObject.notifyAll() } } + fun notifyResumeObjects() { + synchronized(resumeSyncObject) { resumeSyncObject.notifyAll() } + } + + fun notifyCancelObjects() { + synchronized(cancelSyncObject) { cancelSyncObject.notifyAll() } + } + + fun notifyPauseObjects() { + synchronized(pauseSyncObject) { pauseSyncObject.notifyAll() } + } // Intentionally do not expose the StorageTask generic type outside this class @@ -89,33 +105,59 @@ internal class FlutterFirebaseStorageTask private constructor( @JvmStatic fun getInProgressTaskForHandle(handle: Int): FlutterFirebaseStorageTask? { - synchronized(inProgressTasks) { return inProgressTasks.get(handle) } + synchronized(inProgressTasks) { + return inProgressTasks.get(handle) + } } @JvmStatic fun cancelInProgressTasks() { synchronized(inProgressTasks) { + val tasks = ArrayList(inProgressTasks.size()) for (i in 0 until inProgressTasks.size()) { val task: FlutterFirebaseStorageTask? = inProgressTasks.valueAt(i) - task?.destroy() + task?.let { tasks.add(it) } } inProgressTasks.clear() + tasks.forEach { it.destroy() } } } @JvmStatic - fun uploadBytes(handle: Int, reference: StorageReference, data: ByteArray, metadata: StorageMetadata?): FlutterFirebaseStorageTask { - return FlutterFirebaseStorageTask(FlutterFirebaseStorageTaskType.BYTES, handle, reference, data, null, metadata) + fun uploadBytes( + handle: Int, + reference: StorageReference, + data: ByteArray, + metadata: StorageMetadata? + ): FlutterFirebaseStorageTask { + return FlutterFirebaseStorageTask( + FlutterFirebaseStorageTaskType.BYTES, handle, reference, data, null, metadata) } @JvmStatic - fun uploadFile(handle: Int, reference: StorageReference, fileUri: Uri, metadata: StorageMetadata?): FlutterFirebaseStorageTask { - return FlutterFirebaseStorageTask(FlutterFirebaseStorageTaskType.FILE, handle, reference, null, fileUri, metadata) + fun uploadFile( + handle: Int, + reference: StorageReference, + fileUri: Uri, + metadata: StorageMetadata? + ): FlutterFirebaseStorageTask { + return FlutterFirebaseStorageTask( + FlutterFirebaseStorageTaskType.FILE, handle, reference, null, fileUri, metadata) } @JvmStatic - fun downloadFile(handle: Int, reference: StorageReference, file: File): FlutterFirebaseStorageTask { - return FlutterFirebaseStorageTask(FlutterFirebaseStorageTaskType.DOWNLOAD, handle, reference, null, Uri.fromFile(file), null) + fun downloadFile( + handle: Int, + reference: StorageReference, + file: File + ): FlutterFirebaseStorageTask { + return FlutterFirebaseStorageTask( + FlutterFirebaseStorageTaskType.DOWNLOAD, + handle, + reference, + null, + Uri.fromFile(file), + null) } @JvmStatic @@ -135,7 +177,8 @@ internal class FlutterFirebaseStorageTask private constructor( val out: MutableMap = HashMap() out["path"] = snapshot.storage.path // Workaround: sometimes getBytesTransferred != getTotalByteCount when completed - out["bytesTransferred"] = if (snapshot.task.isSuccessful) snapshot.totalByteCount else snapshot.bytesTransferred + out["bytesTransferred"] = + if (snapshot.task.isSuccessful) snapshot.totalByteCount else snapshot.bytesTransferred out["totalBytes"] = snapshot.totalByteCount return out } @@ -156,5 +199,3 @@ internal class FlutterFirebaseStorageTask private constructor( DOWNLOAD } } - - diff --git a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/GeneratedAndroidFirebaseStorage.g.kt b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/GeneratedAndroidFirebaseStorage.g.kt index 66149daa3d82..3e2741a89caa 100644 --- a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/GeneratedAndroidFirebaseStorage.g.kt +++ b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/GeneratedAndroidFirebaseStorage.g.kt @@ -1,8 +1,9 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") package io.flutter.plugins.firebase.storage @@ -14,43 +15,183 @@ import io.flutter.plugin.common.StandardMessageCodec import java.io.ByteArrayOutputStream import java.nio.ByteBuffer -private fun wrapResult(result: Any?): List { - return listOf(result) -} +private object GeneratedAndroidFirebaseStoragePigeonUtils { -private fun wrapError(exception: Throwable): List { - if (exception is FlutterError) { - return listOf( - exception.code, - exception.message, - exception.details - ) - } else { - return listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf(exception.code, exception.message, exception.details) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) + } + } + + fun doubleEquals(a: Double, b: Double): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN()) + } + + fun floatEquals(a: Float, b: Float): Boolean { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN()) + } + + fun doubleHash(d: Double): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (d == 0.0) 0.0 else d + val bits = java.lang.Double.doubleToLongBits(normalized) + return (bits xor (bits ushr 32)).toInt() + } + + fun floatHash(f: Float): Int { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + val normalized = if (f == 0.0f) 0.0f else f + return java.lang.Float.floatToIntBits(normalized) + } + + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a === b) { + return true + } + if (a == null || b == null) { + return false + } + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + if (a.size != b.size) return false + for (i in a.indices) { + if (!doubleEquals(a[i], b[i])) return false + } + return true + } + if (a is FloatArray && b is FloatArray) { + if (a.size != b.size) return false + for (i in a.indices) { + if (!floatEquals(a[i], b[i])) return false + } + return true + } + if (a is Array<*> && b is Array<*>) { + if (a.size != b.size) return false + for (i in a.indices) { + if (!deepEquals(a[i], b[i])) return false + } + return true + } + if (a is List<*> && b is List<*>) { + if (a.size != b.size) return false + val iterA = a.iterator() + val iterB = b.iterator() + while (iterA.hasNext() && iterB.hasNext()) { + if (!deepEquals(iterA.next(), iterB.next())) return false + } + return true + } + if (a is Map<*, *> && b is Map<*, *>) { + if (a.size != b.size) return false + for (entry in a) { + val key = entry.key + var found = false + for (bEntry in b) { + if (deepEquals(key, bEntry.key)) { + if (deepEquals(entry.value, bEntry.value)) { + found = true + break + } else { + return false + } + } + } + if (!found) return false + } + return true + } + if (a is Double && b is Double) { + return doubleEquals(a, b) + } + if (a is Float && b is Float) { + return floatEquals(a, b) + } + return a == b + } + + fun deepHash(value: Any?): Int { + return when (value) { + null -> 0 + is ByteArray -> value.contentHashCode() + is IntArray -> value.contentHashCode() + is LongArray -> value.contentHashCode() + is DoubleArray -> { + var result = 1 + for (item in value) { + result = 31 * result + doubleHash(item) + } + result + } + is FloatArray -> { + var result = 1 + for (item in value) { + result = 31 * result + floatHash(item) + } + result + } + is Array<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is List<*> -> { + var result = 1 + for (item in value) { + result = 31 * result + deepHash(item) + } + result + } + is Map<*, *> -> { + var result = 0 + for (entry in value) { + result += ((deepHash(entry.key) * 31) xor deepHash(entry.value)) + } + result + } + is Double -> doubleHash(value) + is Float -> floatHash(value) + else -> value.hashCode() + } } } /** * 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 FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() -/** - * The type of operation that generated the action code from calling - * [TaskState]. - */ -enum class PigeonStorageTaskState(val raw: Int) { +/** The type of operation that generated the action code from calling [TaskState]. */ +enum class InternalStorageTaskState(val raw: Int) { /** Indicates the task has been paused by the user. */ PAUSED(0), /** Indicates the task is currently in-progress. */ @@ -63,261 +204,443 @@ enum class PigeonStorageTaskState(val raw: Int) { ERROR(4); companion object { - fun ofRaw(raw: Int): PigeonStorageTaskState? { + fun ofRaw(raw: Int): InternalStorageTaskState? { return values().firstOrNull { it.raw == raw } } } } /** Generated class from Pigeon that represents data sent in messages. */ -data class PigeonStorageFirebaseApp ( - val appName: String, - val tenantId: String? = null, - val bucket: String - +data class InternalStorageFirebaseApp( + val appName: String, + val tenantId: String? = null, + val bucket: String ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PigeonStorageFirebaseApp { - val appName = list[0] as String - val tenantId = list[1] as String? - val bucket = list[2] as String - return PigeonStorageFirebaseApp(appName, tenantId, bucket) + fun fromList(pigeonVar_list: List): InternalStorageFirebaseApp { + val appName = pigeonVar_list[0] as String + val tenantId = pigeonVar_list[1] as String? + val bucket = pigeonVar_list[2] as String + return InternalStorageFirebaseApp(appName, tenantId, bucket) } } + fun toList(): List { - return listOf( - appName, - tenantId, - bucket, + return listOf( + appName, + tenantId, + bucket, ) } + + override fun equals(other: Any?): Boolean { + if (other == null || other.javaClass != javaClass) { + return false + } + if (this === other) { + return true + } + val other = other as InternalStorageFirebaseApp + return GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.appName, other.appName) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.tenantId, other.tenantId) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.bucket, other.bucket) + } + + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.appName) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.tenantId) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.bucket) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class PigeonStorageReference ( - val bucket: String, - val fullPath: String, - val name: String - -) { +data class InternalStorageReference(val bucket: String, val fullPath: String, val name: String) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PigeonStorageReference { - val bucket = list[0] as String - val fullPath = list[1] as String - val name = list[2] as String - return PigeonStorageReference(bucket, fullPath, name) + fun fromList(pigeonVar_list: List): InternalStorageReference { + val bucket = pigeonVar_list[0] as String + val fullPath = pigeonVar_list[1] as String + val name = pigeonVar_list[2] as String + return InternalStorageReference(bucket, fullPath, name) } } + fun toList(): List { - return listOf( - bucket, - fullPath, - name, + return listOf( + bucket, + fullPath, + name, ) } + + override fun equals(other: Any?): Boolean { + if (other == null || other.javaClass != javaClass) { + return false + } + if (this === other) { + return true + } + val other = other as InternalStorageReference + return GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.bucket, other.bucket) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.fullPath, other.fullPath) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.name, other.name) + } + + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.bucket) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.fullPath) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.name) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class PigeonFullMetaData ( - val metadata: Map? = null +data class InternalFullMetaData(val metadata: Map? = null) { + companion object { + fun fromList(pigeonVar_list: List): InternalFullMetaData { + val metadata = pigeonVar_list[0] as Map? + return InternalFullMetaData(metadata) + } + } + + fun toList(): List { + return listOf( + metadata, + ) + } + + override fun equals(other: Any?): Boolean { + if (other == null || other.javaClass != javaClass) { + return false + } + if (this === other) { + return true + } + val other = other as InternalFullMetaData + return GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.metadata, other.metadata) + } + + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.metadata) + return result + } +} +/** Generated class from Pigeon that represents data sent in messages. */ +data class InternalListOptions( + /** + * If set, limits the total number of `prefixes` and `items` to return. + * + * The default and maximum maxResults is 1000. + */ + val maxResults: Long, + /** + * The nextPageToken from a previous call to list(). + * + * If provided, listing is resumed from the previous position. + */ + val pageToken: String? = null ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PigeonFullMetaData { - val metadata = list[0] as Map? - return PigeonFullMetaData(metadata) + fun fromList(pigeonVar_list: List): InternalListOptions { + val maxResults = pigeonVar_list[0] as Long + val pageToken = pigeonVar_list[1] as String? + return InternalListOptions(maxResults, pageToken) } } + fun toList(): List { - return listOf( - metadata, + return listOf( + maxResults, + pageToken, ) } + + override fun equals(other: Any?): Boolean { + if (other == null || other.javaClass != javaClass) { + return false + } + if (this === other) { + return true + } + val other = other as InternalListOptions + return GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.maxResults, other.maxResults) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.pageToken, other.pageToken) + } + + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.maxResults) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.pageToken) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class PigeonListOptions ( - /** - * If set, limits the total number of `prefixes` and `items` to return. - * - * The default and maximum maxResults is 1000. - */ - val maxResults: Long, - /** - * The nextPageToken from a previous call to list(). - * - * If provided, listing is resumed from the previous position. - */ - val pageToken: String? = null - +data class InternalSettableMetadata( + /** + * Served as the 'Cache-Control' header on object download. + * + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control. + */ + val cacheControl: String? = null, + /** + * Served as the 'Content-Disposition' header on object download. + * + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition. + */ + val contentDisposition: String? = null, + /** + * Served as the 'Content-Encoding' header on object download. + * + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding. + */ + val contentEncoding: String? = null, + /** + * Served as the 'Content-Language' header on object download. + * + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language. + */ + val contentLanguage: String? = null, + /** + * Served as the 'Content-Type' header on object download. + * + * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type. + */ + val contentType: String? = null, + /** Additional user-defined custom metadata. */ + val customMetadata: Map? = null ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PigeonListOptions { - val maxResults = list[0].let { if (it is Int) it.toLong() else it as Long } - val pageToken = list[1] as String? - return PigeonListOptions(maxResults, pageToken) + fun fromList(pigeonVar_list: List): InternalSettableMetadata { + val cacheControl = pigeonVar_list[0] as String? + val contentDisposition = pigeonVar_list[1] as String? + val contentEncoding = pigeonVar_list[2] as String? + val contentLanguage = pigeonVar_list[3] as String? + val contentType = pigeonVar_list[4] as String? + val customMetadata = pigeonVar_list[5] as Map? + return InternalSettableMetadata( + cacheControl, + contentDisposition, + contentEncoding, + contentLanguage, + contentType, + customMetadata) } } + fun toList(): List { - return listOf( - maxResults, - pageToken, + return listOf( + cacheControl, + contentDisposition, + contentEncoding, + contentLanguage, + contentType, + customMetadata, ) } + + override fun equals(other: Any?): Boolean { + if (other == null || other.javaClass != javaClass) { + return false + } + if (this === other) { + return true + } + val other = other as InternalSettableMetadata + return GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.cacheControl, other.cacheControl) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.contentDisposition, other.contentDisposition) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.contentEncoding, other.contentEncoding) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.contentLanguage, other.contentLanguage) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.contentType, other.contentType) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.customMetadata, other.customMetadata) + } + + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.cacheControl) + result = + 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.contentDisposition) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.contentEncoding) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.contentLanguage) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.contentType) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.customMetadata) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class PigeonSettableMetadata ( - /** - * Served as the 'Cache-Control' header on object download. - * - * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control. - */ - val cacheControl: String? = null, - /** - * Served as the 'Content-Disposition' header on object download. - * - * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition. - */ - val contentDisposition: String? = null, - /** - * Served as the 'Content-Encoding' header on object download. - * - * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding. - */ - val contentEncoding: String? = null, - /** - * Served as the 'Content-Language' header on object download. - * - * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language. - */ - val contentLanguage: String? = null, - /** - * Served as the 'Content-Type' header on object download. - * - * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type. - */ - val contentType: String? = null, - /** Additional user-defined custom metadata. */ - val customMetadata: Map? = null - +data class InternalStorageTaskSnapShot( + val bytesTransferred: Long, + val metadata: InternalFullMetaData? = null, + val state: InternalStorageTaskState, + val totalBytes: Long ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PigeonSettableMetadata { - val cacheControl = list[0] as String? - val contentDisposition = list[1] as String? - val contentEncoding = list[2] as String? - val contentLanguage = list[3] as String? - val contentType = list[4] as String? - val customMetadata = list[5] as Map? - return PigeonSettableMetadata(cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType, customMetadata) + fun fromList(pigeonVar_list: List): InternalStorageTaskSnapShot { + val bytesTransferred = pigeonVar_list[0] as Long + val metadata = pigeonVar_list[1] as InternalFullMetaData? + val state = pigeonVar_list[2] as InternalStorageTaskState + val totalBytes = pigeonVar_list[3] as Long + return InternalStorageTaskSnapShot(bytesTransferred, metadata, state, totalBytes) } } + fun toList(): List { - return listOf( - cacheControl, - contentDisposition, - contentEncoding, - contentLanguage, - contentType, - customMetadata, + return listOf( + bytesTransferred, + metadata, + state, + totalBytes, ) } + + override fun equals(other: Any?): Boolean { + if (other == null || other.javaClass != javaClass) { + return false + } + if (this === other) { + return true + } + val other = other as InternalStorageTaskSnapShot + return GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals( + this.bytesTransferred, other.bytesTransferred) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.metadata, other.metadata) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.state, other.state) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.totalBytes, other.totalBytes) + } + + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = + 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.bytesTransferred) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.metadata) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.state) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.totalBytes) + return result + } } /** Generated class from Pigeon that represents data sent in messages. */ -data class PigeonListResult ( - val items: List, - val pageToken: String? = null, - val prefixs: List - +data class InternalListResult( + val items: List, + val pageToken: String? = null, + val prefixs: List ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): PigeonListResult { - val items = list[0] as List - val pageToken = list[1] as String? - val prefixs = list[2] as List - return PigeonListResult(items, pageToken, prefixs) + fun fromList(pigeonVar_list: List): InternalListResult { + val items = pigeonVar_list[0] as List + val pageToken = pigeonVar_list[1] as String? + val prefixs = pigeonVar_list[2] as List + return InternalListResult(items, pageToken, prefixs) } } + fun toList(): List { - return listOf( - items, - pageToken, - prefixs, + return listOf( + items, + pageToken, + prefixs, ) } + + override fun equals(other: Any?): Boolean { + if (other == null || other.javaClass != javaClass) { + return false + } + if (this === other) { + return true + } + val other = other as InternalListResult + return GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.items, other.items) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.pageToken, other.pageToken) && + GeneratedAndroidFirebaseStoragePigeonUtils.deepEquals(this.prefixs, other.prefixs) + } + + override fun hashCode(): Int { + var result = javaClass.hashCode() + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.items) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.pageToken) + result = 31 * result + GeneratedAndroidFirebaseStoragePigeonUtils.deepHash(this.prefixs) + return result + } } -@Suppress("UNCHECKED_CAST") -private object FirebaseStorageHostApiCodec : StandardMessageCodec() { +private open class GeneratedAndroidFirebaseStoragePigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { - 128.toByte() -> { - return (readValue(buffer) as? List)?.let { - PigeonFullMetaData.fromList(it) - } - } 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - PigeonListOptions.fromList(it) - } + return (readValue(buffer) as Long?)?.let { InternalStorageTaskState.ofRaw(it.toInt()) } } 130.toByte() -> { - return (readValue(buffer) as? List)?.let { - PigeonListResult.fromList(it) - } + return (readValue(buffer) as? List)?.let { InternalStorageFirebaseApp.fromList(it) } } 131.toByte() -> { - return (readValue(buffer) as? List)?.let { - PigeonSettableMetadata.fromList(it) - } + return (readValue(buffer) as? List)?.let { InternalStorageReference.fromList(it) } } 132.toByte() -> { - return (readValue(buffer) as? List)?.let { - PigeonStorageFirebaseApp.fromList(it) - } + return (readValue(buffer) as? List)?.let { InternalFullMetaData.fromList(it) } } 133.toByte() -> { - return (readValue(buffer) as? List)?.let { - PigeonStorageReference.fromList(it) - } + return (readValue(buffer) as? List)?.let { InternalListOptions.fromList(it) } + } + 134.toByte() -> { + return (readValue(buffer) as? List)?.let { InternalSettableMetadata.fromList(it) } + } + 135.toByte() -> { + return (readValue(buffer) as? List)?.let { InternalStorageTaskSnapShot.fromList(it) } + } + 136.toByte() -> { + return (readValue(buffer) as? List)?.let { InternalListResult.fromList(it) } } else -> super.readValueOfType(type, buffer) } } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { - is PigeonFullMetaData -> { - stream.write(128) - writeValue(stream, value.toList()) - } - is PigeonListOptions -> { + is InternalStorageTaskState -> { stream.write(129) - writeValue(stream, value.toList()) + writeValue(stream, value.raw.toLong()) } - is PigeonListResult -> { + is InternalStorageFirebaseApp -> { stream.write(130) writeValue(stream, value.toList()) } - is PigeonSettableMetadata -> { + is InternalStorageReference -> { stream.write(131) writeValue(stream, value.toList()) } - is PigeonStorageFirebaseApp -> { + is InternalFullMetaData -> { stream.write(132) writeValue(stream, value.toList()) } - is PigeonStorageReference -> { + is InternalListOptions -> { stream.write(133) writeValue(stream, value.toList()) } + is InternalSettableMetadata -> { + stream.write(134) + writeValue(stream, value.toList()) + } + is InternalStorageTaskSnapShot -> { + stream.write(135) + writeValue(stream, value.toList()) + } + is InternalListResult -> { + stream.write(136) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -325,49 +648,172 @@ private object FirebaseStorageHostApiCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface FirebaseStorageHostApi { - fun getReferencebyPath(app: PigeonStorageFirebaseApp, path: String, bucket: String?, callback: (Result) -> Unit) - fun setMaxOperationRetryTime(app: PigeonStorageFirebaseApp, time: Long, callback: (Result) -> Unit) - fun setMaxUploadRetryTime(app: PigeonStorageFirebaseApp, time: Long, callback: (Result) -> Unit) - fun setMaxDownloadRetryTime(app: PigeonStorageFirebaseApp, time: Long, callback: (Result) -> Unit) - fun useStorageEmulator(app: PigeonStorageFirebaseApp, host: String, port: Long, callback: (Result) -> Unit) - fun referenceDelete(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, callback: (Result) -> Unit) - fun referenceGetDownloadURL(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, callback: (Result) -> Unit) - fun referenceGetMetaData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, callback: (Result) -> Unit) - fun referenceList(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, options: PigeonListOptions, callback: (Result) -> Unit) - fun referenceListAll(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, callback: (Result) -> Unit) - fun referenceGetData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, maxSize: Long, callback: (Result) -> Unit) - fun referencePutData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, data: ByteArray, settableMetaData: PigeonSettableMetadata, handle: Long, callback: (Result) -> Unit) - fun referencePutString(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, data: String, format: Long, settableMetaData: PigeonSettableMetadata, handle: Long, callback: (Result) -> Unit) - fun referencePutFile(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, filePath: String, settableMetaData: PigeonSettableMetadata?, handle: Long, callback: (Result) -> Unit) - fun referenceDownloadFile(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, filePath: String, handle: Long, callback: (Result) -> Unit) - fun referenceUpdateMetadata(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, metadata: PigeonSettableMetadata, callback: (Result) -> Unit) - fun taskPause(app: PigeonStorageFirebaseApp, handle: Long, callback: (Result>) -> Unit) - fun taskResume(app: PigeonStorageFirebaseApp, handle: Long, callback: (Result>) -> Unit) - fun taskCancel(app: PigeonStorageFirebaseApp, handle: Long, callback: (Result>) -> Unit) + fun getReferencebyPath( + app: InternalStorageFirebaseApp, + path: String, + bucket: String?, + callback: (Result) -> Unit + ) + + fun setMaxOperationRetryTime( + app: InternalStorageFirebaseApp, + time: Long, + callback: (Result) -> Unit + ) + + fun setMaxUploadRetryTime( + app: InternalStorageFirebaseApp, + time: Long, + callback: (Result) -> Unit + ) + + fun setMaxDownloadRetryTime( + app: InternalStorageFirebaseApp, + time: Long, + callback: (Result) -> Unit + ) + + fun useStorageEmulator( + app: InternalStorageFirebaseApp, + host: String, + port: Long, + callback: (Result) -> Unit + ) + + fun referenceDelete( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit + ) + + fun referenceGetDownloadURL( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit + ) + + fun referenceGetMetaData( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit + ) + + fun referenceList( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + options: InternalListOptions, + callback: (Result) -> Unit + ) + + fun referenceListAll( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + callback: (Result) -> Unit + ) + + fun referenceGetData( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + maxSize: Long, + callback: (Result) -> Unit + ) + + fun referencePutData( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + data: ByteArray, + settableMetaData: InternalSettableMetadata, + handle: Long, + callback: (Result) -> Unit + ) + + fun referencePutString( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + data: String, + format: Long, + settableMetaData: InternalSettableMetadata, + handle: Long, + callback: (Result) -> Unit + ) + + fun referencePutFile( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + filePath: String, + settableMetaData: InternalSettableMetadata?, + handle: Long, + callback: (Result) -> Unit + ) + + fun referenceDownloadFile( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + filePath: String, + handle: Long, + callback: (Result) -> Unit + ) + + fun referenceUpdateMetadata( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference, + metadata: InternalSettableMetadata, + callback: (Result) -> Unit + ) + + fun taskPause( + app: InternalStorageFirebaseApp, + handle: Long, + callback: (Result>) -> Unit + ) + + fun taskResume( + app: InternalStorageFirebaseApp, + handle: Long, + callback: (Result>) -> Unit + ) + + fun taskCancel( + app: InternalStorageFirebaseApp, + handle: Long, + callback: (Result>) -> Unit + ) companion object { /** The codec used by FirebaseStorageHostApi. */ - val codec: MessageCodec by lazy { - FirebaseStorageHostApiCodec - } - /** Sets up an instance of `FirebaseStorageHostApi` to handle messages through the `binaryMessenger`. */ - @Suppress("UNCHECKED_CAST") - fun setUp(binaryMessenger: BinaryMessenger, api: FirebaseStorageHostApi?) { + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseStoragePigeonCodec() } + /** + * Sets up an instance of `FirebaseStorageHostApi` to handle messages through the + * `binaryMessenger`. + */ + @JvmOverloads + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebaseStorageHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp + val appArg = args[0] as InternalStorageFirebaseApp val pathArg = args[1] as String val bucketArg = args[2] as String? - api.getReferencebyPath(appArg, pathArg, bucketArg) { result: Result -> + api.getReferencebyPath(appArg, pathArg, bucketArg) { + result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -376,18 +822,22 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val timeArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val appArg = args[0] as InternalStorageFirebaseApp + val timeArg = args[1] as Long api.setMaxOperationRetryTime(appArg, timeArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { - reply.reply(wrapResult(null)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(null)) } } } @@ -396,18 +846,22 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val timeArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val appArg = args[0] as InternalStorageFirebaseApp + val timeArg = args[1] as Long api.setMaxUploadRetryTime(appArg, timeArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { - reply.reply(wrapResult(null)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(null)) } } } @@ -416,18 +870,22 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val timeArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val appArg = args[0] as InternalStorageFirebaseApp + val timeArg = args[1] as Long api.setMaxDownloadRetryTime(appArg, timeArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { - reply.reply(wrapResult(null)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(null)) } } } @@ -436,19 +894,23 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp + val appArg = args[0] as InternalStorageFirebaseApp val hostArg = args[1] as String - val portArg = args[2].let { if (it is Int) it.toLong() else it as Long } + val portArg = args[2] as Long api.useStorageEmulator(appArg, hostArg, portArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { - reply.reply(wrapResult(null)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(null)) } } } @@ -457,18 +919,22 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference api.referenceDelete(appArg, referenceArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { - reply.reply(wrapResult(null)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(null)) } } } @@ -477,19 +943,23 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference api.referenceGetDownloadURL(appArg, referenceArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -498,19 +968,23 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference - api.referenceGetMetaData(appArg, referenceArg) { result: Result -> + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference + api.referenceGetMetaData(appArg, referenceArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -519,20 +993,25 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference - val optionsArg = args[2] as PigeonListOptions - api.referenceList(appArg, referenceArg, optionsArg) { result: Result -> + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference + val optionsArg = args[2] as InternalListOptions + api.referenceList(appArg, referenceArg, optionsArg) { result: Result + -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -541,19 +1020,23 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference - api.referenceListAll(appArg, referenceArg) { result: Result -> + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference + api.referenceListAll(appArg, referenceArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -562,20 +1045,24 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference - val maxSizeArg = args[2].let { if (it is Int) it.toLong() else it as Long } + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference + val maxSizeArg = args[2] as Long api.referenceGetData(appArg, referenceArg, maxSizeArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -584,22 +1071,27 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference val dataArg = args[2] as ByteArray - val settableMetaDataArg = args[3] as PigeonSettableMetadata - val handleArg = args[4].let { if (it is Int) it.toLong() else it as Long } - api.referencePutData(appArg, referenceArg, dataArg, settableMetaDataArg, handleArg) { result: Result -> + val settableMetaDataArg = args[3] as InternalSettableMetadata + val handleArg = args[4] as Long + api.referencePutData(appArg, referenceArg, dataArg, settableMetaDataArg, handleArg) { + result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -608,70 +1100,87 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference val dataArg = args[2] as String - val formatArg = args[3].let { if (it is Int) it.toLong() else it as Long } - val settableMetaDataArg = args[4] as PigeonSettableMetadata - val handleArg = args[5].let { if (it is Int) it.toLong() else it as Long } - api.referencePutString(appArg, referenceArg, dataArg, formatArg, settableMetaDataArg, handleArg) { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } - } + val formatArg = args[3] as Long + val settableMetaDataArg = args[4] as InternalSettableMetadata + val handleArg = args[5] as Long + api.referencePutString( + appArg, referenceArg, dataArg, formatArg, settableMetaDataArg, handleArg) { + result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) + } + } } } else { channel.setMessageHandler(null) } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference val filePathArg = args[2] as String - val settableMetaDataArg = args[3] as PigeonSettableMetadata? - val handleArg = args[4].let { if (it is Int) it.toLong() else it as Long } - api.referencePutFile(appArg, referenceArg, filePathArg, settableMetaDataArg, handleArg) { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } - } + val settableMetaDataArg = args[3] as InternalSettableMetadata? + val handleArg = args[4] as Long + api.referencePutFile( + appArg, referenceArg, filePathArg, settableMetaDataArg, handleArg) { + result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) + } + } } } else { channel.setMessageHandler(null) } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference val filePathArg = args[2] as String - val handleArg = args[3].let { if (it is Int) it.toLong() else it as Long } - api.referenceDownloadFile(appArg, referenceArg, filePathArg, handleArg) { result: Result -> + val handleArg = args[3] as Long + api.referenceDownloadFile(appArg, referenceArg, filePathArg, handleArg) { + result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -680,20 +1189,25 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val referenceArg = args[1] as PigeonStorageReference - val metadataArg = args[2] as PigeonSettableMetadata - api.referenceUpdateMetadata(appArg, referenceArg, metadataArg) { result: Result -> + val appArg = args[0] as InternalStorageFirebaseApp + val referenceArg = args[1] as InternalStorageReference + val metadataArg = args[2] as InternalSettableMetadata + api.referenceUpdateMetadata(appArg, referenceArg, metadataArg) { + result: Result -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -702,19 +1216,23 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val handleArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val appArg = args[0] as InternalStorageFirebaseApp + val handleArg = args[1] as Long api.taskPause(appArg, handleArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -723,19 +1241,23 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val handleArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val appArg = args[0] as InternalStorageFirebaseApp + val handleArg = args[1] as Long api.taskResume(appArg, handleArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } @@ -744,19 +1266,23 @@ interface FirebaseStorageHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val appArg = args[0] as PigeonStorageFirebaseApp - val handleArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val appArg = args[0] as InternalStorageFirebaseApp + val handleArg = args[1] as Long api.taskCancel(appArg, handleArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { - reply.reply(wrapError(error)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapError(error)) } else { val data = result.getOrNull() - reply.reply(wrapResult(data)) + reply.reply(GeneratedAndroidFirebaseStoragePigeonUtils.wrapResult(data)) } } } diff --git a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/TaskStateChannelStreamHandler.kt b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/TaskStateChannelStreamHandler.kt index 06cc7eddaadb..396b525d116b 100644 --- a/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/TaskStateChannelStreamHandler.kt +++ b/packages/firebase_storage/firebase_storage/android/src/main/kotlin/io/flutter/plugins/firebase/storage/TaskStateChannelStreamHandler.kt @@ -8,16 +8,15 @@ import androidx.annotation.Nullable import com.google.firebase.storage.FirebaseStorage import com.google.firebase.storage.StorageException import com.google.firebase.storage.StorageTask -import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel.EventSink import io.flutter.plugin.common.EventChannel.StreamHandler import java.util.HashMap internal class TaskStateChannelStreamHandler( - private val flutterTask: FlutterFirebaseStorageTask, - private val androidStorage: FirebaseStorage, - task: Any, - private val identifier: String + private val flutterTask: FlutterFirebaseStorageTask, + private val androidStorage: FirebaseStorage, + task: Any, + private val identifier: String ) : StreamHandler { private val androidTask: StorageTask<*> = task as StorageTask<*> @@ -31,7 +30,7 @@ internal class TaskStateChannelStreamHandler( androidTask.addOnProgressListener { taskSnapshot -> if (flutterTask.isDestroyed()) return@addOnProgressListener val event = getTaskEventMap(taskSnapshot, null) - event[TASK_STATE_NAME] = PigeonStorageTaskState.RUNNING.raw + event[TASK_STATE_NAME] = InternalStorageTaskState.RUNNING.raw events.success(event) flutterTask.notifyResumeObjects() } @@ -39,7 +38,7 @@ internal class TaskStateChannelStreamHandler( androidTask.addOnPausedListener { taskSnapshot -> if (flutterTask.isDestroyed()) return@addOnPausedListener val event = getTaskEventMap(taskSnapshot, null) - event[TASK_STATE_NAME] = PigeonStorageTaskState.PAUSED.raw + event[TASK_STATE_NAME] = InternalStorageTaskState.PAUSED.raw events.success(event) flutterTask.notifyPauseObjects() } @@ -47,7 +46,7 @@ internal class TaskStateChannelStreamHandler( androidTask.addOnSuccessListener { taskSnapshot -> if (flutterTask.isDestroyed()) return@addOnSuccessListener val event = getTaskEventMap(taskSnapshot, null) - event[TASK_STATE_NAME] = PigeonStorageTaskState.SUCCESS.raw + event[TASK_STATE_NAME] = InternalStorageTaskState.SUCCESS.raw events.success(event) flutterTask.destroy() } @@ -55,10 +54,12 @@ internal class TaskStateChannelStreamHandler( androidTask.addOnCanceledListener { if (flutterTask.isDestroyed()) return@addOnCanceledListener val event = getTaskEventMap(null, null) - event[TASK_STATE_NAME] = PigeonStorageTaskState.ERROR.raw + event[TASK_STATE_NAME] = InternalStorageTaskState.ERROR.raw val syntheticException: MutableMap = HashMap() - syntheticException["code"] = FlutterFirebaseStorageException.getCode(StorageException.ERROR_CANCELED) - syntheticException["message"] = FlutterFirebaseStorageException.getMessage(StorageException.ERROR_CANCELED) + syntheticException["code"] = + FlutterFirebaseStorageException.getCode(StorageException.ERROR_CANCELED) + syntheticException["message"] = + FlutterFirebaseStorageException.getMessage(StorageException.ERROR_CANCELED) event[TASK_ERROR] = syntheticException events.success(event) flutterTask.notifyCancelObjects() @@ -68,7 +69,7 @@ internal class TaskStateChannelStreamHandler( androidTask.addOnFailureListener { exception -> if (flutterTask.isDestroyed()) return@addOnFailureListener val event = getTaskEventMap(null, exception) - event[TASK_STATE_NAME] = PigeonStorageTaskState.ERROR.raw + event[TASK_STATE_NAME] = InternalStorageTaskState.ERROR.raw events.success(event) flutterTask.destroy() } @@ -87,7 +88,10 @@ internal class TaskStateChannelStreamHandler( } } - private fun getTaskEventMap(@Nullable snapshot: Any?, @Nullable exception: Exception?): MutableMap { + private fun getTaskEventMap( + @Nullable snapshot: Any?, + @Nullable exception: Exception? + ): MutableMap { val arguments: MutableMap = HashMap() arguments[TASK_APP_NAME] = androidStorage.app.name if (snapshot != null) { @@ -99,5 +103,3 @@ internal class TaskStateChannelStreamHandler( return arguments } } - - diff --git a/packages/firebase_storage/firebase_storage/example/android/app/build.gradle b/packages/firebase_storage/firebase_storage/example/android/app/build.gradle index 45de20db120a..e85ee381169d 100644 --- a/packages/firebase_storage/firebase_storage/example/android/app/build.gradle +++ b/packages/firebase_storage/firebase_storage/example/android/app/build.gradle @@ -45,7 +45,7 @@ android { applicationId = "io.flutter.plugins.firebasestorageexample" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = 23 + minSdkVersion = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName diff --git a/packages/firebase_storage/firebase_storage/example/android/app/src/main/kotlin/io/flutter/plugins/firebasestorageexample/MainActivity.kt b/packages/firebase_storage/firebase_storage/example/android/app/src/main/kotlin/io/flutter/plugins/firebasestorageexample/MainActivity.kt index 7391f3a97ef9..4e41f73c96ae 100644 --- a/packages/firebase_storage/firebase_storage/example/android/app/src/main/kotlin/io/flutter/plugins/firebasestorageexample/MainActivity.kt +++ b/packages/firebase_storage/firebase_storage/example/android/app/src/main/kotlin/io/flutter/plugins/firebasestorageexample/MainActivity.kt @@ -2,4 +2,4 @@ package io.flutter.plugins.firebasestorageexample import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/packages/firebase_storage/firebase_storage/example/ios/Flutter/AppFrameworkInfo.plist b/packages/firebase_storage/firebase_storage/example/ios/Flutter/AppFrameworkInfo.plist index b3aaa733dfbb..6fe4034356ac 100755 --- a/packages/firebase_storage/firebase_storage/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/firebase_storage/firebase_storage/example/ios/Flutter/AppFrameworkInfo.plist @@ -24,7 +24,5 @@ arm64 - MinimumOSVersion - 12.0 diff --git a/packages/firebase_storage/firebase_storage/example/ios/Runner.xcodeproj/project.pbxproj b/packages/firebase_storage/firebase_storage/example/ios/Runner.xcodeproj/project.pbxproj index 25c8e390af78..3fffed27f781 100644 --- a/packages/firebase_storage/firebase_storage/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_storage/firebase_storage/example/ios/Runner.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 5C6F5A6F1EC3CCCC008D64B5 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 5C6F5A701EC3CCCC008D64B5 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7A1ECC901E8EDB6900309407 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -72,6 +73,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -248,26 +250,10 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseAuthInterop/FirebaseAuthInterop.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseStorage/FirebaseStorage.framework", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/image_picker_ios/image_picker_ios.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAppCheckInterop.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuthInterop.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseStorage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker_ios.framework", ); runOnlyForDeploymentPostprocessing = 0; diff --git a/packages/firebase_storage/firebase_storage/example/macos/Podfile b/packages/firebase_storage/firebase_storage/example/macos/Podfile index b4134e5ac6ca..c60870efea08 100644 --- a/packages/firebase_storage/firebase_storage/example/macos/Podfile +++ b/packages/firebase_storage/firebase_storage/example/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.14' +platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/project.pbxproj b/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/project.pbxproj index e688261c0c0b..75f513426b4b 100644 --- a/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ 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 = ""; }; 623F4E175EF0069D1AA8A1AE /* 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 = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; B567FAAE240D1F2E0031F210 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; @@ -135,6 +136,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, @@ -188,6 +190,7 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 0E2E82C8DA2AA4C782E46D76 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -264,6 +267,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0E2E82C8DA2AA4C782E46D76 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/file_selector_macos/file_selector_macos.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_selector_macos.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; diff --git a/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8de585b49f18..126b4eb8ea7d 100644 --- a/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/firebase_storage/firebase_storage/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -78,6 +78,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/packages/firebase_storage/firebase_storage/example/pubspec.yaml b/packages/firebase_storage/firebase_storage/example/pubspec.yaml index bf43d06e4bcc..b6c7fd6c00ba 100755 --- a/packages/firebase_storage/firebase_storage/example/pubspec.yaml +++ b/packages/firebase_storage/firebase_storage/example/pubspec.yaml @@ -1,12 +1,14 @@ name: firebase_storage_example description: Demonstrates how to use the firebase_storage plugin. +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_storage: ^13.2.0 + firebase_core: ^4.11.0 + firebase_storage: ^13.4.3 flutter: sdk: flutter image_picker: ^1.1.2 diff --git a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Package.swift b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Package.swift index 93c37b682939..968917087c67 100644 --- a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Package.swift +++ b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "13.1.0" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "13.4.3" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_storage", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-storage", targets: ["firebase_storage"]), + .library(name: "firebase-storage", targets: ["firebase_storage"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-gcs\""), ] - ), + ) ] ) diff --git a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FLTFirebaseStoragePlugin.swift b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FLTFirebaseStoragePlugin.swift index bd35fb32d196..ef81f56e31da 100644 --- a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FLTFirebaseStoragePlugin.swift +++ b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FLTFirebaseStoragePlugin.swift @@ -57,51 +57,67 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt result(FlutterMethodNotImplemented) } - private func storage(app: PigeonStorageFirebaseApp) -> Storage { + private func storage(app: InternalStorageFirebaseApp) -> Storage { let base = "gs://" + app.bucket let firApp = FLTFirebasePlugin.firebaseAppNamed(app.appName)! return Storage.storage(app: firApp, url: base) } - private func ref(app: PigeonStorageFirebaseApp, - reference: PigeonStorageReference) -> StorageReference { + private func ref( + app: InternalStorageFirebaseApp, + reference: InternalStorageReference + ) -> StorageReference { storage(app: app).reference(withPath: reference.fullPath) } - private func toPigeon(_ ref: StorageReference) -> PigeonStorageReference { - PigeonStorageReference(bucket: ref.bucket, fullPath: ref.fullPath, name: ref.name) + private func toPigeon(_ ref: StorageReference) -> InternalStorageReference { + InternalStorageReference(bucket: ref.bucket, fullPath: ref.fullPath, name: ref.name) } - func getReferencebyPath(app: PigeonStorageFirebaseApp, path: String, bucket: String?, - completion: @escaping (Result) -> Void) { + func getReferencebyPath( + app: InternalStorageFirebaseApp, path: String, bucket: String?, + completion: @escaping (Result) -> Void + ) { let r = storage(app: app).reference(withPath: path) - completion(.success(PigeonStorageReference( - bucket: r.bucket, - fullPath: r.fullPath, - name: r.name - ))) - } - - func setMaxOperationRetryTime(app: PigeonStorageFirebaseApp, time: Int64, - completion: @escaping (Result) -> Void) { + completion( + .success( + InternalStorageReference( + bucket: r.bucket, + fullPath: r.fullPath, + name: r.name + ) + ) + ) + } + + func setMaxOperationRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void + ) { storage(app: app).maxOperationRetryTime = TimeInterval(Double(time) / 1000.0) completion(.success(())) } - func setMaxUploadRetryTime(app: PigeonStorageFirebaseApp, time: Int64, - completion: @escaping (Result) -> Void) { + func setMaxUploadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void + ) { storage(app: app).maxUploadRetryTime = TimeInterval(Double(time) / 1000.0) completion(.success(())) } - func setMaxDownloadRetryTime(app: PigeonStorageFirebaseApp, time: Int64, - completion: @escaping (Result) -> Void) { + func setMaxDownloadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void + ) { storage(app: app).maxDownloadRetryTime = TimeInterval(Double(time) / 1000.0) completion(.success(())) } - func useStorageEmulator(app: PigeonStorageFirebaseApp, host: String, port: Int64, - completion: @escaping (Result) -> Void) { + func useStorageEmulator( + app: InternalStorageFirebaseApp, host: String, port: Int64, + completion: @escaping (Result) -> Void + ) { guard emulatorBooted[app.bucket] == nil else { completion(.success(())) return @@ -112,40 +128,62 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt completion(.success(())) } - func referenceDelete(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) { + func referenceDelete( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { ref(app: app, reference: reference).delete { error in - if let e = error { completion(.failure(self.toFlutterError(e))) } - else { completion(.success(())) } + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(())) + } } } - func referenceGetDownloadURL(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) { + func referenceGetDownloadURL( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { ref(app: app, reference: reference).downloadURL { url, error in - if let e = error { completion(.failure(self.toFlutterError(e))) } - else { completion(.success(url!.absoluteString.replacingOccurrences( - of: ":443", - with: "" - ))) } + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion( + .success( + url!.absoluteString.replacingOccurrences( + of: ":443", + with: "" + ) + ) + ) + } } } - func referenceGetMetaData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) { + func referenceGetMetaData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { ref(app: app, reference: reference).getMetadata { md, error in - if let e = error { completion(.failure(self.toFlutterError(e))) } else { - completion(.success(PigeonFullMetaData(metadata: self.metaToDict(md)))) + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(InternalFullMetaData(metadata: self.metaToDict(md)))) } } } - func referenceList(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - options: PigeonListOptions, - completion: @escaping (Result) -> Void) { + func referenceList( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + options: InternalListOptions, + completion: @escaping (Result) -> Void + ) { let r = ref(app: app, reference: reference) let block: (StorageListResult?, Error?) -> Void = { list, error in - if let e = error { completion(.failure(self.toFlutterError(e))) } else { + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { completion(.success(self.listToPigeon(list!))) } } @@ -156,17 +194,24 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt } } - func referenceListAll(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) { + func referenceListAll( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void + ) { ref(app: app, reference: reference).listAll { list, error in - if let e = error { completion(.failure(self.toFlutterError(e))) } - else { completion(.success(self.listToPigeon(list!))) } + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(self.listToPigeon(list!))) + } } } - func referenceGetData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - maxSize: Int64, - completion: @escaping (Result) -> Void) { + func referenceGetData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + maxSize: Int64, + completion: @escaping (Result) -> Void + ) { ref(app: app, reference: reference).getData(maxSize: maxSize) { data, error in if let e = error { completion(.failure(self.toFlutterError(e))) @@ -178,81 +223,123 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt } } - func referencePutData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - data: FlutterStandardTypedData, settableMetaData: PigeonSettableMetadata, - handle: Int64, completion: @escaping (Result) -> Void) { + func referencePutData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: FlutterStandardTypedData, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void + ) { let r = ref(app: app, reference: reference) let task = r.putData(data.data, metadata: toMeta(settableMetaData)) - completion(.success(registerTask( - task: task, - appName: r.storage.app.name, - handle: handle, - path: r.fullPath - ))) - } - - func referencePutString(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - data: String, format: Int64, settableMetaData: PigeonSettableMetadata, - handle: Int64, completion: @escaping (Result) -> Void) { + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referencePutString( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: String, format: Int64, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void + ) { let r = ref(app: app, reference: reference) let d: Data - if format == 1 { d = Data(base64Encoded: data) ?? Data() } - else if format == - 2 { - d = Data(base64Encoded: data.replacingOccurrences(of: "-", with: "+") - .replacingOccurrences(of: "_", with: "/") - .padding(toLength: ((data.count + 3) / 4) * 4, withPad: "=", startingAt: 0)) ?? Data() - } else { d = Data() } + if format == 1 { + d = Data(base64Encoded: data) ?? Data() + } else if format == 2 { + d = + Data( + base64Encoded: data.replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + .padding(toLength: ((data.count + 3) / 4) * 4, withPad: "=", startingAt: 0) + ) ?? Data() + } else { + d = Data() + } let task = r.putData(d, metadata: toMeta(settableMetaData)) - completion(.success(registerTask( - task: task, - appName: r.storage.app.name, - handle: handle, - path: r.fullPath - ))) - } - - func referencePutFile(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - filePath: String, settableMetaData: PigeonSettableMetadata?, handle: Int64, - completion: @escaping (Result) -> Void) { + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referencePutFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, settableMetaData: InternalSettableMetadata?, + handle: Int64, + completion: @escaping (Result) -> Void + ) { let r = ref(app: app, reference: reference) let url = URL(fileURLWithPath: filePath) let task: StorageUploadTask - if let md = settableMetaData { task = r.putFile(from: url, metadata: toMeta(md)) } - else { task = r.putFile(from: url) } - completion(.success(registerTask( - task: task, - appName: r.storage.app.name, - handle: handle, - path: r.fullPath - ))) - } - - func referenceDownloadFile(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - filePath: String, handle: Int64, - completion: @escaping (Result) -> Void) { + if let md = settableMetaData { + task = r.putFile(from: url, metadata: toMeta(md)) + } else { + task = r.putFile(from: url) + } + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referenceDownloadFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, handle: Int64, + completion: @escaping (Result) -> Void + ) { let r = ref(app: app, reference: reference) let url = URL(fileURLWithPath: filePath) let task = r.write(toFile: url) - completion(.success(registerTask( - task: task, - appName: r.storage.app.name, - handle: handle, - path: r.fullPath - ))) - } - - func referenceUpdateMetadata(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - metadata: PigeonSettableMetadata, - completion: @escaping (Result) -> Void) { + completion( + .success( + registerTask( + task: task, + appName: r.storage.app.name, + handle: handle, + path: r.fullPath + ) + ) + ) + } + + func referenceUpdateMetadata( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + metadata: InternalSettableMetadata, + completion: + @escaping (Result) + -> Void + ) { ref(app: app, reference: reference).updateMetadata(toMeta(metadata)) { md, error in - if let e = error { completion(.failure(self.toFlutterError(e))) } - else { completion(.success(PigeonFullMetaData(metadata: self.metaToDict(md)))) } + if let e = error { + completion(.failure(self.toFlutterError(e))) + } else { + completion(.success(InternalFullMetaData(metadata: self.metaToDict(md)))) + } } } - func taskPause(app: PigeonStorageFirebaseApp, handle: Int64, - completion: @escaping (Result<[String: Any], Error>) -> Void) { + func taskPause( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void + ) { if let task = handleToTask[handle] as? StorageUploadTask { task.pause() completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) @@ -264,8 +351,10 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt } } - func taskResume(app: PigeonStorageFirebaseApp, handle: Int64, - completion: @escaping (Result<[String: Any], Error>) -> Void) { + func taskResume( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void + ) { if let task = handleToTask[handle] as? StorageUploadTask { task.resume() completion(.success(["status": true, "snapshot": currentSnapshot(handle: handle)])) @@ -277,8 +366,10 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt } } - func taskCancel(app: PigeonStorageFirebaseApp, handle: Int64, - completion: @escaping (Result<[String: Any], Error>) -> Void) { + func taskCancel( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void + ) { if let task = handleToTask[handle] as? StorageUploadTask { task.cancel() if let id = handleToIdentifier[handle] { @@ -296,7 +387,7 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt } } - private func toMeta(_ m: PigeonSettableMetadata) -> StorageMetadata { + private func toMeta(_ m: InternalSettableMetadata) -> StorageMetadata { let md = StorageMetadata() if let v = m.cacheControl { md.cacheControl = v } if let v = m.contentType { md.contentType = v } @@ -328,25 +419,29 @@ public final class FLTFirebaseStoragePlugin: NSObject, FlutterPlugin, FirebaseSt return out } - private func listToPigeon(_ list: StorageListResult) -> PigeonListResult { + private func listToPigeon(_ list: StorageListResult) -> InternalListResult { let items = list.items.map { toPigeon($0) } let prefixes = list.prefixes.map { toPigeon($0) } - let itemsOpt: [PigeonStorageReference?] = items.map { Optional($0) } - let prefixesOpt: [PigeonStorageReference?] = prefixes.map { Optional($0) } - return PigeonListResult(items: itemsOpt, pageToken: list.pageToken, prefixs: prefixesOpt) + let itemsOpt: [InternalStorageReference?] = items.map { Optional($0) } + let prefixesOpt: [InternalStorageReference?] = prefixes.map { Optional($0) } + return InternalListResult(items: itemsOpt, pageToken: list.pageToken, prefixs: prefixesOpt) } - private func registerTask(task: StorageObservableTask, appName: String, handle: Int64, - path: String) -> String { + private func registerTask( + task: StorageObservableTask, appName: String, handle: Int64, + path: String + ) -> String { let uuid = UUID().uuidString let channelName = "plugins.flutter.io/firebase_storage/taskEvent/\(uuid)" let channel = FlutterEventChannel(name: channelName, binaryMessenger: messenger!) let storageInstance = Storage.storage(app: FLTFirebasePlugin.firebaseAppNamed(appName)!) - channel.setStreamHandler(TaskStateChannelStreamHandler( - task: task, - storage: storageInstance, - identifier: channelName - )) + channel.setStreamHandler( + TaskStateChannelStreamHandler( + task: task, + storage: storageInstance, + identifier: channelName + ) + ) eventChannels[channelName] = channel handleToTask[handle] = task as AnyObject handleToPath[handle] = path diff --git a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FirebaseStorageMessages.g.swift b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FirebaseStorageMessages.g.swift index be06489ca8f0..8012456b979d 100644 --- a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FirebaseStorageMessages.g.swift +++ b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/FirebaseStorageMessages.g.swift @@ -1,10 +1,11 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation + #if os(iOS) import Flutter #elseif os(macOS) @@ -13,8 +14,21 @@ import Foundation #error("Unsupported platform.") #endif -private func isNullish(_ value: Any?) -> Bool { - 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: Sendable? + + init(code: String, message: String?, details: Sendable?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } } private func wrapResult(_ result: Any?) -> [Any?] { @@ -22,6 +36,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, @@ -31,19 +52,132 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } +private func isNullish(_ value: Any?) -> Bool { + value is NSNull || value == nil +} + private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } return value as! T? } +private func doubleEqualsFirebaseStorageMessages(_ lhs: Double, _ rhs: Double) -> Bool { + (lhs.isNaN && rhs.isNaN) || lhs == rhs +} + +private func doubleHashFirebaseStorageMessages(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8_0000_0000_0000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } +} + +func deepEqualsFirebaseStorageMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { + let cleanLhs = nilOrValue(lhs) as Any? + let cleanRhs = nilOrValue(rhs) as Any? + switch (cleanLhs, cleanRhs) { + case (nil, nil): + return true + + case (nil, _), (_, nil): + return false + + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: + return true + + case is (Void, Void): + return true + + case let (lhsArray, rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEqualsFirebaseStorageMessages(element, rhsArray[index]) { + return false + } + } + return true + + case let (lhsArray, rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEqualsFirebaseStorageMessages(element, rhsArray[index]) { + return false + } + } + return true + + case let (lhsDictionary, rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEqualsFirebaseStorageMessages(lhsKey, rhsKey) { + if deepEqualsFirebaseStorageMessages(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEqualsFirebaseStorageMessages(lhs, rhs) + + case let (lhsHashable, rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + + default: + return false + } +} + +func deepHashFirebaseStorageMessages(value: Any?, hasher: inout Hasher) { + let cleanValue = nilOrValue(value) as Any? + if let cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHashFirebaseStorageMessages(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHashFirebaseStorageMessages(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHashFirebaseStorageMessages(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHashFirebaseStorageMessages(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHashFirebaseStorageMessages(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) + } + } else { + hasher.combine(0) + } +} + /// The type of operation that generated the action code from calling /// [TaskState]. -enum PigeonStorageTaskState: Int { +enum InternalStorageTaskState: Int { /// Indicates the task has been paused by the user. case paused = 0 /// Indicates the task is currently in-progress. @@ -57,17 +191,18 @@ enum PigeonStorageTaskState: Int { } /// Generated class from Pigeon that represents data sent in messages. -struct PigeonStorageFirebaseApp { +struct InternalStorageFirebaseApp: Hashable { var appName: String var tenantId: String? var bucket: String - static func fromList(_ list: [Any?]) -> PigeonStorageFirebaseApp? { - let appName = list[0] as! String - let tenantId: String? = nilOrValue(list[1]) - let bucket = list[2] as! String + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalStorageFirebaseApp? { + let appName = pigeonVar_list[0] as! String + let tenantId: String? = nilOrValue(pigeonVar_list[1]) + let bucket = pigeonVar_list[2] as! String - return PigeonStorageFirebaseApp( + return InternalStorageFirebaseApp( appName: appName, tenantId: tenantId, bucket: bucket @@ -81,20 +216,39 @@ struct PigeonStorageFirebaseApp { bucket, ] } + + static func == (lhs: InternalStorageFirebaseApp, rhs: InternalStorageFirebaseApp) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.appName, rhs.appName) + && deepEqualsFirebaseStorageMessages( + lhs.tenantId, + rhs.tenantId + ) && deepEqualsFirebaseStorageMessages(lhs.bucket, rhs.bucket) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalStorageFirebaseApp") + deepHashFirebaseStorageMessages(value: appName, hasher: &hasher) + deepHashFirebaseStorageMessages(value: tenantId, hasher: &hasher) + deepHashFirebaseStorageMessages(value: bucket, hasher: &hasher) + } } /// Generated class from Pigeon that represents data sent in messages. -struct PigeonStorageReference { +struct InternalStorageReference: Hashable { var bucket: String var fullPath: String var name: String - static func fromList(_ list: [Any?]) -> PigeonStorageReference? { - let bucket = list[0] as! String - let fullPath = list[1] as! String - let name = list[2] as! String + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalStorageReference? { + let bucket = pigeonVar_list[0] as! String + let fullPath = pigeonVar_list[1] as! String + let name = pigeonVar_list[2] as! String - return PigeonStorageReference( + return InternalStorageReference( bucket: bucket, fullPath: fullPath, name: name @@ -108,29 +262,60 @@ struct PigeonStorageReference { name, ] } + + static func == (lhs: InternalStorageReference, rhs: InternalStorageReference) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.bucket, rhs.bucket) + && deepEqualsFirebaseStorageMessages( + lhs.fullPath, + rhs.fullPath + ) && deepEqualsFirebaseStorageMessages(lhs.name, rhs.name) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalStorageReference") + deepHashFirebaseStorageMessages(value: bucket, hasher: &hasher) + deepHashFirebaseStorageMessages(value: fullPath, hasher: &hasher) + deepHashFirebaseStorageMessages(value: name, hasher: &hasher) + } } /// Generated class from Pigeon that represents data sent in messages. -struct PigeonFullMetaData { +struct InternalFullMetaData: Hashable { var metadata: [String?: Any?]? - static func fromList(_ list: [Any?]) -> PigeonFullMetaData? { - let metadata: [String?: Any?]? = nilOrValue(list[0]) + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalFullMetaData? { + let metadata: [String?: Any?]? = nilOrValue(pigeonVar_list[0]) - return PigeonFullMetaData( + return InternalFullMetaData( metadata: metadata ) } func toList() -> [Any?] { [ - metadata, + metadata ] } + + static func == (lhs: InternalFullMetaData, rhs: InternalFullMetaData) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.metadata, rhs.metadata) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalFullMetaData") + deepHashFirebaseStorageMessages(value: metadata, hasher: &hasher) + } } /// Generated class from Pigeon that represents data sent in messages. -struct PigeonListOptions { +struct InternalListOptions: Hashable { /// If set, limits the total number of `prefixes` and `items` to return. /// /// The default and maximum maxResults is 1000. @@ -140,11 +325,12 @@ struct PigeonListOptions { /// If provided, listing is resumed from the previous position. var pageToken: String? - static func fromList(_ list: [Any?]) -> PigeonListOptions? { - let maxResults = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let pageToken: String? = nilOrValue(list[1]) + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalListOptions? { + let maxResults = pigeonVar_list[0] as! Int64 + let pageToken: String? = nilOrValue(pigeonVar_list[1]) - return PigeonListOptions( + return InternalListOptions( maxResults: maxResults, pageToken: pageToken ) @@ -156,10 +342,27 @@ struct PigeonListOptions { pageToken, ] } + + static func == (lhs: InternalListOptions, rhs: InternalListOptions) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.maxResults, rhs.maxResults) + && deepEqualsFirebaseStorageMessages( + lhs.pageToken, + rhs.pageToken + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalListOptions") + deepHashFirebaseStorageMessages(value: maxResults, hasher: &hasher) + deepHashFirebaseStorageMessages(value: pageToken, hasher: &hasher) + } } /// Generated class from Pigeon that represents data sent in messages. -struct PigeonSettableMetadata { +struct InternalSettableMetadata: Hashable { /// Served as the 'Cache-Control' header on object download. /// /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control. @@ -183,15 +386,16 @@ struct PigeonSettableMetadata { /// Additional user-defined custom metadata. var customMetadata: [String?: String?]? - static func fromList(_ list: [Any?]) -> PigeonSettableMetadata? { - let cacheControl: String? = nilOrValue(list[0]) - let contentDisposition: String? = nilOrValue(list[1]) - let contentEncoding: String? = nilOrValue(list[2]) - let contentLanguage: String? = nilOrValue(list[3]) - let contentType: String? = nilOrValue(list[4]) - let customMetadata: [String?: String?]? = nilOrValue(list[5]) + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalSettableMetadata? { + let cacheControl: String? = nilOrValue(pigeonVar_list[0]) + let contentDisposition: String? = nilOrValue(pigeonVar_list[1]) + let contentEncoding: String? = nilOrValue(pigeonVar_list[2]) + let contentLanguage: String? = nilOrValue(pigeonVar_list[3]) + let contentType: String? = nilOrValue(pigeonVar_list[4]) + let customMetadata: [String?: String?]? = nilOrValue(pigeonVar_list[5]) - return PigeonSettableMetadata( + return InternalSettableMetadata( cacheControl: cacheControl, contentDisposition: contentDisposition, contentEncoding: contentEncoding, @@ -211,20 +415,105 @@ struct PigeonSettableMetadata { customMetadata, ] } + + static func == (lhs: InternalSettableMetadata, rhs: InternalSettableMetadata) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.cacheControl, rhs.cacheControl) + && deepEqualsFirebaseStorageMessages( + lhs.contentDisposition, + rhs.contentDisposition + ) && deepEqualsFirebaseStorageMessages(lhs.contentEncoding, rhs.contentEncoding) + && deepEqualsFirebaseStorageMessages( + lhs.contentLanguage, + rhs.contentLanguage + ) && deepEqualsFirebaseStorageMessages(lhs.contentType, rhs.contentType) + && deepEqualsFirebaseStorageMessages( + lhs.customMetadata, + rhs.customMetadata + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalSettableMetadata") + deepHashFirebaseStorageMessages(value: cacheControl, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentDisposition, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentEncoding, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentLanguage, hasher: &hasher) + deepHashFirebaseStorageMessages(value: contentType, hasher: &hasher) + deepHashFirebaseStorageMessages(value: customMetadata, hasher: &hasher) + } } /// Generated class from Pigeon that represents data sent in messages. -struct PigeonListResult { - var items: [PigeonStorageReference?] +struct InternalStorageTaskSnapShot: Hashable { + var bytesTransferred: Int64 + var metadata: InternalFullMetaData? + var state: InternalStorageTaskState + var totalBytes: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalStorageTaskSnapShot? { + let bytesTransferred = pigeonVar_list[0] as! Int64 + let metadata: InternalFullMetaData? = nilOrValue(pigeonVar_list[1]) + let state = pigeonVar_list[2] as! InternalStorageTaskState + let totalBytes = pigeonVar_list[3] as! Int64 + + return InternalStorageTaskSnapShot( + bytesTransferred: bytesTransferred, + metadata: metadata, + state: state, + totalBytes: totalBytes + ) + } + + func toList() -> [Any?] { + [ + bytesTransferred, + metadata, + state, + totalBytes, + ] + } + + static func == (lhs: InternalStorageTaskSnapShot, rhs: InternalStorageTaskSnapShot) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.bytesTransferred, rhs.bytesTransferred) + && deepEqualsFirebaseStorageMessages( + lhs.metadata, + rhs.metadata + ) && deepEqualsFirebaseStorageMessages(lhs.state, rhs.state) + && deepEqualsFirebaseStorageMessages( + lhs.totalBytes, + rhs.totalBytes + ) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalStorageTaskSnapShot") + deepHashFirebaseStorageMessages(value: bytesTransferred, hasher: &hasher) + deepHashFirebaseStorageMessages(value: metadata, hasher: &hasher) + deepHashFirebaseStorageMessages(value: state, hasher: &hasher) + deepHashFirebaseStorageMessages(value: totalBytes, hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct InternalListResult: Hashable { + var items: [InternalStorageReference?] var pageToken: String? - var prefixs: [PigeonStorageReference?] + var prefixs: [InternalStorageReference?] - static func fromList(_ list: [Any?]) -> PigeonListResult? { - let items = list[0] as! [PigeonStorageReference?] - let pageToken: String? = nilOrValue(list[1]) - let prefixs = list[2] as! [PigeonStorageReference?] + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> InternalListResult? { + let items = pigeonVar_list[0] as! [InternalStorageReference?] + let pageToken: String? = nilOrValue(pigeonVar_list[1]) + let prefixs = pigeonVar_list[2] as! [InternalStorageReference?] - return PigeonListResult( + return InternalListResult( items: items, pageToken: pageToken, prefixs: prefixs @@ -238,145 +527,202 @@ struct PigeonListResult { prefixs, ] } + + static func == (lhs: InternalListResult, rhs: InternalListResult) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return deepEqualsFirebaseStorageMessages(lhs.items, rhs.items) + && deepEqualsFirebaseStorageMessages( + lhs.pageToken, + rhs.pageToken + ) && deepEqualsFirebaseStorageMessages(lhs.prefixs, rhs.prefixs) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("InternalListResult") + deepHashFirebaseStorageMessages(value: items, hasher: &hasher) + deepHashFirebaseStorageMessages(value: pageToken, hasher: &hasher) + deepHashFirebaseStorageMessages(value: prefixs, hasher: &hasher) + } } -private class FirebaseStorageHostApiCodecReader: FlutterStandardReader { +private class FirebaseStorageMessagesPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { - case 128: - return PigeonFullMetaData.fromList(readValue() as! [Any?]) case 129: - return PigeonListOptions.fromList(readValue() as! [Any?]) + let enumResultAsInt: Int? = nilOrValue(readValue() as! Int?) + if let enumResultAsInt { + return InternalStorageTaskState(rawValue: enumResultAsInt) + } + return nil case 130: - return PigeonListResult.fromList(readValue() as! [Any?]) + return InternalStorageFirebaseApp.fromList(readValue() as! [Any?]) case 131: - return PigeonSettableMetadata.fromList(readValue() as! [Any?]) + return InternalStorageReference.fromList(readValue() as! [Any?]) case 132: - return PigeonStorageFirebaseApp.fromList(readValue() as! [Any?]) + return InternalFullMetaData.fromList(readValue() as! [Any?]) case 133: - return PigeonStorageReference.fromList(readValue() as! [Any?]) + return InternalListOptions.fromList(readValue() as! [Any?]) + case 134: + return InternalSettableMetadata.fromList(readValue() as! [Any?]) + case 135: + return InternalStorageTaskSnapShot.fromList(readValue() as! [Any?]) + case 136: + return InternalListResult.fromList(readValue() as! [Any?]) default: return super.readValue(ofType: type) } } } -private class FirebaseStorageHostApiCodecWriter: FlutterStandardWriter { +private class FirebaseStorageMessagesPigeonCodecWriter: FlutterStandardWriter { override func writeValue(_ value: Any) { - if let value = value as? PigeonFullMetaData { - super.writeByte(128) - super.writeValue(value.toList()) - } else if let value = value as? PigeonListOptions { + if let value = value as? InternalStorageTaskState { super.writeByte(129) - super.writeValue(value.toList()) - } else if let value = value as? PigeonListResult { + super.writeValue(value.rawValue) + } else if let value = value as? InternalStorageFirebaseApp { super.writeByte(130) super.writeValue(value.toList()) - } else if let value = value as? PigeonSettableMetadata { + } else if let value = value as? InternalStorageReference { super.writeByte(131) super.writeValue(value.toList()) - } else if let value = value as? PigeonStorageFirebaseApp { + } else if let value = value as? InternalFullMetaData { super.writeByte(132) super.writeValue(value.toList()) - } else if let value = value as? PigeonStorageReference { + } else if let value = value as? InternalListOptions { super.writeByte(133) super.writeValue(value.toList()) + } else if let value = value as? InternalSettableMetadata { + super.writeByte(134) + super.writeValue(value.toList()) + } else if let value = value as? InternalStorageTaskSnapShot { + super.writeByte(135) + super.writeValue(value.toList()) + } else if let value = value as? InternalListResult { + super.writeByte(136) + super.writeValue(value.toList()) } else { super.writeValue(value) } } } -private class FirebaseStorageHostApiCodecReaderWriter: FlutterStandardReaderWriter { +private class FirebaseStorageMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { override func reader(with data: Data) -> FlutterStandardReader { - FirebaseStorageHostApiCodecReader(data: data) + FirebaseStorageMessagesPigeonCodecReader(data: data) } override func writer(with data: NSMutableData) -> FlutterStandardWriter { - FirebaseStorageHostApiCodecWriter(data: data) + FirebaseStorageMessagesPigeonCodecWriter(data: data) } } -class FirebaseStorageHostApiCodec: FlutterStandardMessageCodec { +class FirebaseStorageMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { static let shared = - FirebaseStorageHostApiCodec(readerWriter: FirebaseStorageHostApiCodecReaderWriter()) + FirebaseStorageMessagesPigeonCodec( + readerWriter: FirebaseStorageMessagesPigeonCodecReaderWriter() + ) } /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol FirebaseStorageHostApi { - func getReferencebyPath(app: PigeonStorageFirebaseApp, path: String, bucket: String?, - completion: @escaping (Result) -> Void) - func setMaxOperationRetryTime(app: PigeonStorageFirebaseApp, time: Int64, - completion: @escaping (Result) -> Void) - func setMaxUploadRetryTime(app: PigeonStorageFirebaseApp, time: Int64, - completion: @escaping (Result) -> Void) - func setMaxDownloadRetryTime(app: PigeonStorageFirebaseApp, time: Int64, - completion: @escaping (Result) -> Void) - func useStorageEmulator(app: PigeonStorageFirebaseApp, host: String, port: Int64, - completion: @escaping (Result) -> Void) - func referenceDelete(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) - func referenceGetDownloadURL(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) - func referenceGetMetaData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) - func referenceList(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - options: PigeonListOptions, - completion: @escaping (Result) -> Void) - func referenceListAll(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - completion: @escaping (Result) -> Void) - func referenceGetData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - maxSize: Int64, - completion: @escaping (Result) -> Void) - func referencePutData(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - data: FlutterStandardTypedData, settableMetaData: PigeonSettableMetadata, - handle: Int64, completion: @escaping (Result) -> Void) - func referencePutString(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - data: String, format: Int64, settableMetaData: PigeonSettableMetadata, - handle: Int64, completion: @escaping (Result) -> Void) - func referencePutFile(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - filePath: String, settableMetaData: PigeonSettableMetadata?, handle: Int64, - completion: @escaping (Result) -> Void) - func referenceDownloadFile(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - filePath: String, handle: Int64, - completion: @escaping (Result) -> Void) - func referenceUpdateMetadata(app: PigeonStorageFirebaseApp, reference: PigeonStorageReference, - metadata: PigeonSettableMetadata, - completion: @escaping (Result) -> Void) - func taskPause(app: PigeonStorageFirebaseApp, handle: Int64, - completion: @escaping (Result<[String: Any], Error>) -> Void) - func taskResume(app: PigeonStorageFirebaseApp, handle: Int64, - completion: @escaping (Result<[String: Any], Error>) -> Void) - func taskCancel(app: PigeonStorageFirebaseApp, handle: Int64, - completion: @escaping (Result<[String: Any], Error>) -> Void) + func getReferencebyPath( + app: InternalStorageFirebaseApp, path: String, bucket: String?, + completion: @escaping (Result) -> Void) + func setMaxOperationRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void) + func setMaxUploadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void) + func setMaxDownloadRetryTime( + app: InternalStorageFirebaseApp, time: Int64, + completion: @escaping (Result) -> Void) + func useStorageEmulator( + app: InternalStorageFirebaseApp, host: String, port: Int64, + completion: @escaping (Result) -> Void) + func referenceDelete( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceGetDownloadURL( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceGetMetaData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceList( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + options: InternalListOptions, + completion: @escaping (Result) -> Void) + func referenceListAll( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + completion: @escaping (Result) -> Void) + func referenceGetData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + maxSize: Int64, + completion: @escaping (Result) -> Void) + func referencePutData( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: FlutterStandardTypedData, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void) + func referencePutString( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + data: String, format: Int64, settableMetaData: InternalSettableMetadata, + handle: Int64, completion: @escaping (Result) -> Void) + func referencePutFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, settableMetaData: InternalSettableMetadata?, + handle: Int64, completion: @escaping (Result) -> Void) + func referenceDownloadFile( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + filePath: String, handle: Int64, + completion: @escaping (Result) -> Void) + func referenceUpdateMetadata( + app: InternalStorageFirebaseApp, reference: InternalStorageReference, + metadata: InternalSettableMetadata, + completion: @escaping (Result) -> Void) + func taskPause( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void) + func taskResume( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void) + func taskCancel( + app: InternalStorageFirebaseApp, handle: Int64, + completion: @escaping (Result<[String: Any], Error>) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. class FirebaseStorageHostApiSetup { - /// The codec used by FirebaseStorageHostApi. static var codec: FlutterStandardMessageCodec { - FirebaseStorageHostApiCodec.shared + FirebaseStorageMessagesPigeonCodec.shared } /// Sets up an instance of `FirebaseStorageHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseStorageHostApi?) { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseStorageHostApi?, + messageChannelSuffix: String = "" + ) { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let getReferencebyPathChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { getReferencebyPathChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp + let appArg = args[0] as! InternalStorageFirebaseApp let pathArg = args[1] as! String let bucketArg: String? = nilOrValue(args[2]) api.getReferencebyPath(app: appArg, path: pathArg, bucket: bucketArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -385,20 +731,21 @@ class FirebaseStorageHostApiSetup { getReferencebyPathChannel.setMessageHandler(nil) } let setMaxOperationRetryTimeChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { setMaxOperationRetryTimeChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let timeArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let appArg = args[0] as! InternalStorageFirebaseApp + let timeArg = args[1] as! Int64 api.setMaxOperationRetryTime(app: appArg, time: timeArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -407,20 +754,21 @@ class FirebaseStorageHostApiSetup { setMaxOperationRetryTimeChannel.setMessageHandler(nil) } let setMaxUploadRetryTimeChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { setMaxUploadRetryTimeChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let timeArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let appArg = args[0] as! InternalStorageFirebaseApp + let timeArg = args[1] as! Int64 api.setMaxUploadRetryTime(app: appArg, time: timeArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -429,20 +777,21 @@ class FirebaseStorageHostApiSetup { setMaxUploadRetryTimeChannel.setMessageHandler(nil) } let setMaxDownloadRetryTimeChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { setMaxDownloadRetryTimeChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let timeArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let appArg = args[0] as! InternalStorageFirebaseApp + let timeArg = args[1] as! Int64 api.setMaxDownloadRetryTime(app: appArg, time: timeArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -451,21 +800,22 @@ class FirebaseStorageHostApiSetup { setMaxDownloadRetryTimeChannel.setMessageHandler(nil) } let useStorageEmulatorChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { useStorageEmulatorChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp + let appArg = args[0] as! InternalStorageFirebaseApp let hostArg = args[1] as! String - let portArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32) + let portArg = args[2] as! Int64 api.useStorageEmulator(app: appArg, host: hostArg, port: portArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -474,20 +824,21 @@ class FirebaseStorageHostApiSetup { useStorageEmulatorChannel.setMessageHandler(nil) } let referenceDeleteChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceDeleteChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference api.referenceDelete(app: appArg, reference: referenceArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -496,20 +847,21 @@ class FirebaseStorageHostApiSetup { referenceDeleteChannel.setMessageHandler(nil) } let referenceGetDownloadURLChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceGetDownloadURLChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference api.referenceGetDownloadURL(app: appArg, reference: referenceArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -518,20 +870,21 @@ class FirebaseStorageHostApiSetup { referenceGetDownloadURLChannel.setMessageHandler(nil) } let referenceGetMetaDataChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceGetMetaDataChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference api.referenceGetMetaData(app: appArg, reference: referenceArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -540,21 +893,22 @@ class FirebaseStorageHostApiSetup { referenceGetMetaDataChannel.setMessageHandler(nil) } let referenceListChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceListChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference - let optionsArg = args[2] as! PigeonListOptions + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let optionsArg = args[2] as! InternalListOptions api.referenceList(app: appArg, reference: referenceArg, options: optionsArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -563,20 +917,21 @@ class FirebaseStorageHostApiSetup { referenceListChannel.setMessageHandler(nil) } let referenceListAllChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceListAllChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference api.referenceListAll(app: appArg, reference: referenceArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -585,21 +940,22 @@ class FirebaseStorageHostApiSetup { referenceListAllChannel.setMessageHandler(nil) } let referenceGetDataChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceGetDataChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference - let maxSizeArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32) + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let maxSizeArg = args[2] as! Int64 api.referenceGetData(app: appArg, reference: referenceArg, maxSize: maxSizeArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -608,18 +964,19 @@ class FirebaseStorageHostApiSetup { referenceGetDataChannel.setMessageHandler(nil) } let referencePutDataChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referencePutDataChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference let dataArg = args[2] as! FlutterStandardTypedData - let settableMetaDataArg = args[3] as! PigeonSettableMetadata - let handleArg = args[4] is Int64 ? args[4] as! Int64 : Int64(args[4] as! Int32) + let settableMetaDataArg = args[3] as! InternalSettableMetadata + let handleArg = args[4] as! Int64 api.referencePutData( app: appArg, reference: referenceArg, @@ -628,9 +985,9 @@ class FirebaseStorageHostApiSetup { handle: handleArg ) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -639,19 +996,20 @@ class FirebaseStorageHostApiSetup { referencePutDataChannel.setMessageHandler(nil) } let referencePutStringChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referencePutStringChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference let dataArg = args[2] as! String - let formatArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) - let settableMetaDataArg = args[4] as! PigeonSettableMetadata - let handleArg = args[5] is Int64 ? args[5] as! Int64 : Int64(args[5] as! Int32) + let formatArg = args[3] as! Int64 + let settableMetaDataArg = args[4] as! InternalSettableMetadata + let handleArg = args[5] as! Int64 api.referencePutString( app: appArg, reference: referenceArg, @@ -661,9 +1019,9 @@ class FirebaseStorageHostApiSetup { handle: handleArg ) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -672,18 +1030,19 @@ class FirebaseStorageHostApiSetup { referencePutStringChannel.setMessageHandler(nil) } let referencePutFileChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referencePutFileChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference let filePathArg = args[2] as! String - let settableMetaDataArg: PigeonSettableMetadata? = nilOrValue(args[3]) - let handleArg = args[4] is Int64 ? args[4] as! Int64 : Int64(args[4] as! Int32) + let settableMetaDataArg: InternalSettableMetadata? = nilOrValue(args[3]) + let handleArg = args[4] as! Int64 api.referencePutFile( app: appArg, reference: referenceArg, @@ -692,9 +1051,9 @@ class FirebaseStorageHostApiSetup { handle: handleArg ) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -703,17 +1062,18 @@ class FirebaseStorageHostApiSetup { referencePutFileChannel.setMessageHandler(nil) } let referenceDownloadFileChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceDownloadFileChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference let filePathArg = args[2] as! String - let handleArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) + let handleArg = args[3] as! Int64 api.referenceDownloadFile( app: appArg, reference: referenceArg, @@ -721,9 +1081,9 @@ class FirebaseStorageHostApiSetup { handle: handleArg ) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -732,23 +1092,26 @@ class FirebaseStorageHostApiSetup { referenceDownloadFileChannel.setMessageHandler(nil) } let referenceUpdateMetadataChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { referenceUpdateMetadataChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let referenceArg = args[1] as! PigeonStorageReference - let metadataArg = args[2] as! PigeonSettableMetadata + let appArg = args[0] as! InternalStorageFirebaseApp + let referenceArg = args[1] as! InternalStorageReference + let metadataArg = args[2] as! InternalSettableMetadata api - .referenceUpdateMetadata(app: appArg, reference: referenceArg, - metadata: metadataArg) { result in + .referenceUpdateMetadata( + app: appArg, reference: referenceArg, + metadata: metadataArg + ) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -757,20 +1120,21 @@ class FirebaseStorageHostApiSetup { referenceUpdateMetadataChannel.setMessageHandler(nil) } let taskPauseChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { taskPauseChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let handleArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let appArg = args[0] as! InternalStorageFirebaseApp + let handleArg = args[1] as! Int64 api.taskPause(app: appArg, handle: handleArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -779,20 +1143,21 @@ class FirebaseStorageHostApiSetup { taskPauseChannel.setMessageHandler(nil) } let taskResumeChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { taskResumeChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let handleArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let appArg = args[0] as! InternalStorageFirebaseApp + let handleArg = args[1] as! Int64 api.taskResume(app: appArg, handle: handleArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -801,20 +1166,21 @@ class FirebaseStorageHostApiSetup { taskResumeChannel.setMessageHandler(nil) } let taskCancelChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel", + name: + "dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { taskCancelChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let appArg = args[0] as! PigeonStorageFirebaseApp - let handleArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let appArg = args[0] as! InternalStorageFirebaseApp + let handleArg = args[1] as! Int64 api.taskCancel(app: appArg, handle: handleArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } diff --git a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/TaskStateChannelStreamHandler.swift b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/TaskStateChannelStreamHandler.swift index 9cc69b046a25..21acfcbe6f32 100644 --- a/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/TaskStateChannelStreamHandler.swift +++ b/packages/firebase_storage/firebase_storage/ios/firebase_storage/Sources/firebase_storage/TaskStateChannelStreamHandler.swift @@ -27,11 +27,13 @@ final class TaskStateChannelStreamHandler: NSObject, FlutterStreamHandler { self.identifier = identifier } - func onListen(withArguments arguments: Any?, - eventSink events: @escaping FlutterEventSink) -> FlutterError? { + func onListen( + withArguments arguments: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { successHandle = task.observe(.success) { snapshot in events([ - "taskState": 2, // success + "taskState": 2, // success "appName": self.storage.app.name, "snapshot": self.parseTaskSnapshot(snapshot), ]) @@ -41,7 +43,7 @@ final class TaskStateChannelStreamHandler: NSObject, FlutterStreamHandler { let err = snapshot.error as NSError? let errorDict: [String: Any] = self.errorDict(err) events([ - "taskState": 4, // error (including cancellations as errors per platform contract) + "taskState": 4, // error (including cancellations as errors per platform contract) "appName": self.storage.app.name, "error": errorDict, ]) @@ -49,14 +51,14 @@ final class TaskStateChannelStreamHandler: NSObject, FlutterStreamHandler { } pausedHandle = task.observe(.pause) { snapshot in events([ - "taskState": 0, // paused + "taskState": 0, // paused "appName": self.storage.app.name, "snapshot": self.parseTaskSnapshot(snapshot), ]) } progressHandle = task.observe(.progress) { snapshot in events([ - "taskState": 1, // running + "taskState": 1, // running "appName": self.storage.app.name, "snapshot": self.parseTaskSnapshot(snapshot), ]) @@ -105,7 +107,8 @@ final class TaskStateChannelStreamHandler: NSObject, FlutterStreamHandler { } let code: String if error.domain == StorageErrorDomain, - let storageCode = StorageErrorCode(rawValue: error.code) { + let storageCode = StorageErrorCode(rawValue: error.code) + { switch storageCode { case .objectNotFound: code = "object-not-found" case .bucketNotFound: code = "bucket-not-found" diff --git a/packages/firebase_storage/firebase_storage/lib/firebase_storage.dart b/packages/firebase_storage/firebase_storage/lib/firebase_storage.dart index 9a19065a691d..f93119421fd0 100755 --- a/packages/firebase_storage/firebase_storage/lib/firebase_storage.dart +++ b/packages/firebase_storage/firebase_storage/lib/firebase_storage.dart @@ -12,7 +12,7 @@ import 'dart:io' show File; // import 'package:flutter/foundation.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' - show FirebasePluginPlatform; + show FirebasePlugin; import 'package:firebase_storage_platform_interface/firebase_storage_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:mime/mime.dart'; diff --git a/packages/firebase_storage/firebase_storage/lib/src/firebase_storage.dart b/packages/firebase_storage/firebase_storage/lib/src/firebase_storage.dart index b833bfaba6e4..bc53c6bd5ef1 100644 --- a/packages/firebase_storage/firebase_storage/lib/src/firebase_storage.dart +++ b/packages/firebase_storage/firebase_storage/lib/src/firebase_storage.dart @@ -6,7 +6,7 @@ part of firebase_storage; /// The entrypoint for [FirebaseStorage]. -class FirebaseStorage extends FirebasePluginPlatform { +class FirebaseStorage extends FirebasePlugin { FirebaseStorage._({required this.app, required this.bucket}) : super(app.name, 'plugins.flutter.io/firebase_storage'); diff --git a/packages/firebase_storage/firebase_storage/macos/firebase_storage/Package.swift b/packages/firebase_storage/firebase_storage/macos/firebase_storage/Package.swift index fc4e98c134c9..e14f91280ac1 100644 --- a/packages/firebase_storage/firebase_storage/macos/firebase_storage/Package.swift +++ b/packages/firebase_storage/firebase_storage/macos/firebase_storage/Package.swift @@ -7,19 +7,19 @@ import PackageDescription -let library_version = "13.1.0" -let firebase_sdk_version: Version = "12.9.0" +let libraryVersion = "13.4.3" +let firebaseSdkVersion: Version = "12.15.0" let package = Package( name: "firebase_storage", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-storage", targets: ["firebase_storage"]), + .library(name: "firebase-storage", targets: ["firebase_storage"]) ], dependencies: [ - .package(url: "https://github.com/firebase/firebase-ios-sdk", from: firebase_sdk_version), + .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), .package(name: "firebase_core", path: "../firebase_core"), ], targets: [ @@ -30,13 +30,13 @@ let package = Package( .product(name: "firebase-core", package: "firebase_core"), ], resources: [ - .process("Resources"), + .process("Resources") ], cSettings: [ .headerSearchPath("include"), - .define("LIBRARY_VERSION", to: "\"\(library_version)\""), + .define("LIBRARY_VERSION", to: "\"\(libraryVersion)\""), .define("LIBRARY_NAME", to: "\"flutter-fire-gcs\""), ] - ), + ) ] ) diff --git a/packages/firebase_storage/firebase_storage/pubspec.yaml b/packages/firebase_storage/firebase_storage/pubspec.yaml index c43348335a83..45bccdd06158 100755 --- a/packages/firebase_storage/firebase_storage/pubspec.yaml +++ b/packages/firebase_storage/firebase_storage/pubspec.yaml @@ -3,7 +3,8 @@ description: Flutter plugin for Firebase Cloud Storage, a powerful, simple, and cost-effective object storage service for Android and iOS. homepage: https://firebase.google.com/docs/storage/flutter/start repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_storage/firebase_storage -version: 13.2.0 +version: 13.4.3 +resolution: workspace topics: - firebase - storage @@ -15,14 +16,14 @@ false_secrets: - example/** environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_storage_platform_interface: ^5.2.19 - firebase_storage_web: ^3.11.4 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_storage_platform_interface: ^6.0.3 + firebase_storage_web: ^3.11.9 flutter: sdk: flutter mime: ^2.0.0 diff --git a/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.cpp b/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.cpp index 8d318c57196e..6ae3a7e1d69d 100644 --- a/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.cpp +++ b/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.cpp @@ -69,7 +69,7 @@ FirebaseStoragePlugin::FirebaseStoragePlugin() {} FirebaseStoragePlugin::~FirebaseStoragePlugin() = default; -Storage* GetCPPStorageFromPigeon(const PigeonStorageFirebaseApp& pigeonApp, +Storage* GetCPPStorageFromPigeon(const InternalStorageFirebaseApp& pigeonApp, const std::string& bucket_path) { std::string default_url = std::string("gs://") + bucket_path; App* app = App::GetInstance(pigeonApp.app_name().c_str()); @@ -79,8 +79,8 @@ Storage* GetCPPStorageFromPigeon(const PigeonStorageFirebaseApp& pigeonApp, } StorageReference GetCPPStorageReferenceFromPigeon( - const PigeonStorageFirebaseApp& pigeonApp, - const PigeonStorageReference& pigeonReference) { + const InternalStorageFirebaseApp& pigeonApp, + const InternalStorageReference& pigeonReference) { Storage* cpp_storage = GetCPPStorageFromPigeon(pigeonApp, pigeonReference.bucket()); return cpp_storage->GetReference(pigeonReference.full_path()); @@ -215,8 +215,8 @@ flutter::EncodableMap FirebaseStoragePlugin::ErrorStreamEvent( flutter::EncodableMap event; event[flutter::EncodableValue("appName")] = flutter::EncodableValue(app_name); - event[flutter::EncodableValue("taskState")] = - flutter::EncodableValue(static_cast(PigeonStorageTaskState::error)); + event[flutter::EncodableValue("taskState")] = flutter::EncodableValue( + static_cast(InternalStorageTaskState::kError)); event[flutter::EncodableValue("error")] = error; return event; @@ -231,39 +231,40 @@ FlutterError FirebaseStoragePlugin::ParseError( } void FirebaseStoragePlugin::GetReferencebyPath( - const PigeonStorageFirebaseApp& app, const std::string& path, + const InternalStorageFirebaseApp& app, const std::string& path, const std::string* bucket, - std::function reply)> result) { + std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(app, *bucket); StorageReference cpp_reference = cpp_storage->GetReference(path); - PigeonStorageReference* value_ptr = new PigeonStorageReference( + InternalStorageReference* value_ptr = new InternalStorageReference( cpp_reference.bucket(), cpp_reference.full_path(), cpp_reference.name()); result(*value_ptr); } void FirebaseStoragePlugin::SetMaxOperationRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(app, ""); cpp_storage->set_max_operation_retry_time((double)time); } void FirebaseStoragePlugin::SetMaxUploadRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(app, ""); cpp_storage->set_max_upload_retry_time((double)time); } void FirebaseStoragePlugin::SetMaxDownloadRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(app, ""); cpp_storage->set_max_download_retry_time((double)time); } void FirebaseStoragePlugin::UseStorageEmulator( - const PigeonStorageFirebaseApp& app, const std::string& host, int64_t port, + const InternalStorageFirebaseApp& app, const std::string& host, + int64_t port, std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(app, ""); cpp_storage->UseEmulator(host, static_cast(port)); @@ -271,8 +272,8 @@ void FirebaseStoragePlugin::UseStorageEmulator( } void FirebaseStoragePlugin::ReferenceDelete( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, std::function reply)> result) { StorageReference cpp_reference = GetCPPStorageReferenceFromPigeon(app, reference); @@ -287,8 +288,8 @@ void FirebaseStoragePlugin::ReferenceDelete( }); } void FirebaseStoragePlugin::ReferenceGetDownloadURL( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, std::function reply)> result) { StorageReference cpp_reference = GetCPPStorageReferenceFromPigeon(app, reference); @@ -306,7 +307,7 @@ void FirebaseStoragePlugin::ReferenceGetDownloadURL( firebase::storage::Metadata* FirebaseStoragePlugin::CreateStorageMetadataFromPigeon( - const PigeonSettableMetadata* pigeonMetaData) { + const InternalSettableMetadata* pigeonMetaData) { if (pigeonMetaData == nullptr) { return nullptr; // No metadata to process } @@ -444,16 +445,16 @@ flutter::EncodableMap ConvertMedadataToPigeon(const Metadata* meta) { } void FirebaseStoragePlugin::ReferenceGetMetaData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - std::function reply)> result) { + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + std::function reply)> result) { StorageReference cpp_reference = GetCPPStorageReferenceFromPigeon(app, reference); Future future_result = cpp_reference.GetMetadata(); ::Sleep(1); // timing for c++ sdk grabbing a mutex future_result.OnCompletion([result](const Future& metadata_result) { if (metadata_result.error() == firebase::storage::kErrorNone) { - PigeonFullMetaData pigeon_meta = PigeonFullMetaData(); + InternalFullMetaData pigeon_meta = InternalFullMetaData(); pigeon_meta.set_metadata( ConvertMedadataToPigeon(metadata_result.result())); @@ -465,30 +466,31 @@ void FirebaseStoragePlugin::ReferenceGetMetaData( } void FirebaseStoragePlugin::ReferenceList( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const PigeonListOptions& options, - std::function reply)> result) { + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const InternalListOptions& options, + std::function reply)> result) { // C++ doesn't support list yet flutter::EncodableList items = flutter::EncodableList(); flutter::EncodableList prefixs = flutter::EncodableList(); - PigeonListResult pigeon_result = PigeonListResult(items, prefixs); + InternalListResult pigeon_result = InternalListResult(items, prefixs); result(pigeon_result); } void FirebaseStoragePlugin::ReferenceListAll( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - std::function reply)> result) { + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + std::function reply)> result) { // C++ doesn't support listAll yet flutter::EncodableList items = flutter::EncodableList(); flutter::EncodableList prefixs = flutter::EncodableList(); - PigeonListResult pigeon_result = PigeonListResult(items, prefixs); + InternalListResult pigeon_result = InternalListResult(items, prefixs); result(pigeon_result); } void FirebaseStoragePlugin::ReferenceGetData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, int64_t max_size, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, int64_t max_size, std::function>> reply)> result) { StorageReference cpp_reference = @@ -530,7 +532,8 @@ class TaskStateListener : public Listener { } virtual void OnProgress(firebase::storage::Controller* controller) { flutter::EncodableMap event = flutter::EncodableMap(); - event[kTaskStateName] = static_cast(PigeonStorageTaskState::running); + event[kTaskStateName] = + static_cast(InternalStorageTaskState::kRunning); event[kTaskAppName] = controller->GetReference().storage()->app()->name(); flutter::EncodableMap snapshot = flutter::EncodableMap(); snapshot[kTaskSnapshotPath] = controller->GetReference().full_path(); @@ -543,7 +546,7 @@ class TaskStateListener : public Listener { virtual void OnPaused(firebase::storage::Controller* controller) { flutter::EncodableMap event = flutter::EncodableMap(); - event[kTaskStateName] = static_cast(PigeonStorageTaskState::paused); + event[kTaskStateName] = static_cast(InternalStorageTaskState::kPaused); event[kTaskAppName] = controller->GetReference().storage()->app()->name(); flutter::EncodableMap snapshot = flutter::EncodableMap(); snapshot[kTaskSnapshotPath] = controller->GetReference().full_path(); @@ -563,7 +566,7 @@ class PutDataStreamHandler PutDataStreamHandler(Storage* storage, std::string reference_path, const void* data, size_t buffer_size, Controller* controller, - const PigeonSettableMetadata& pigeon_meta_data) + const InternalSettableMetadata& pigeon_meta_data) : meta_data_(pigeon_meta_data) { storage_ = storage; reference_path_ = reference_path; @@ -600,7 +603,7 @@ class PutDataStreamHandler if (data_result.error() == firebase::storage::kErrorNone) { flutter::EncodableMap event = flutter::EncodableMap(); event[kTaskStateName] = - static_cast(PigeonStorageTaskState::success); + static_cast(InternalStorageTaskState::kSuccess); event[kTaskAppName] = std::string(storage_->app()->name()); flutter::EncodableMap snapshot = flutter::EncodableMap(); snapshot[kTaskSnapshotPath] = data_result.result()->path(); @@ -630,7 +633,7 @@ class PutDataStreamHandler Storage* storage_; std::string reference_path_; std::vector data_; - PigeonSettableMetadata meta_data_; + InternalSettableMetadata meta_data_; Controller* controller_; std::unique_ptr>&& events_ = nullptr; @@ -641,9 +644,9 @@ class PutFileStreamHandler public: PutFileStreamHandler(Storage* storage, std::string reference_path, std::string file_path, Controller* controller, - const PigeonSettableMetadata* pigeon_meta_data) + const InternalSettableMetadata* pigeon_meta_data) : meta_data_( - std::make_unique(*pigeon_meta_data)) { + std::make_unique(*pigeon_meta_data)) { storage_ = storage; reference_path_ = reference_path; file_path_ = file_path; @@ -678,7 +681,7 @@ class PutFileStreamHandler if (data_result.error() == firebase::storage::kErrorNone) { flutter::EncodableMap event = flutter::EncodableMap(); event[kTaskStateName] = - static_cast(PigeonStorageTaskState::success); + static_cast(InternalStorageTaskState::kSuccess); event[kTaskAppName] = std::string(storage_->app()->name()); flutter::EncodableMap snapshot = flutter::EncodableMap(); snapshot[kTaskSnapshotPath] = data_result.result()->path(); @@ -711,7 +714,7 @@ class PutFileStreamHandler Controller* controller_; std::unique_ptr>&& events_ = nullptr; - std::unique_ptr meta_data_; + std::unique_ptr meta_data_; }; class GetFileStreamHandler @@ -743,7 +746,7 @@ class GetFileStreamHandler if (data_result.error() == firebase::storage::kErrorNone) { flutter::EncodableMap event = flutter::EncodableMap(); event[kTaskStateName] = - static_cast(PigeonStorageTaskState::success); + static_cast(InternalStorageTaskState::kSuccess); event[kTaskAppName] = std::string(storage_->app()->name()); flutter::EncodableMap snapshot = flutter::EncodableMap(); size_t data_size = *data_result.result(); @@ -783,10 +786,10 @@ class GetFileStreamHandler }; void FirebaseStoragePlugin::ReferencePutData( - const PigeonStorageFirebaseApp& pigeon_app, - const PigeonStorageReference& pigeon_reference, + const InternalStorageFirebaseApp& pigeon_app, + const InternalStorageReference& pigeon_reference, const std::vector& data, - const PigeonSettableMetadata& pigeon_meta_data, int64_t handle, + const InternalSettableMetadata& pigeon_meta_data, int64_t handle, std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(pigeon_app, pigeon_reference.bucket()); @@ -804,9 +807,9 @@ void FirebaseStoragePlugin::ReferencePutData( } void FirebaseStoragePlugin::ReferencePutString( - const PigeonStorageFirebaseApp& pigeon_app, - const PigeonStorageReference& pigeon_reference, const std::string& data, - int64_t format, const PigeonSettableMetadata& settable_meta_data, + const InternalStorageFirebaseApp& pigeon_app, + const InternalStorageReference& pigeon_reference, const std::string& data, + int64_t format, const InternalSettableMetadata& settable_meta_data, int64_t handle, std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(pigeon_app, pigeon_reference.bucket()); @@ -827,10 +830,10 @@ void FirebaseStoragePlugin::ReferencePutString( } void FirebaseStoragePlugin::ReferencePutFile( - const PigeonStorageFirebaseApp& pigeon_app, - const PigeonStorageReference& pigeon_reference, + const InternalStorageFirebaseApp& pigeon_app, + const InternalStorageReference& pigeon_reference, const std::string& file_path, - const PigeonSettableMetadata* settable_meta_data, int64_t handle, + const InternalSettableMetadata* settable_meta_data, int64_t handle, std::function reply)> result) { Storage* cpp_storage = GetCPPStorageFromPigeon(pigeon_app, pigeon_reference.bucket()); @@ -848,8 +851,8 @@ void FirebaseStoragePlugin::ReferencePutFile( } void FirebaseStoragePlugin::ReferenceDownloadFile( - const PigeonStorageFirebaseApp& pigeon_app, - const PigeonStorageReference& pigeon_reference, + const InternalStorageFirebaseApp& pigeon_app, + const InternalStorageReference& pigeon_reference, const std::string& file_path, int64_t handle, std::function reply)> result) { Storage* cpp_storage = @@ -868,10 +871,10 @@ void FirebaseStoragePlugin::ReferenceDownloadFile( } void FirebaseStoragePlugin::ReferenceUpdateMetadata( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - const PigeonSettableMetadata& metadata, - std::function reply)> result) { + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const InternalSettableMetadata& metadata, + std::function reply)> result) { StorageReference cpp_reference = GetCPPStorageReferenceFromPigeon(app, reference); Metadata* cpp_meta = @@ -882,7 +885,7 @@ void FirebaseStoragePlugin::ReferenceUpdateMetadata( future_result.OnCompletion([result](const Future& data_result) { if (data_result.error() == firebase::storage::kErrorNone) { const Metadata* result_meta = data_result.result(); - PigeonFullMetaData pigeonData; + InternalFullMetaData pigeonData; pigeonData.set_metadata(ConvertMedadataToPigeon(result_meta)); result(pigeonData); @@ -893,7 +896,7 @@ void FirebaseStoragePlugin::ReferenceUpdateMetadata( } void FirebaseStoragePlugin::TaskPause( - const PigeonStorageFirebaseApp& app, int64_t handle, + const InternalStorageFirebaseApp& app, int64_t handle, std::function reply)> result) { bool status = controllers_[handle]->Pause(); flutter::EncodableMap task_result = flutter::EncodableMap(); @@ -911,7 +914,7 @@ void FirebaseStoragePlugin::TaskPause( } void FirebaseStoragePlugin::TaskResume( - const PigeonStorageFirebaseApp& app, int64_t handle, + const InternalStorageFirebaseApp& app, int64_t handle, std::function reply)> result) { bool status = controllers_[handle]->Resume(); flutter::EncodableMap task_result = flutter::EncodableMap(); @@ -928,7 +931,7 @@ void FirebaseStoragePlugin::TaskResume( } void FirebaseStoragePlugin::TaskCancel( - const PigeonStorageFirebaseApp& app, int64_t handle, + const InternalStorageFirebaseApp& app, int64_t handle, std::function reply)> result) { bool status = controllers_[handle]->Cancel(); flutter::EncodableMap task_result = flutter::EncodableMap(); diff --git a/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.h b/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.h index 8c4487d9e351..ed5191fbee8b 100644 --- a/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.h +++ b/packages/firebase_storage/firebase_storage/windows/firebase_storage_plugin.h @@ -37,7 +37,7 @@ class FirebaseStoragePlugin : public flutter::Plugin, // Static function declarations // Helper functions static firebase::storage::Metadata* CreateStorageMetadataFromPigeon( - const PigeonSettableMetadata* pigeonMetaData); + const InternalSettableMetadata* pigeonMetaData); static std::map ProcessCustomMetadataMap( const flutter::EncodableMap& customMetadata); static std::vector StringToByteData(const std::string& data, @@ -53,84 +53,86 @@ class FirebaseStoragePlugin : public flutter::Plugin, // FirebaseStorageHostApi virtual void GetReferencebyPath( - const PigeonStorageFirebaseApp& app, const std::string& path, + const InternalStorageFirebaseApp& app, const std::string& path, const std::string* bucket, - std::function reply)> result) + std::function reply)> result) override; virtual void SetMaxOperationRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) override; virtual void SetMaxUploadRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) override; virtual void SetMaxDownloadRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) override; virtual void UseStorageEmulator( - const PigeonStorageFirebaseApp& app, const std::string& host, + const InternalStorageFirebaseApp& app, const std::string& host, int64_t port, std::function reply)> result) override; virtual void ReferenceDelete( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, std::function reply)> result) override; virtual void ReferenceGetDownloadURL( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, std::function reply)> result) override; virtual void ReferenceGetMetaData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - std::function reply)> result) override; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + std::function reply)> result) override; virtual void ReferenceList( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const PigeonListOptions& options, - std::function reply)> result) override; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const InternalListOptions& options, + std::function reply)> result) override; virtual void ReferenceListAll( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - std::function reply)> result) override; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + std::function reply)> result) override; virtual void ReferenceGetData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, int64_t max_size, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, int64_t max_size, std::function>> reply)> result) override; virtual void ReferencePutData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::vector& data, - const PigeonSettableMetadata& settable_meta_data, int64_t handle, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const std::vector& data, + const InternalSettableMetadata& settable_meta_data, int64_t handle, std::function reply)> result) override; virtual void ReferencePutString( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::string& data, - int64_t format, const PigeonSettableMetadata& settable_meta_data, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, const std::string& data, + int64_t format, const InternalSettableMetadata& settable_meta_data, int64_t handle, std::function reply)> result) override; virtual void ReferencePutFile( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::string& file_path, - const PigeonSettableMetadata* settable_meta_data, int64_t handle, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, const std::string& file_path, + const InternalSettableMetadata* settable_meta_data, int64_t handle, std::function reply)> result) override; virtual void ReferenceDownloadFile( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::string& file_path, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, const std::string& file_path, int64_t handle, std::function reply)> result) override; virtual void ReferenceUpdateMetadata( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - const PigeonSettableMetadata& metadata, - std::function reply)> result) override; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const InternalSettableMetadata& metadata, + std::function reply)> result) override; virtual void TaskPause( - const PigeonStorageFirebaseApp& app, int64_t handle, + const InternalStorageFirebaseApp& app, int64_t handle, std::function reply)> result) override; virtual void TaskResume( - const PigeonStorageFirebaseApp& app, int64_t handle, + const InternalStorageFirebaseApp& app, int64_t handle, std::function reply)> result) override; virtual void TaskCancel( - const PigeonStorageFirebaseApp& app, int64_t handle, + const InternalStorageFirebaseApp& app, int64_t handle, std::function reply)> result) override; diff --git a/packages/firebase_storage/firebase_storage/windows/messages.g.cpp b/packages/firebase_storage/firebase_storage/windows/messages.g.cpp index c4f093b94f48..ace21074604b 100644 --- a/packages/firebase_storage/firebase_storage/windows/messages.g.cpp +++ b/packages/firebase_storage/firebase_storage/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,60 +13,277 @@ #include #include +#include +#include #include #include #include namespace firebase_storage_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; + +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} -// PigeonStorageFirebaseApp +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = + std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} -PigeonStorageFirebaseApp::PigeonStorageFirebaseApp(const std::string& app_name, - const std::string& bucket) +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace +// InternalStorageFirebaseApp + +InternalStorageFirebaseApp::InternalStorageFirebaseApp( + const std::string& app_name, const std::string& bucket) : app_name_(app_name), bucket_(bucket) {} -PigeonStorageFirebaseApp::PigeonStorageFirebaseApp(const std::string& app_name, - const std::string* tenant_id, - const std::string& bucket) +InternalStorageFirebaseApp::InternalStorageFirebaseApp( + const std::string& app_name, const std::string* tenant_id, + const std::string& bucket) : app_name_(app_name), tenant_id_(tenant_id ? std::optional(*tenant_id) : std::nullopt), bucket_(bucket) {} -const std::string& PigeonStorageFirebaseApp::app_name() const { +const std::string& InternalStorageFirebaseApp::app_name() const { return app_name_; } -void PigeonStorageFirebaseApp::set_app_name(std::string_view value_arg) { +void InternalStorageFirebaseApp::set_app_name(std::string_view value_arg) { app_name_ = value_arg; } -const std::string* PigeonStorageFirebaseApp::tenant_id() const { +const std::string* InternalStorageFirebaseApp::tenant_id() const { return tenant_id_ ? &(*tenant_id_) : nullptr; } -void PigeonStorageFirebaseApp::set_tenant_id( +void InternalStorageFirebaseApp::set_tenant_id( const std::string_view* value_arg) { tenant_id_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonStorageFirebaseApp::set_tenant_id(std::string_view value_arg) { +void InternalStorageFirebaseApp::set_tenant_id(std::string_view value_arg) { tenant_id_ = value_arg; } -const std::string& PigeonStorageFirebaseApp::bucket() const { return bucket_; } +const std::string& InternalStorageFirebaseApp::bucket() const { + return bucket_; +} -void PigeonStorageFirebaseApp::set_bucket(std::string_view value_arg) { +void InternalStorageFirebaseApp::set_bucket(std::string_view value_arg) { bucket_ = value_arg; } -EncodableList PigeonStorageFirebaseApp::ToEncodableList() const { +EncodableList InternalStorageFirebaseApp::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(EncodableValue(app_name_)); @@ -75,10 +292,10 @@ EncodableList PigeonStorageFirebaseApp::ToEncodableList() const { return list; } -PigeonStorageFirebaseApp PigeonStorageFirebaseApp::FromEncodableList( +InternalStorageFirebaseApp InternalStorageFirebaseApp::FromEncodableList( const EncodableList& list) { - PigeonStorageFirebaseApp decoded(std::get(list[0]), - std::get(list[2])); + InternalStorageFirebaseApp decoded(std::get(list[0]), + std::get(list[2])); auto& encodable_tenant_id = list[1]; if (!encodable_tenant_id.IsNull()) { decoded.set_tenant_id(std::get(encodable_tenant_id)); @@ -86,34 +303,58 @@ PigeonStorageFirebaseApp PigeonStorageFirebaseApp::FromEncodableList( return decoded; } -// PigeonStorageReference +bool InternalStorageFirebaseApp::operator==( + const InternalStorageFirebaseApp& other) const { + return PigeonInternalDeepEquals(app_name_, other.app_name_) && + PigeonInternalDeepEquals(tenant_id_, other.tenant_id_) && + PigeonInternalDeepEquals(bucket_, other.bucket_); +} -PigeonStorageReference::PigeonStorageReference(const std::string& bucket, - const std::string& full_path, - const std::string& name) +bool InternalStorageFirebaseApp::operator!=( + const InternalStorageFirebaseApp& other) const { + return !(*this == other); +} + +size_t InternalStorageFirebaseApp::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(app_name_); + result = result * 31 + PigeonInternalDeepHash(tenant_id_); + result = result * 31 + PigeonInternalDeepHash(bucket_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalStorageFirebaseApp& v) { + return v.Hash(); +} + +// InternalStorageReference + +InternalStorageReference::InternalStorageReference(const std::string& bucket, + const std::string& full_path, + const std::string& name) : bucket_(bucket), full_path_(full_path), name_(name) {} -const std::string& PigeonStorageReference::bucket() const { return bucket_; } +const std::string& InternalStorageReference::bucket() const { return bucket_; } -void PigeonStorageReference::set_bucket(std::string_view value_arg) { +void InternalStorageReference::set_bucket(std::string_view value_arg) { bucket_ = value_arg; } -const std::string& PigeonStorageReference::full_path() const { +const std::string& InternalStorageReference::full_path() const { return full_path_; } -void PigeonStorageReference::set_full_path(std::string_view value_arg) { +void InternalStorageReference::set_full_path(std::string_view value_arg) { full_path_ = value_arg; } -const std::string& PigeonStorageReference::name() const { return name_; } +const std::string& InternalStorageReference::name() const { return name_; } -void PigeonStorageReference::set_name(std::string_view value_arg) { +void InternalStorageReference::set_name(std::string_view value_arg) { name_ = value_arg; } -EncodableList PigeonStorageReference::ToEncodableList() const { +EncodableList InternalStorageReference::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(EncodableValue(bucket_)); @@ -122,45 +363,69 @@ EncodableList PigeonStorageReference::ToEncodableList() const { return list; } -PigeonStorageReference PigeonStorageReference::FromEncodableList( +InternalStorageReference InternalStorageReference::FromEncodableList( const EncodableList& list) { - PigeonStorageReference decoded(std::get(list[0]), - std::get(list[1]), - std::get(list[2])); + InternalStorageReference decoded(std::get(list[0]), + std::get(list[1]), + std::get(list[2])); return decoded; } -// PigeonFullMetaData +bool InternalStorageReference::operator==( + const InternalStorageReference& other) const { + return PigeonInternalDeepEquals(bucket_, other.bucket_) && + PigeonInternalDeepEquals(full_path_, other.full_path_) && + PigeonInternalDeepEquals(name_, other.name_); +} + +bool InternalStorageReference::operator!=( + const InternalStorageReference& other) const { + return !(*this == other); +} + +size_t InternalStorageReference::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(bucket_); + result = result * 31 + PigeonInternalDeepHash(full_path_); + result = result * 31 + PigeonInternalDeepHash(name_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalStorageReference& v) { + return v.Hash(); +} + +// InternalFullMetaData -PigeonFullMetaData::PigeonFullMetaData() {} +InternalFullMetaData::InternalFullMetaData() {} -PigeonFullMetaData::PigeonFullMetaData(const EncodableMap* metadata) +InternalFullMetaData::InternalFullMetaData(const EncodableMap* metadata) : metadata_(metadata ? std::optional(*metadata) : std::nullopt) {} -const EncodableMap* PigeonFullMetaData::metadata() const { +const EncodableMap* InternalFullMetaData::metadata() const { return metadata_ ? &(*metadata_) : nullptr; } -void PigeonFullMetaData::set_metadata(const EncodableMap* value_arg) { +void InternalFullMetaData::set_metadata(const EncodableMap* value_arg) { metadata_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonFullMetaData::set_metadata(const EncodableMap& value_arg) { +void InternalFullMetaData::set_metadata(const EncodableMap& value_arg) { metadata_ = value_arg; } -EncodableList PigeonFullMetaData::ToEncodableList() const { +EncodableList InternalFullMetaData::ToEncodableList() const { EncodableList list; list.reserve(1); list.push_back(metadata_ ? EncodableValue(*metadata_) : EncodableValue()); return list; } -PigeonFullMetaData PigeonFullMetaData::FromEncodableList( +InternalFullMetaData InternalFullMetaData::FromEncodableList( const EncodableList& list) { - PigeonFullMetaData decoded; + InternalFullMetaData decoded; auto& encodable_metadata = list[0]; if (!encodable_metadata.IsNull()) { decoded.set_metadata(std::get(encodable_metadata)); @@ -168,37 +433,55 @@ PigeonFullMetaData PigeonFullMetaData::FromEncodableList( return decoded; } -// PigeonListOptions +bool InternalFullMetaData::operator==(const InternalFullMetaData& other) const { + return PigeonInternalDeepEquals(metadata_, other.metadata_); +} + +bool InternalFullMetaData::operator!=(const InternalFullMetaData& other) const { + return !(*this == other); +} -PigeonListOptions::PigeonListOptions(int64_t max_results) +size_t InternalFullMetaData::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(metadata_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalFullMetaData& v) { + return v.Hash(); +} + +// InternalListOptions + +InternalListOptions::InternalListOptions(int64_t max_results) : max_results_(max_results) {} -PigeonListOptions::PigeonListOptions(int64_t max_results, - const std::string* page_token) +InternalListOptions::InternalListOptions(int64_t max_results, + const std::string* page_token) : max_results_(max_results), page_token_(page_token ? std::optional(*page_token) : std::nullopt) {} -int64_t PigeonListOptions::max_results() const { return max_results_; } +int64_t InternalListOptions::max_results() const { return max_results_; } -void PigeonListOptions::set_max_results(int64_t value_arg) { +void InternalListOptions::set_max_results(int64_t value_arg) { max_results_ = value_arg; } -const std::string* PigeonListOptions::page_token() const { +const std::string* InternalListOptions::page_token() const { return page_token_ ? &(*page_token_) : nullptr; } -void PigeonListOptions::set_page_token(const std::string_view* value_arg) { +void InternalListOptions::set_page_token(const std::string_view* value_arg) { page_token_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonListOptions::set_page_token(std::string_view value_arg) { +void InternalListOptions::set_page_token(std::string_view value_arg) { page_token_ = value_arg; } -EncodableList PigeonListOptions::ToEncodableList() const { +EncodableList InternalListOptions::ToEncodableList() const { EncodableList list; list.reserve(2); list.push_back(EncodableValue(max_results_)); @@ -206,9 +489,9 @@ EncodableList PigeonListOptions::ToEncodableList() const { return list; } -PigeonListOptions PigeonListOptions::FromEncodableList( +InternalListOptions InternalListOptions::FromEncodableList( const EncodableList& list) { - PigeonListOptions decoded(list[0].LongValue()); + InternalListOptions decoded(std::get(list[0])); auto& encodable_page_token = list[1]; if (!encodable_page_token.IsNull()) { decoded.set_page_token(std::get(encodable_page_token)); @@ -216,11 +499,29 @@ PigeonListOptions PigeonListOptions::FromEncodableList( return decoded; } -// PigeonSettableMetadata +bool InternalListOptions::operator==(const InternalListOptions& other) const { + return PigeonInternalDeepEquals(max_results_, other.max_results_) && + PigeonInternalDeepEquals(page_token_, other.page_token_); +} -PigeonSettableMetadata::PigeonSettableMetadata() {} +bool InternalListOptions::operator!=(const InternalListOptions& other) const { + return !(*this == other); +} -PigeonSettableMetadata::PigeonSettableMetadata( +size_t InternalListOptions::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(max_results_); + result = result * 31 + PigeonInternalDeepHash(page_token_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalListOptions& v) { return v.Hash(); } + +// InternalSettableMetadata + +InternalSettableMetadata::InternalSettableMetadata() {} + +InternalSettableMetadata::InternalSettableMetadata( const std::string* cache_control, const std::string* content_disposition, const std::string* content_encoding, const std::string* content_language, const std::string* content_type, const EncodableMap* custom_metadata) @@ -241,93 +542,95 @@ PigeonSettableMetadata::PigeonSettableMetadata( ? std::optional(*custom_metadata) : std::nullopt) {} -const std::string* PigeonSettableMetadata::cache_control() const { +const std::string* InternalSettableMetadata::cache_control() const { return cache_control_ ? &(*cache_control_) : nullptr; } -void PigeonSettableMetadata::set_cache_control( +void InternalSettableMetadata::set_cache_control( const std::string_view* value_arg) { cache_control_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSettableMetadata::set_cache_control(std::string_view value_arg) { +void InternalSettableMetadata::set_cache_control(std::string_view value_arg) { cache_control_ = value_arg; } -const std::string* PigeonSettableMetadata::content_disposition() const { +const std::string* InternalSettableMetadata::content_disposition() const { return content_disposition_ ? &(*content_disposition_) : nullptr; } -void PigeonSettableMetadata::set_content_disposition( +void InternalSettableMetadata::set_content_disposition( const std::string_view* value_arg) { content_disposition_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSettableMetadata::set_content_disposition( +void InternalSettableMetadata::set_content_disposition( std::string_view value_arg) { content_disposition_ = value_arg; } -const std::string* PigeonSettableMetadata::content_encoding() const { +const std::string* InternalSettableMetadata::content_encoding() const { return content_encoding_ ? &(*content_encoding_) : nullptr; } -void PigeonSettableMetadata::set_content_encoding( +void InternalSettableMetadata::set_content_encoding( const std::string_view* value_arg) { content_encoding_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSettableMetadata::set_content_encoding(std::string_view value_arg) { +void InternalSettableMetadata::set_content_encoding( + std::string_view value_arg) { content_encoding_ = value_arg; } -const std::string* PigeonSettableMetadata::content_language() const { +const std::string* InternalSettableMetadata::content_language() const { return content_language_ ? &(*content_language_) : nullptr; } -void PigeonSettableMetadata::set_content_language( +void InternalSettableMetadata::set_content_language( const std::string_view* value_arg) { content_language_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSettableMetadata::set_content_language(std::string_view value_arg) { +void InternalSettableMetadata::set_content_language( + std::string_view value_arg) { content_language_ = value_arg; } -const std::string* PigeonSettableMetadata::content_type() const { +const std::string* InternalSettableMetadata::content_type() const { return content_type_ ? &(*content_type_) : nullptr; } -void PigeonSettableMetadata::set_content_type( +void InternalSettableMetadata::set_content_type( const std::string_view* value_arg) { content_type_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSettableMetadata::set_content_type(std::string_view value_arg) { +void InternalSettableMetadata::set_content_type(std::string_view value_arg) { content_type_ = value_arg; } -const EncodableMap* PigeonSettableMetadata::custom_metadata() const { +const EncodableMap* InternalSettableMetadata::custom_metadata() const { return custom_metadata_ ? &(*custom_metadata_) : nullptr; } -void PigeonSettableMetadata::set_custom_metadata( +void InternalSettableMetadata::set_custom_metadata( const EncodableMap* value_arg) { custom_metadata_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonSettableMetadata::set_custom_metadata( +void InternalSettableMetadata::set_custom_metadata( const EncodableMap& value_arg) { custom_metadata_ = value_arg; } -EncodableList PigeonSettableMetadata::ToEncodableList() const { +EncodableList InternalSettableMetadata::ToEncodableList() const { EncodableList list; list.reserve(6); list.push_back(cache_control_ ? EncodableValue(*cache_control_) @@ -345,9 +648,9 @@ EncodableList PigeonSettableMetadata::ToEncodableList() const { return list; } -PigeonSettableMetadata PigeonSettableMetadata::FromEncodableList( +InternalSettableMetadata InternalSettableMetadata::FromEncodableList( const EncodableList& list) { - PigeonSettableMetadata decoded; + InternalSettableMetadata decoded; auto& encodable_cache_control = list[0]; if (!encodable_cache_control.IsNull()) { decoded.set_cache_control(std::get(encodable_cache_control)); @@ -379,46 +682,208 @@ PigeonSettableMetadata PigeonSettableMetadata::FromEncodableList( return decoded; } -// PigeonListResult +bool InternalSettableMetadata::operator==( + const InternalSettableMetadata& other) const { + return PigeonInternalDeepEquals(cache_control_, other.cache_control_) && + PigeonInternalDeepEquals(content_disposition_, + other.content_disposition_) && + PigeonInternalDeepEquals(content_encoding_, other.content_encoding_) && + PigeonInternalDeepEquals(content_language_, other.content_language_) && + PigeonInternalDeepEquals(content_type_, other.content_type_) && + PigeonInternalDeepEquals(custom_metadata_, other.custom_metadata_); +} + +bool InternalSettableMetadata::operator!=( + const InternalSettableMetadata& other) const { + return !(*this == other); +} + +size_t InternalSettableMetadata::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(cache_control_); + result = result * 31 + PigeonInternalDeepHash(content_disposition_); + result = result * 31 + PigeonInternalDeepHash(content_encoding_); + result = result * 31 + PigeonInternalDeepHash(content_language_); + result = result * 31 + PigeonInternalDeepHash(content_type_); + result = result * 31 + PigeonInternalDeepHash(custom_metadata_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalSettableMetadata& v) { + return v.Hash(); +} + +// InternalStorageTaskSnapShot + +InternalStorageTaskSnapShot::InternalStorageTaskSnapShot( + int64_t bytes_transferred, const InternalStorageTaskState& state, + int64_t total_bytes) + : bytes_transferred_(bytes_transferred), + state_(state), + total_bytes_(total_bytes) {} + +InternalStorageTaskSnapShot::InternalStorageTaskSnapShot( + int64_t bytes_transferred, const InternalFullMetaData* metadata, + const InternalStorageTaskState& state, int64_t total_bytes) + : bytes_transferred_(bytes_transferred), + metadata_(metadata ? std::make_unique(*metadata) + : nullptr), + state_(state), + total_bytes_(total_bytes) {} + +InternalStorageTaskSnapShot::InternalStorageTaskSnapShot( + const InternalStorageTaskSnapShot& other) + : bytes_transferred_(other.bytes_transferred_), + metadata_(other.metadata_ + ? std::make_unique(*other.metadata_) + : nullptr), + state_(other.state_), + total_bytes_(other.total_bytes_) {} + +InternalStorageTaskSnapShot& InternalStorageTaskSnapShot::operator=( + const InternalStorageTaskSnapShot& other) { + bytes_transferred_ = other.bytes_transferred_; + metadata_ = other.metadata_ + ? std::make_unique(*other.metadata_) + : nullptr; + state_ = other.state_; + total_bytes_ = other.total_bytes_; + return *this; +} + +int64_t InternalStorageTaskSnapShot::bytes_transferred() const { + return bytes_transferred_; +} + +void InternalStorageTaskSnapShot::set_bytes_transferred(int64_t value_arg) { + bytes_transferred_ = value_arg; +} + +const InternalFullMetaData* InternalStorageTaskSnapShot::metadata() const { + return metadata_.get(); +} + +void InternalStorageTaskSnapShot::set_metadata( + const InternalFullMetaData* value_arg) { + metadata_ = + value_arg ? std::make_unique(*value_arg) : nullptr; +} + +void InternalStorageTaskSnapShot::set_metadata( + const InternalFullMetaData& value_arg) { + metadata_ = std::make_unique(value_arg); +} + +const InternalStorageTaskState& InternalStorageTaskSnapShot::state() const { + return state_; +} + +void InternalStorageTaskSnapShot::set_state( + const InternalStorageTaskState& value_arg) { + state_ = value_arg; +} + +int64_t InternalStorageTaskSnapShot::total_bytes() const { + return total_bytes_; +} + +void InternalStorageTaskSnapShot::set_total_bytes(int64_t value_arg) { + total_bytes_ = value_arg; +} + +EncodableList InternalStorageTaskSnapShot::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(EncodableValue(bytes_transferred_)); + list.push_back(metadata_ ? CustomEncodableValue(*metadata_) + : EncodableValue()); + list.push_back(CustomEncodableValue(state_)); + list.push_back(EncodableValue(total_bytes_)); + return list; +} + +InternalStorageTaskSnapShot InternalStorageTaskSnapShot::FromEncodableList( + const EncodableList& list) { + InternalStorageTaskSnapShot decoded( + std::get(list[0]), + std::any_cast( + std::get(list[2])), + std::get(list[3])); + auto& encodable_metadata = list[1]; + if (!encodable_metadata.IsNull()) { + decoded.set_metadata(std::any_cast( + std::get(encodable_metadata))); + } + return decoded; +} + +bool InternalStorageTaskSnapShot::operator==( + const InternalStorageTaskSnapShot& other) const { + return PigeonInternalDeepEquals(bytes_transferred_, + other.bytes_transferred_) && + PigeonInternalDeepEquals(metadata_, other.metadata_) && + PigeonInternalDeepEquals(state_, other.state_) && + PigeonInternalDeepEquals(total_bytes_, other.total_bytes_); +} + +bool InternalStorageTaskSnapShot::operator!=( + const InternalStorageTaskSnapShot& other) const { + return !(*this == other); +} + +size_t InternalStorageTaskSnapShot::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(bytes_transferred_); + result = result * 31 + PigeonInternalDeepHash(metadata_); + result = result * 31 + PigeonInternalDeepHash(state_); + result = result * 31 + PigeonInternalDeepHash(total_bytes_); + return result; +} + +size_t PigeonInternalDeepHash(const InternalStorageTaskSnapShot& v) { + return v.Hash(); +} + +// InternalListResult -PigeonListResult::PigeonListResult(const EncodableList& items, - const EncodableList& prefixs) +InternalListResult::InternalListResult(const EncodableList& items, + const EncodableList& prefixs) : items_(items), prefixs_(prefixs) {} -PigeonListResult::PigeonListResult(const EncodableList& items, - const std::string* page_token, - const EncodableList& prefixs) +InternalListResult::InternalListResult(const EncodableList& items, + const std::string* page_token, + const EncodableList& prefixs) : items_(items), page_token_(page_token ? std::optional(*page_token) : std::nullopt), prefixs_(prefixs) {} -const EncodableList& PigeonListResult::items() const { return items_; } +const EncodableList& InternalListResult::items() const { return items_; } -void PigeonListResult::set_items(const EncodableList& value_arg) { +void InternalListResult::set_items(const EncodableList& value_arg) { items_ = value_arg; } -const std::string* PigeonListResult::page_token() const { +const std::string* InternalListResult::page_token() const { return page_token_ ? &(*page_token_) : nullptr; } -void PigeonListResult::set_page_token(const std::string_view* value_arg) { +void InternalListResult::set_page_token(const std::string_view* value_arg) { page_token_ = value_arg ? std::optional(*value_arg) : std::nullopt; } -void PigeonListResult::set_page_token(std::string_view value_arg) { +void InternalListResult::set_page_token(std::string_view value_arg) { page_token_ = value_arg; } -const EncodableList& PigeonListResult::prefixs() const { return prefixs_; } +const EncodableList& InternalListResult::prefixs() const { return prefixs_; } -void PigeonListResult::set_prefixs(const EncodableList& value_arg) { +void InternalListResult::set_prefixs(const EncodableList& value_arg) { prefixs_ = value_arg; } -EncodableList PigeonListResult::ToEncodableList() const { +EncodableList InternalListResult::ToEncodableList() const { EncodableList list; list.reserve(3); list.push_back(EncodableValue(items_)); @@ -427,10 +892,10 @@ EncodableList PigeonListResult::ToEncodableList() const { return list; } -PigeonListResult PigeonListResult::FromEncodableList( +InternalListResult InternalListResult::FromEncodableList( const EncodableList& list) { - PigeonListResult decoded(std::get(list[0]), - std::get(list[2])); + InternalListResult decoded(std::get(list[0]), + std::get(list[2])); auto& encodable_page_token = list[1]; if (!encodable_page_token.IsNull()) { decoded.set_page_token(std::get(encodable_page_token)); @@ -438,109 +903,175 @@ PigeonListResult PigeonListResult::FromEncodableList( return decoded; } -FirebaseStorageHostApiCodecSerializer::FirebaseStorageHostApiCodecSerializer() { +bool InternalListResult::operator==(const InternalListResult& other) const { + return PigeonInternalDeepEquals(items_, other.items_) && + PigeonInternalDeepEquals(page_token_, other.page_token_) && + PigeonInternalDeepEquals(prefixs_, other.prefixs_); +} + +bool InternalListResult::operator!=(const InternalListResult& other) const { + return !(*this == other); +} + +size_t InternalListResult::Hash() const { + size_t result = 1; + result = result * 31 + PigeonInternalDeepHash(items_); + result = result * 31 + PigeonInternalDeepHash(page_token_); + result = result * 31 + PigeonInternalDeepHash(prefixs_); + return result; } -EncodableValue FirebaseStorageHostApiCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { +size_t PigeonInternalDeepHash(const InternalListResult& v) { return v.Hash(); } + +PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} + +EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { - case 128: - return CustomEncodableValue(PigeonFullMetaData::FromEncodableList( + case 129: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + case 130: { + return CustomEncodableValue(InternalStorageFirebaseApp::FromEncodableList( std::get(ReadValue(stream)))); - case 129: - return CustomEncodableValue(PigeonListOptions::FromEncodableList( + } + case 131: { + return CustomEncodableValue(InternalStorageReference::FromEncodableList( std::get(ReadValue(stream)))); - case 130: - return CustomEncodableValue(PigeonListResult::FromEncodableList( + } + case 132: { + return CustomEncodableValue(InternalFullMetaData::FromEncodableList( std::get(ReadValue(stream)))); - case 131: - return CustomEncodableValue(PigeonSettableMetadata::FromEncodableList( + } + case 133: { + return CustomEncodableValue(InternalListOptions::FromEncodableList( std::get(ReadValue(stream)))); - case 132: - return CustomEncodableValue(PigeonStorageFirebaseApp::FromEncodableList( + } + case 134: { + return CustomEncodableValue(InternalSettableMetadata::FromEncodableList( std::get(ReadValue(stream)))); - case 133: - return CustomEncodableValue(PigeonStorageReference::FromEncodableList( + } + case 135: { + return CustomEncodableValue( + InternalStorageTaskSnapShot::FromEncodableList( + std::get(ReadValue(stream)))); + } + case 136: { + return CustomEncodableValue(InternalListResult::FromEncodableList( std::get(ReadValue(stream)))); + } default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } -void FirebaseStorageHostApiCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { +void PigeonInternalCodecSerializer::WriteValue( + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { - if (custom_value->type() == typeid(PigeonFullMetaData)) { - stream->WriteByte(128); - WriteValue(EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), - stream); - return; - } - if (custom_value->type() == typeid(PigeonListOptions)) { + if (custom_value->type() == typeid(InternalStorageTaskState)) { stream->WriteByte(129); - WriteValue(EncodableValue(std::any_cast(*custom_value) - .ToEncodableList()), + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), stream); return; } - if (custom_value->type() == typeid(PigeonListResult)) { + if (custom_value->type() == typeid(InternalStorageFirebaseApp)) { stream->WriteByte(130); - WriteValue( - EncodableValue( - std::any_cast(*custom_value).ToEncodableList()), - stream); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); return; } - if (custom_value->type() == typeid(PigeonSettableMetadata)) { + if (custom_value->type() == typeid(InternalStorageReference)) { stream->WriteByte(131); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonStorageFirebaseApp)) { + if (custom_value->type() == typeid(InternalFullMetaData)) { stream->WriteByte(132); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(PigeonStorageReference)) { + if (custom_value->type() == typeid(InternalListOptions)) { stream->WriteByte(133); WriteValue( - EncodableValue(std::any_cast(*custom_value) + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(InternalSettableMetadata)) { + stream->WriteByte(134); + WriteValue( + EncodableValue(std::any_cast(*custom_value) .ToEncodableList()), stream); return; } + if (custom_value->type() == typeid(InternalStorageTaskSnapShot)) { + stream->WriteByte(135); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(InternalListResult)) { + stream->WriteByte(136); + WriteValue(EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } } - flutter::StandardCodecSerializer::WriteValue(value, stream); + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebaseStorageHostApi. -const flutter::StandardMessageCodec& FirebaseStorageHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &FirebaseStorageHostApiCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& FirebaseStorageHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } // Sets up an instance of `FirebaseStorageHostApi` to handle messages through // the `binary_messenger`. -void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, +void FirebaseStorageHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseStorageHostApi* api) { + FirebaseStorageHostApi::SetUp(binary_messenger, api, ""); +} + +void FirebaseStorageHostApi::SetUp(::flutter::BinaryMessenger* binary_messenger, + FirebaseStorageHostApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = + message_channel_suffix.length() > 0 + ? std::string(".") + message_channel_suffix + : ""; { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.getReferencebyPath", + "FirebaseStorageHostApi.getReferencebyPath" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -549,7 +1080,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_path_arg = args.at(1); if (encodable_path_arg.IsNull()) { @@ -562,7 +1093,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get_if(&encodable_bucket_arg); api->GetReferencebyPath( app_arg, path_arg, bucket_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -577,19 +1108,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.setMaxOperationRetryTime", + "FirebaseStorageHostApi.setMaxOperationRetryTime" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -598,7 +1130,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_time_arg = args.at(1); if (encodable_time_arg.IsNull()) { @@ -622,19 +1154,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.setMaxUploadRetryTime", + "FirebaseStorageHostApi.setMaxUploadRetryTime" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -643,7 +1176,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_time_arg = args.at(1); if (encodable_time_arg.IsNull()) { @@ -667,19 +1200,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.setMaxDownloadRetryTime", + "FirebaseStorageHostApi.setMaxDownloadRetryTime" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -688,7 +1222,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_time_arg = args.at(1); if (encodable_time_arg.IsNull()) { @@ -712,19 +1246,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.useStorageEmulator", + "FirebaseStorageHostApi.useStorageEmulator" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -733,7 +1268,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_host_arg = args.at(1); if (encodable_host_arg.IsNull()) { @@ -763,19 +1298,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceDelete", + "FirebaseStorageHostApi.referenceDelete" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -784,7 +1320,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -792,7 +1328,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); api->ReferenceDelete( app_arg, reference_arg, @@ -810,19 +1346,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceGetDownloadURL", + "FirebaseStorageHostApi.referenceGetDownloadURL" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -831,7 +1368,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -839,7 +1376,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); api->ReferenceGetDownloadURL( app_arg, reference_arg, @@ -858,19 +1395,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceGetMetaData", + "FirebaseStorageHostApi.referenceGetMetaData" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -879,7 +1417,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -887,11 +1425,11 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); api->ReferenceGetMetaData( app_arg, reference_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -906,19 +1444,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceList", + "FirebaseStorageHostApi.referenceList" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -927,7 +1466,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -935,17 +1474,18 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); const auto& encodable_options_arg = args.at(2); if (encodable_options_arg.IsNull()) { reply(WrapError("options_arg unexpectedly null.")); return; } - const auto& options_arg = std::any_cast( - std::get(encodable_options_arg)); + const auto& options_arg = + std::any_cast( + std::get(encodable_options_arg)); api->ReferenceList(app_arg, reference_arg, options_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -960,19 +1500,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceListAll", + "FirebaseStorageHostApi.referenceListAll" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -981,7 +1522,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -989,11 +1530,11 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); api->ReferenceListAll( app_arg, reference_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -1008,19 +1549,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceGetData", + "FirebaseStorageHostApi.referenceGetData" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1029,7 +1571,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -1037,7 +1579,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); const auto& encodable_max_size_arg = args.at(2); if (encodable_max_size_arg.IsNull()) { @@ -1068,19 +1610,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referencePutData", + "FirebaseStorageHostApi.referencePutData" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1089,7 +1632,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -1097,7 +1640,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); const auto& encodable_data_arg = args.at(2); if (encodable_data_arg.IsNull()) { @@ -1112,7 +1655,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& settable_meta_data_arg = - std::any_cast( + std::any_cast( std::get( encodable_settable_meta_data_arg)); const auto& encodable_handle_arg = args.at(4); @@ -1138,19 +1681,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referencePutString", + "FirebaseStorageHostApi.referencePutString" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1159,7 +1703,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -1167,7 +1711,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); const auto& encodable_data_arg = args.at(2); if (encodable_data_arg.IsNull()) { @@ -1187,7 +1731,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& settable_meta_data_arg = - std::any_cast( + std::any_cast( std::get( encodable_settable_meta_data_arg)); const auto& encodable_handle_arg = args.at(5); @@ -1214,19 +1758,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referencePutFile", + "FirebaseStorageHostApi.referencePutFile" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1235,7 +1780,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -1243,7 +1788,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); const auto& encodable_file_path_arg = args.at(2); if (encodable_file_path_arg.IsNull()) { @@ -1254,9 +1799,11 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, std::get(encodable_file_path_arg); const auto& encodable_settable_meta_data_arg = args.at(3); const auto* settable_meta_data_arg = - &(std::any_cast( - std::get( - encodable_settable_meta_data_arg))); + encodable_settable_meta_data_arg.IsNull() + ? nullptr + : &(std::any_cast( + std::get( + encodable_settable_meta_data_arg))); const auto& encodable_handle_arg = args.at(4); if (encodable_handle_arg.IsNull()) { reply(WrapError("handle_arg unexpectedly null.")); @@ -1280,19 +1827,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceDownloadFile", + "FirebaseStorageHostApi.referenceDownloadFile" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1301,7 +1849,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -1309,7 +1857,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); const auto& encodable_file_path_arg = args.at(2); if (encodable_file_path_arg.IsNull()) { @@ -1341,19 +1889,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.referenceUpdateMetadata", + "FirebaseStorageHostApi.referenceUpdateMetadata" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1362,7 +1911,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_reference_arg = args.at(1); if (encodable_reference_arg.IsNull()) { @@ -1370,7 +1919,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& reference_arg = - std::any_cast( + std::any_cast( std::get(encodable_reference_arg)); const auto& encodable_metadata_arg = args.at(2); if (encodable_metadata_arg.IsNull()) { @@ -1378,11 +1927,11 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& metadata_arg = - std::any_cast( + std::any_cast( std::get(encodable_metadata_arg)); api->ReferenceUpdateMetadata( app_arg, reference_arg, metadata_arg, - [reply](ErrorOr&& output) { + [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -1397,19 +1946,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.taskPause", + "FirebaseStorageHostApi.taskPause" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1418,7 +1968,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_handle_arg = args.at(1); if (encodable_handle_arg.IsNull()) { @@ -1442,19 +1992,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.taskResume", + "FirebaseStorageHostApi.taskResume" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1463,7 +2014,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_handle_arg = args.at(1); if (encodable_handle_arg.IsNull()) { @@ -1487,19 +2038,20 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.firebase_storage_platform_interface." - "FirebaseStorageHostApi.taskCancel", + "FirebaseStorageHostApi.taskCancel" + + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, - const flutter::MessageReply& reply) { + const ::flutter::MessageReply& reply) { try { const auto& args = std::get(message); const auto& encodable_app_arg = args.at(0); @@ -1508,7 +2060,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, return; } const auto& app_arg = - std::any_cast( + std::any_cast( std::get(encodable_app_arg)); const auto& encodable_handle_arg = args.at(1); if (encodable_handle_arg.IsNull()) { @@ -1532,7 +2084,7 @@ void FirebaseStorageHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } } @@ -1550,4 +2102,4 @@ EncodableValue FirebaseStorageHostApi::WrapError(const FlutterError& error) { error.details()}); } -} // namespace firebase_storage_windows \ No newline at end of file +} // namespace firebase_storage_windows diff --git a/packages/firebase_storage/firebase_storage/windows/messages.g.h b/packages/firebase_storage/firebase_storage/windows/messages.g.h index 307747a7320b..35ca69bdf997 100644 --- a/packages/firebase_storage/firebase_storage/windows/messages.g.h +++ b/packages/firebase_storage/firebase_storage/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -25,17 +25,17 @@ class FlutterError { explicit FlutterError(const std::string& code, const std::string& message) : code_(code), message_(message) {} explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) + const ::flutter::EncodableValue& details) : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; template @@ -60,30 +60,30 @@ class ErrorOr { // The type of operation that generated the action code from calling // [TaskState]. -enum class PigeonStorageTaskState { +enum class InternalStorageTaskState { // Indicates the task has been paused by the user. - paused = 0, + kPaused = 0, // Indicates the task is currently in-progress. - running = 1, + kRunning = 1, // Indicates the task has successfully completed. - success = 2, + kSuccess = 2, // Indicates the task was canceled. - canceled = 3, + kCanceled = 3, // Indicates the task failed with an error. - error = 4 + kError = 4 }; // Generated class from Pigeon that represents data sent in messages. -class PigeonStorageFirebaseApp { +class InternalStorageFirebaseApp { public: // Constructs an object setting all non-nullable fields. - explicit PigeonStorageFirebaseApp(const std::string& app_name, - const std::string& bucket); + explicit InternalStorageFirebaseApp(const std::string& app_name, + const std::string& bucket); // Constructs an object setting all fields. - explicit PigeonStorageFirebaseApp(const std::string& app_name, - const std::string* tenant_id, - const std::string& bucket); + explicit InternalStorageFirebaseApp(const std::string& app_name, + const std::string* tenant_id, + const std::string& bucket); const std::string& app_name() const; void set_app_name(std::string_view value_arg); @@ -95,24 +95,36 @@ class PigeonStorageFirebaseApp { const std::string& bucket() const; void set_bucket(std::string_view value_arg); + bool operator==(const InternalStorageFirebaseApp& other) const; + bool operator!=(const InternalStorageFirebaseApp& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalStorageFirebaseApp FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static PigeonStorageFirebaseApp FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseStorageHostApi; - friend class FirebaseStorageHostApiCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string app_name_; std::optional tenant_id_; std::string bucket_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonStorageReference { +class InternalStorageReference { public: // Constructs an object setting all fields. - explicit PigeonStorageReference(const std::string& bucket, - const std::string& full_path, - const std::string& name); + explicit InternalStorageReference(const std::string& bucket, + const std::string& full_path, + const std::string& name); const std::string& bucket() const; void set_bucket(std::string_view value_arg); @@ -123,48 +135,73 @@ class PigeonStorageReference { const std::string& name() const; void set_name(std::string_view value_arg); + bool operator==(const InternalStorageReference& other) const; + bool operator!=(const InternalStorageReference& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalStorageReference FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static PigeonStorageReference FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseStorageHostApi; - friend class FirebaseStorageHostApiCodecSerializer; + friend class PigeonInternalCodecSerializer; std::string bucket_; std::string full_path_; std::string name_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonFullMetaData { +class InternalFullMetaData { public: // Constructs an object setting all non-nullable fields. - PigeonFullMetaData(); + InternalFullMetaData(); // Constructs an object setting all fields. - explicit PigeonFullMetaData(const flutter::EncodableMap* metadata); + explicit InternalFullMetaData(const ::flutter::EncodableMap* metadata); + + const ::flutter::EncodableMap* metadata() const; + void set_metadata(const ::flutter::EncodableMap* value_arg); + void set_metadata(const ::flutter::EncodableMap& value_arg); - const flutter::EncodableMap* metadata() const; - void set_metadata(const flutter::EncodableMap* value_arg); - void set_metadata(const flutter::EncodableMap& value_arg); + bool operator==(const InternalFullMetaData& other) const; + bool operator!=(const InternalFullMetaData& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; private: - static PigeonFullMetaData FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + static InternalFullMetaData FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: + friend class InternalStorageTaskSnapShot; friend class FirebaseStorageHostApi; - friend class FirebaseStorageHostApiCodecSerializer; - std::optional metadata_; + friend class PigeonInternalCodecSerializer; + std::optional<::flutter::EncodableMap> metadata_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonListOptions { +class InternalListOptions { public: // Constructs an object setting all non-nullable fields. - explicit PigeonListOptions(int64_t max_results); + explicit InternalListOptions(int64_t max_results); // Constructs an object setting all fields. - explicit PigeonListOptions(int64_t max_results, - const std::string* page_token); + explicit InternalListOptions(int64_t max_results, + const std::string* page_token); // If set, limits the total number of `prefixes` and `items` to return. // @@ -179,29 +216,40 @@ class PigeonListOptions { void set_page_token(const std::string_view* value_arg); void set_page_token(std::string_view value_arg); + bool operator==(const InternalListOptions& other) const; + bool operator!=(const InternalListOptions& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalListOptions FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static PigeonListOptions FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseStorageHostApi; - friend class FirebaseStorageHostApiCodecSerializer; + friend class PigeonInternalCodecSerializer; int64_t max_results_; std::optional page_token_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonSettableMetadata { +class InternalSettableMetadata { public: // Constructs an object setting all non-nullable fields. - PigeonSettableMetadata(); + InternalSettableMetadata(); // Constructs an object setting all fields. - explicit PigeonSettableMetadata(const std::string* cache_control, - const std::string* content_disposition, - const std::string* content_encoding, - const std::string* content_language, - const std::string* content_type, - const flutter::EncodableMap* custom_metadata); + explicit InternalSettableMetadata( + const std::string* cache_control, const std::string* content_disposition, + const std::string* content_encoding, const std::string* content_language, + const std::string* content_type, + const ::flutter::EncodableMap* custom_metadata); // Served as the 'Cache-Control' header on object download. // @@ -243,71 +291,154 @@ class PigeonSettableMetadata { void set_content_type(std::string_view value_arg); // Additional user-defined custom metadata. - const flutter::EncodableMap* custom_metadata() const; - void set_custom_metadata(const flutter::EncodableMap* value_arg); - void set_custom_metadata(const flutter::EncodableMap& value_arg); + const ::flutter::EncodableMap* custom_metadata() const; + void set_custom_metadata(const ::flutter::EncodableMap* value_arg); + void set_custom_metadata(const ::flutter::EncodableMap& value_arg); + + bool operator==(const InternalSettableMetadata& other) const; + bool operator!=(const InternalSettableMetadata& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; private: - static PigeonSettableMetadata FromEncodableList( - const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; + static InternalSettableMetadata FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: friend class FirebaseStorageHostApi; - friend class FirebaseStorageHostApiCodecSerializer; + friend class PigeonInternalCodecSerializer; std::optional cache_control_; std::optional content_disposition_; std::optional content_encoding_; std::optional content_language_; std::optional content_type_; - std::optional custom_metadata_; + std::optional<::flutter::EncodableMap> custom_metadata_; }; // Generated class from Pigeon that represents data sent in messages. -class PigeonListResult { +class InternalStorageTaskSnapShot { public: // Constructs an object setting all non-nullable fields. - explicit PigeonListResult(const flutter::EncodableList& items, - const flutter::EncodableList& prefixs); + explicit InternalStorageTaskSnapShot(int64_t bytes_transferred, + const InternalStorageTaskState& state, + int64_t total_bytes); // Constructs an object setting all fields. - explicit PigeonListResult(const flutter::EncodableList& items, - const std::string* page_token, - const flutter::EncodableList& prefixs); + explicit InternalStorageTaskSnapShot(int64_t bytes_transferred, + const InternalFullMetaData* metadata, + const InternalStorageTaskState& state, + int64_t total_bytes); + + ~InternalStorageTaskSnapShot() = default; + InternalStorageTaskSnapShot(const InternalStorageTaskSnapShot& other); + InternalStorageTaskSnapShot& operator=( + const InternalStorageTaskSnapShot& other); + InternalStorageTaskSnapShot(InternalStorageTaskSnapShot&& other) = default; + InternalStorageTaskSnapShot& operator=( + InternalStorageTaskSnapShot&& other) noexcept = default; + int64_t bytes_transferred() const; + void set_bytes_transferred(int64_t value_arg); + + const InternalFullMetaData* metadata() const; + void set_metadata(const InternalFullMetaData* value_arg); + void set_metadata(const InternalFullMetaData& value_arg); + + const InternalStorageTaskState& state() const; + void set_state(const InternalStorageTaskState& value_arg); + + int64_t total_bytes() const; + void set_total_bytes(int64_t value_arg); + + bool operator==(const InternalStorageTaskSnapShot& other) const; + bool operator!=(const InternalStorageTaskSnapShot& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalStorageTaskSnapShot FromEncodableList( + const ::flutter::EncodableList& list); - const flutter::EncodableList& items() const; - void set_items(const flutter::EncodableList& value_arg); + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: + private: + friend class FirebaseStorageHostApi; + friend class PigeonInternalCodecSerializer; + int64_t bytes_transferred_; + std::unique_ptr metadata_; + InternalStorageTaskState state_; + int64_t total_bytes_; +}; + +// Generated class from Pigeon that represents data sent in messages. +class InternalListResult { + public: + // Constructs an object setting all non-nullable fields. + explicit InternalListResult(const ::flutter::EncodableList& items, + const ::flutter::EncodableList& prefixs); + + // Constructs an object setting all fields. + explicit InternalListResult(const ::flutter::EncodableList& items, + const std::string* page_token, + const ::flutter::EncodableList& prefixs); + + const ::flutter::EncodableList& items() const; + void set_items(const ::flutter::EncodableList& value_arg); const std::string* page_token() const; void set_page_token(const std::string_view* value_arg); void set_page_token(std::string_view value_arg); - const flutter::EncodableList& prefixs() const; - void set_prefixs(const flutter::EncodableList& value_arg); + const ::flutter::EncodableList& prefixs() const; + void set_prefixs(const ::flutter::EncodableList& value_arg); + bool operator==(const InternalListResult& other) const; + bool operator!=(const InternalListResult& other) const; + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. + size_t Hash() const; + + private: + static InternalListResult FromEncodableList( + const ::flutter::EncodableList& list); + + public: + public: + ::flutter::EncodableList ToEncodableList() const; + + private: private: - static PigeonListResult FromEncodableList(const flutter::EncodableList& list); - flutter::EncodableList ToEncodableList() const; friend class FirebaseStorageHostApi; - friend class FirebaseStorageHostApiCodecSerializer; - flutter::EncodableList items_; + friend class PigeonInternalCodecSerializer; + ::flutter::EncodableList items_; std::optional page_token_; - flutter::EncodableList prefixs_; + ::flutter::EncodableList prefixs_; }; -class FirebaseStorageHostApiCodecSerializer - : public flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: - FirebaseStorageHostApiCodecSerializer(); - inline static FirebaseStorageHostApiCodecSerializer& GetInstance() { - static FirebaseStorageHostApiCodecSerializer sInstance; + PigeonInternalCodecSerializer(); + inline static PigeonInternalCodecSerializer& GetInstance() { + static PigeonInternalCodecSerializer sInstance; return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; + void WriteValue(const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, ::flutter::ByteStreamReader* stream) const override; }; // Generated interface from Pigeon that represents a handler of messages from @@ -318,91 +449,96 @@ class FirebaseStorageHostApi { FirebaseStorageHostApi& operator=(const FirebaseStorageHostApi&) = delete; virtual ~FirebaseStorageHostApi() {} virtual void GetReferencebyPath( - const PigeonStorageFirebaseApp& app, const std::string& path, + const InternalStorageFirebaseApp& app, const std::string& path, const std::string* bucket, - std::function reply)> result) = 0; + std::function reply)> result) = 0; virtual void SetMaxOperationRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) = 0; virtual void SetMaxUploadRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) = 0; virtual void SetMaxDownloadRetryTime( - const PigeonStorageFirebaseApp& app, int64_t time, + const InternalStorageFirebaseApp& app, int64_t time, std::function reply)> result) = 0; virtual void UseStorageEmulator( - const PigeonStorageFirebaseApp& app, const std::string& host, + const InternalStorageFirebaseApp& app, const std::string& host, int64_t port, std::function reply)> result) = 0; virtual void ReferenceDelete( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, std::function reply)> result) = 0; virtual void ReferenceGetDownloadURL( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, std::function reply)> result) = 0; virtual void ReferenceGetMetaData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - std::function reply)> result) = 0; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + std::function reply)> result) = 0; virtual void ReferenceList( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const PigeonListOptions& options, - std::function reply)> result) = 0; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const InternalListOptions& options, + std::function reply)> result) = 0; virtual void ReferenceListAll( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - std::function reply)> result) = 0; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + std::function reply)> result) = 0; virtual void ReferenceGetData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, int64_t max_size, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, int64_t max_size, std::function>> reply)> result) = 0; virtual void ReferencePutData( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::vector& data, - const PigeonSettableMetadata& settable_meta_data, int64_t handle, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const std::vector& data, + const InternalSettableMetadata& settable_meta_data, int64_t handle, std::function reply)> result) = 0; virtual void ReferencePutString( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::string& data, - int64_t format, const PigeonSettableMetadata& settable_meta_data, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, const std::string& data, + int64_t format, const InternalSettableMetadata& settable_meta_data, int64_t handle, std::function reply)> result) = 0; virtual void ReferencePutFile( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::string& file_path, - const PigeonSettableMetadata* settable_meta_data, int64_t handle, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, const std::string& file_path, + const InternalSettableMetadata* settable_meta_data, int64_t handle, std::function reply)> result) = 0; virtual void ReferenceDownloadFile( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, const std::string& file_path, + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, const std::string& file_path, int64_t handle, std::function reply)> result) = 0; virtual void ReferenceUpdateMetadata( - const PigeonStorageFirebaseApp& app, - const PigeonStorageReference& reference, - const PigeonSettableMetadata& metadata, - std::function reply)> result) = 0; + const InternalStorageFirebaseApp& app, + const InternalStorageReference& reference, + const InternalSettableMetadata& metadata, + std::function reply)> result) = 0; virtual void TaskPause( - const PigeonStorageFirebaseApp& app, int64_t handle, - std::function reply)> result) = 0; + const InternalStorageFirebaseApp& app, int64_t handle, + std::function reply)> result) = 0; virtual void TaskResume( - const PigeonStorageFirebaseApp& app, int64_t handle, - std::function reply)> result) = 0; + const InternalStorageFirebaseApp& app, int64_t handle, + std::function reply)> result) = 0; virtual void TaskCancel( - const PigeonStorageFirebaseApp& app, int64_t handle, - std::function reply)> result) = 0; + const InternalStorageFirebaseApp& app, int64_t handle, + std::function reply)> result) = 0; // The codec used by FirebaseStorageHostApi. - static const flutter::StandardMessageCodec& GetCodec(); + static const ::flutter::StandardMessageCodec& GetCodec(); // Sets up an instance of `FirebaseStorageHostApi` to handle messages through // the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, + static void SetUp(::flutter::BinaryMessenger* binary_messenger, FirebaseStorageHostApi* api); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); + static void SetUp(::flutter::BinaryMessenger* binary_messenger, + FirebaseStorageHostApi* api, + const std::string& message_channel_suffix); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseStorageHostApi() = default; diff --git a/packages/firebase_storage/firebase_storage_platform_interface/CHANGELOG.md b/packages/firebase_storage/firebase_storage_platform_interface/CHANGELOG.md index cf66c6d1a1e2..34ad71f0e918 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/CHANGELOG.md +++ b/packages/firebase_storage/firebase_storage_platform_interface/CHANGELOG.md @@ -1,3 +1,26 @@ +## 6.0.3 + + - Update a dependency to the latest release. + +## 6.0.2 + + - Update a dependency to the latest release. + +## 6.0.1 + + - Update a dependency to the latest release. + +## 6.0.0 + + - Bump platform interface a major version due to pigeon dependency update + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + - **FEAT**: upgrade pigeon to version 26.3.4 ([#18205](https://github.com/firebase/flutterfire/issues/18205)). ([cb6b4aef](https://github.com/firebase/flutterfire/commit/cb6b4aeffc568755ea3eebe32b998f00237bf5ad)) + +## 5.2.20 + + - Update a dependency to the latest release. + ## 5.2.19 - **REFACTOR**: fix formatting and analysis issues across the repo ([#18124](https://github.com/firebase/flutterfire/issues/18124)). ([ab79fd93](https://github.com/firebase/flutterfire/commit/ab79fd93ee4ccfeb478687623134b1cf8ab71c74)) diff --git a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_firebase_storage.dart b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_firebase_storage.dart index ec3544856a96..85cc085517c2 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_firebase_storage.dart +++ b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_firebase_storage.dart @@ -43,8 +43,8 @@ class MethodChannelFirebaseStorage extends FirebaseStoragePlatform { static final FirebaseStorageHostApi pigeonChannel = FirebaseStorageHostApi(); /// FirebaseApp pigeon instance - PigeonStorageFirebaseApp get pigeonFirebaseApp { - return PigeonStorageFirebaseApp( + InternalStorageFirebaseApp get pigeonFirebaseApp { + return InternalStorageFirebaseApp( appName: app.name, bucket: bucket, ); @@ -71,28 +71,28 @@ class MethodChannelFirebaseStorage extends FirebaseStoragePlatform { return MethodChannelFirebaseStorage._(); } - /// Return an instance of a [PigeonStorageReference] - static PigeonStorageReference getPigeonReference( + /// Return an instance of a [InternalStorageReference] + static InternalStorageReference getPigeonReference( String bucket, String fullPath, String name) { - return PigeonStorageReference( + return InternalStorageReference( bucket: bucket, fullPath: fullPath, name: name); } - /// Return an instance of a [PigeonStorageFirebaseApp] - PigeonStorageFirebaseApp getPigeonFirebaseApp(String appName) { - return PigeonStorageFirebaseApp( + /// Return an instance of a [InternalStorageFirebaseApp] + InternalStorageFirebaseApp getPigeonFirebaseApp(String appName) { + return InternalStorageFirebaseApp( appName: appName, bucket: bucket, ); } - /// Convert a [SettableMetadata] to [PigeonSettableMetadata] - static PigeonSettableMetadata getPigeonSettableMetaData( + /// Convert a [SettableMetadata] to [InternalSettableMetadata] + static InternalSettableMetadata getPigeonSettableMetaData( SettableMetadata? metaData) { if (metaData == null) { - return PigeonSettableMetadata(); + return InternalSettableMetadata(); } - return PigeonSettableMetadata( + return InternalSettableMetadata( cacheControl: metaData.cacheControl, contentDisposition: metaData.contentDisposition, contentEncoding: metaData.contentEncoding, diff --git a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_reference.dart b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_reference.dart index 22b05190161a..bc279770919b 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_reference.dart +++ b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_reference.dart @@ -22,16 +22,16 @@ class MethodChannelReference extends ReferencePlatform { : super(storage, path); /// FirebaseApp pigeon instance - PigeonStorageFirebaseApp get pigeonFirebaseApp { - return PigeonStorageFirebaseApp( + InternalStorageFirebaseApp get pigeonFirebaseApp { + return InternalStorageFirebaseApp( appName: storage.app.name, bucket: storage.bucket, ); } /// Default of FirebaseReference pigeon instance - PigeonStorageReference get pigeonReference { - return PigeonStorageReference( + InternalStorageReference get pigeonReference { + return InternalStorageReference( bucket: storage.bucket, fullPath: fullPath, name: name, @@ -59,8 +59,8 @@ class MethodChannelReference extends ReferencePlatform { } } - /// Convert a [PigeonFullMetaData] to [FullMetadata] - static FullMetadata convertMetadata(PigeonFullMetaData pigeonMetadata) { + /// Convert a [InternalFullMetaData] to [FullMetadata] + static FullMetadata convertMetadata(InternalFullMetaData pigeonMetadata) { Map _metadata = {}; pigeonMetadata.metadata?.forEach((key, value) { if (key != null) { @@ -73,7 +73,7 @@ class MethodChannelReference extends ReferencePlatform { @override Future getMetadata() async { try { - PigeonFullMetaData metaData = await MethodChannelFirebaseStorage + InternalFullMetaData metaData = await MethodChannelFirebaseStorage .pigeonChannel .referenceGetMetaData(pigeonFirebaseApp, pigeonReference); return convertMetadata(metaData); @@ -82,20 +82,20 @@ class MethodChannelReference extends ReferencePlatform { } } - /// Convert a [ListOptions] to [PigeonListOptions] - static PigeonListOptions convertOptions(ListOptions? options) { + /// Convert a [ListOptions] to [InternalListOptions] + static InternalListOptions convertOptions(ListOptions? options) { if (options == null) { - return PigeonListOptions(maxResults: 1000); + return InternalListOptions(maxResults: 1000); } - return PigeonListOptions( + return InternalListOptions( maxResults: options.maxResults ?? 1000, pageToken: options.pageToken, ); } - /// Convert a [PigeonListResult] to [ListResultPlatform] + /// Convert a [InternalListResult] to [ListResultPlatform] ListResultPlatform convertListReference( - PigeonListResult pigeonReferenceList) { + InternalListResult pigeonReferenceList) { List referencePaths = []; for (final reference in pigeonReferenceList.items) { referencePaths.add(reference!.fullPath); @@ -115,10 +115,10 @@ class MethodChannelReference extends ReferencePlatform { @override Future list([ListOptions? options]) async { try { - PigeonListOptions pigeonOptions = convertOptions(options); - PigeonListResult pigeonReferenceList = await MethodChannelFirebaseStorage - .pigeonChannel - .referenceList(pigeonFirebaseApp, pigeonReference, pigeonOptions); + InternalListOptions pigeonOptions = convertOptions(options); + InternalListResult pigeonReferenceList = + await MethodChannelFirebaseStorage.pigeonChannel + .referenceList(pigeonFirebaseApp, pigeonReference, pigeonOptions); return convertListReference(pigeonReferenceList); } catch (e, stack) { convertPlatformException(e, stack); @@ -128,9 +128,9 @@ class MethodChannelReference extends ReferencePlatform { @override Future listAll() async { try { - PigeonListResult pigeonReferenceList = await MethodChannelFirebaseStorage - .pigeonChannel - .referenceListAll(pigeonFirebaseApp, pigeonReference); + InternalListResult pigeonReferenceList = + await MethodChannelFirebaseStorage.pigeonChannel + .referenceListAll(pigeonFirebaseApp, pigeonReference); return convertListReference(pigeonReferenceList); } catch (e, stack) { convertPlatformException(e, stack); @@ -173,9 +173,9 @@ class MethodChannelReference extends ReferencePlatform { handle, storage, fullPath, data, format, metadata); } - /// Convert a [SettableMetadata] to [PigeonSettableMetadata] - PigeonSettableMetadata convertToPigeonMetaData(SettableMetadata data) { - return PigeonSettableMetadata( + /// Convert a [SettableMetadata] to [InternalSettableMetadata] + InternalSettableMetadata convertToPigeonMetaData(SettableMetadata data) { + return InternalSettableMetadata( cacheControl: data.cacheControl, contentDisposition: data.contentDisposition, contentEncoding: data.contentEncoding, @@ -188,7 +188,7 @@ class MethodChannelReference extends ReferencePlatform { @override Future updateMetadata(SettableMetadata metadata) async { try { - PigeonFullMetaData updatedMetaData = await MethodChannelFirebaseStorage + InternalFullMetaData updatedMetaData = await MethodChannelFirebaseStorage .pigeonChannel .referenceUpdateMetadata(pigeonFirebaseApp, pigeonReference, convertToPigeonMetaData(metadata)); diff --git a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_task.dart b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_task.dart index cb10a718cc6b..71524a2c441e 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_task.dart +++ b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/method_channel/method_channel_task.dart @@ -126,27 +126,27 @@ abstract class MethodChannelTask extends TaskPlatform { bool _userListening = false; /// FirebaseApp pigeon instance - static PigeonStorageFirebaseApp pigeonFirebaseApp( + static InternalStorageFirebaseApp pigeonFirebaseApp( FirebaseStoragePlatform storage) { - return PigeonStorageFirebaseApp( + return InternalStorageFirebaseApp( appName: storage.app.name, bucket: storage.bucket, ); } - /// Convert [TaskState] to [PigeonStorageTaskState] - PigeonStorageTaskState convertToPigeonTaskState(TaskState state) { + /// Convert [TaskState] to [InternalStorageTaskState] + InternalStorageTaskState convertToPigeonTaskState(TaskState state) { switch (state) { case TaskState.canceled: - return PigeonStorageTaskState.canceled; + return InternalStorageTaskState.canceled; case TaskState.error: - return PigeonStorageTaskState.error; + return InternalStorageTaskState.error; case TaskState.paused: - return PigeonStorageTaskState.paused; + return InternalStorageTaskState.paused; case TaskState.running: - return PigeonStorageTaskState.running; + return InternalStorageTaskState.running; case TaskState.success: - return PigeonStorageTaskState.success; + return InternalStorageTaskState.success; } } @@ -263,7 +263,7 @@ class MethodChannelPutFileTask extends MethodChannelTask { static Future _getTask(int handle, FirebaseStoragePlatform storage, String path, File file, SettableMetadata? metadata) { - PigeonSettableMetadata? pigeonSettableMetadata; + InternalSettableMetadata? pigeonSettableMetadata; if (defaultTargetPlatform == TargetPlatform.windows) { // TODO(russellwheatley): sending null to windows throws exception so we pass empty metadata pigeonSettableMetadata = diff --git a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/pigeon/messages.pigeon.dart index d5f1ca5617df..ceb6abb3e6e0 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_storage/firebase_storage_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,19 +1,118 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.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: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; +} + +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + return a == b; +} + +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} /// The type of operation that generated the action code from calling /// [TaskState]. -enum PigeonStorageTaskState { +enum InternalStorageTaskState { /// Indicates the task has been paused by the user. paused, @@ -30,8 +129,8 @@ enum PigeonStorageTaskState { error, } -class PigeonStorageFirebaseApp { - PigeonStorageFirebaseApp({ +class InternalStorageFirebaseApp { + InternalStorageFirebaseApp({ required this.appName, this.tenantId, required this.bucket, @@ -43,7 +142,7 @@ class PigeonStorageFirebaseApp { String bucket; - Object encode() { + List _toList() { return [ appName, tenantId, @@ -51,18 +150,41 @@ class PigeonStorageFirebaseApp { ]; } - static PigeonStorageFirebaseApp decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalStorageFirebaseApp decode(Object result) { result as List; - return PigeonStorageFirebaseApp( + return InternalStorageFirebaseApp( appName: result[0]! as String, tenantId: result[1] as String?, bucket: result[2]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalStorageFirebaseApp || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(appName, other.appName) && + _deepEquals(tenantId, other.tenantId) && + _deepEquals(bucket, other.bucket); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonStorageReference { - PigeonStorageReference({ +class InternalStorageReference { + InternalStorageReference({ required this.bucket, required this.fullPath, required this.name, @@ -74,7 +196,7 @@ class PigeonStorageReference { String name; - Object encode() { + List _toList() { return [ bucket, fullPath, @@ -82,39 +204,82 @@ class PigeonStorageReference { ]; } - static PigeonStorageReference decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalStorageReference decode(Object result) { result as List; - return PigeonStorageReference( + return InternalStorageReference( bucket: result[0]! as String, fullPath: result[1]! as String, name: result[2]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalStorageReference || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(bucket, other.bucket) && + _deepEquals(fullPath, other.fullPath) && + _deepEquals(name, other.name); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonFullMetaData { - PigeonFullMetaData({ +class InternalFullMetaData { + InternalFullMetaData({ this.metadata, }); Map? metadata; - Object encode() { + List _toList() { return [ metadata, ]; } - static PigeonFullMetaData decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalFullMetaData decode(Object result) { result as List; - return PigeonFullMetaData( + return InternalFullMetaData( metadata: (result[0] as Map?)?.cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalFullMetaData || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(metadata, other.metadata); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonListOptions { - PigeonListOptions({ +class InternalListOptions { + InternalListOptions({ required this.maxResults, this.pageToken, }); @@ -129,24 +294,45 @@ class PigeonListOptions { /// If provided, listing is resumed from the previous position. String? pageToken; - Object encode() { + List _toList() { return [ maxResults, pageToken, ]; } - static PigeonListOptions decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalListOptions decode(Object result) { result as List; - return PigeonListOptions( + return InternalListOptions( maxResults: result[0]! as int, pageToken: result[1] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalListOptions || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(maxResults, other.maxResults) && + _deepEquals(pageToken, other.pageToken); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonSettableMetadata { - PigeonSettableMetadata({ +class InternalSettableMetadata { + InternalSettableMetadata({ this.cacheControl, this.contentDisposition, this.contentEncoding, @@ -183,7 +369,7 @@ class PigeonSettableMetadata { /// Additional user-defined custom metadata. Map? customMetadata; - Object encode() { + List _toList() { return [ cacheControl, contentDisposition, @@ -194,9 +380,13 @@ class PigeonSettableMetadata { ]; } - static PigeonSettableMetadata decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalSettableMetadata decode(Object result) { result as List; - return PigeonSettableMetadata( + return InternalSettableMetadata( cacheControl: result[0] as String?, contentDisposition: result[1] as String?, contentEncoding: result[2] as String?, @@ -206,22 +396,104 @@ class PigeonSettableMetadata { (result[5] as Map?)?.cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalSettableMetadata || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(cacheControl, other.cacheControl) && + _deepEquals(contentDisposition, other.contentDisposition) && + _deepEquals(contentEncoding, other.contentEncoding) && + _deepEquals(contentLanguage, other.contentLanguage) && + _deepEquals(contentType, other.contentType) && + _deepEquals(customMetadata, other.customMetadata); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class PigeonListResult { - PigeonListResult({ +class InternalStorageTaskSnapShot { + InternalStorageTaskSnapShot({ + required this.bytesTransferred, + this.metadata, + required this.state, + required this.totalBytes, + }); + + int bytesTransferred; + + InternalFullMetaData? metadata; + + InternalStorageTaskState state; + + int totalBytes; + + List _toList() { + return [ + bytesTransferred, + metadata, + state, + totalBytes, + ]; + } + + Object encode() { + return _toList(); + } + + static InternalStorageTaskSnapShot decode(Object result) { + result as List; + return InternalStorageTaskSnapShot( + bytesTransferred: result[0]! as int, + metadata: result[1] as InternalFullMetaData?, + state: result[2]! as InternalStorageTaskState, + totalBytes: result[3]! as int, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalStorageTaskSnapShot || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(bytesTransferred, other.bytesTransferred) && + _deepEquals(metadata, other.metadata) && + _deepEquals(state, other.state) && + _deepEquals(totalBytes, other.totalBytes); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); +} + +class InternalListResult { + InternalListResult({ required this.items, this.pageToken, required this.prefixs, }); - List items; + List items; String? pageToken; - List prefixs; + List prefixs; - Object encode() { + List _toList() { return [ items, pageToken, @@ -229,38 +501,69 @@ class PigeonListResult { ]; } - static PigeonListResult decode(Object result) { + Object encode() { + return _toList(); + } + + static InternalListResult decode(Object result) { result as List; - return PigeonListResult( - items: (result[0] as List?)!.cast(), + return InternalListResult( + items: (result[0]! as List).cast(), pageToken: result[1] as String?, - prefixs: (result[2] as List?)!.cast(), + prefixs: (result[2]! as List).cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! InternalListResult || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(items, other.items) && + _deepEquals(pageToken, other.pageToken) && + _deepEquals(prefixs, other.prefixs); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); } -class _FirebaseStorageHostApiCodec extends StandardMessageCodec { - const _FirebaseStorageHostApiCodec(); +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is PigeonFullMetaData) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonListOptions) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is InternalStorageTaskState) { buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonListResult) { + writeValue(buffer, value.index); + } else if (value is InternalStorageFirebaseApp) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is PigeonSettableMetadata) { + } else if (value is InternalStorageReference) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is PigeonStorageFirebaseApp) { + } else if (value is InternalFullMetaData) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PigeonStorageReference) { + } else if (value is InternalListOptions) { buffer.putUint8(133); writeValue(buffer, value.encode()); + } else if (value is InternalSettableMetadata) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is InternalStorageTaskSnapShot) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is InternalListResult) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -269,18 +572,23 @@ class _FirebaseStorageHostApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: - return PigeonFullMetaData.decode(readValue(buffer)!); case 129: - return PigeonListOptions.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : InternalStorageTaskState.values[value]; case 130: - return PigeonListResult.decode(readValue(buffer)!); + return InternalStorageFirebaseApp.decode(readValue(buffer)!); case 131: - return PigeonSettableMetadata.decode(readValue(buffer)!); + return InternalStorageReference.decode(readValue(buffer)!); case 132: - return PigeonStorageFirebaseApp.decode(readValue(buffer)!); + return InternalFullMetaData.decode(readValue(buffer)!); case 133: - return PigeonStorageReference.decode(readValue(buffer)!); + return InternalListOptions.decode(readValue(buffer)!); + case 134: + return InternalSettableMetadata.decode(readValue(buffer)!); + case 135: + return InternalStorageTaskSnapShot.decode(readValue(buffer)!); + case 136: + return InternalListResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -291,571 +599,427 @@ class FirebaseStorageHostApi { /// Constructor for [FirebaseStorageHostApi]. 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. - FirebaseStorageHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; - - static const MessageCodec codec = _FirebaseStorageHostApiCodec(); - - Future getReferencebyPath( - PigeonStorageFirebaseApp arg_app, - String arg_path, - String? arg_bucket) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_app, arg_path, arg_bucket]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonStorageReference?)!; - } + FirebaseStorageHostApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future getReferencebyPath( + InternalStorageFirebaseApp app, String path, String? bucket) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, path, bucket]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalStorageReference; } Future setMaxOperationRetryTime( - PigeonStorageFirebaseApp arg_app, int arg_time) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_time]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + InternalStorageFirebaseApp app, int time) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, time]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setMaxUploadRetryTime( - PigeonStorageFirebaseApp arg_app, int arg_time) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_time]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + InternalStorageFirebaseApp app, int time) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, time]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future setMaxDownloadRetryTime( - PigeonStorageFirebaseApp arg_app, int arg_time) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_time]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + InternalStorageFirebaseApp app, int time) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, time]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future useStorageEmulator( - PigeonStorageFirebaseApp arg_app, String arg_host, int arg_port) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_app, arg_host, arg_port]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + InternalStorageFirebaseApp app, String host, int port) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, host, port]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future referenceDelete(PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_reference]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } + Future referenceDelete(InternalStorageFirebaseApp app, + InternalStorageReference reference) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } - Future referenceGetDownloadURL(PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_reference]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + Future referenceGetDownloadURL(InternalStorageFirebaseApp app, + InternalStorageReference reference) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } - Future referenceGetMetaData( - PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_reference]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonFullMetaData?)!; - } + Future referenceGetMetaData( + InternalStorageFirebaseApp app, + InternalStorageReference reference) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalFullMetaData; } - Future referenceList( - PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference, - PigeonListOptions arg_options) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_app, arg_reference, arg_options]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonListResult?)!; - } + Future referenceList(InternalStorageFirebaseApp app, + InternalStorageReference reference, InternalListOptions options) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference, options]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalListResult; } - Future referenceListAll(PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_reference]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonListResult?)!; - } + Future referenceListAll(InternalStorageFirebaseApp app, + InternalStorageReference reference) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalListResult; } - Future referenceGetData(PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference, int arg_maxSize) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_app, arg_reference, arg_maxSize]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return (replyList[0] as Uint8List?); - } + Future referenceGetData(InternalStorageFirebaseApp app, + InternalStorageReference reference, int maxSize) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference, maxSize]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + return pigeonVar_replyValue as Uint8List?; } Future referencePutData( - PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference, - Uint8List arg_data, - PigeonSettableMetadata arg_settableMetaData, - int arg_handle) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send([ - arg_app, - arg_reference, - arg_data, - arg_settableMetaData, - arg_handle - ]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + InternalStorageFirebaseApp app, + InternalStorageReference reference, + Uint8List data, + InternalSettableMetadata settableMetaData, + int handle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([app, reference, data, settableMetaData, handle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future referencePutString( - PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference, - String arg_data, - int arg_format, - PigeonSettableMetadata arg_settableMetaData, - int arg_handle) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send([ - arg_app, - arg_reference, - arg_data, - arg_format, - arg_settableMetaData, - arg_handle - ]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + InternalStorageFirebaseApp app, + InternalStorageReference reference, + String data, + int format, + InternalSettableMetadata settableMetaData, + int handle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [app, reference, data, format, settableMetaData, handle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } Future referencePutFile( - PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference, - String arg_filePath, - PigeonSettableMetadata? arg_settableMetaData, - int arg_handle) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send([ - arg_app, - arg_reference, - arg_filePath, - arg_settableMetaData, - arg_handle - ]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + InternalStorageFirebaseApp app, + InternalStorageReference reference, + String filePath, + InternalSettableMetadata? settableMetaData, + int handle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([app, reference, filePath, settableMetaData, handle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } - Future referenceDownloadFile( - PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference, - String arg_filePath, - int arg_handle) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_app, arg_reference, arg_filePath, arg_handle]) - as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as String?)!; - } + Future referenceDownloadFile(InternalStorageFirebaseApp app, + InternalStorageReference reference, String filePath, int handle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference, filePath, handle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; } - Future referenceUpdateMetadata( - PigeonStorageFirebaseApp arg_app, - PigeonStorageReference arg_reference, - PigeonSettableMetadata arg_metadata) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_reference, arg_metadata]) - as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as PigeonFullMetaData?)!; - } + Future referenceUpdateMetadata( + InternalStorageFirebaseApp app, + InternalStorageReference reference, + InternalSettableMetadata metadata) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, reference, metadata]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as InternalFullMetaData; } - Future> taskPause( - PigeonStorageFirebaseApp arg_app, int arg_handle) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_handle]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as Map?)!.cast(); - } + Future> taskPause( + InternalStorageFirebaseApp app, int handle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, handle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as Map) + .cast(); } - Future> taskResume( - PigeonStorageFirebaseApp arg_app, int arg_handle) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_handle]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as Map?)!.cast(); - } + Future> taskResume( + InternalStorageFirebaseApp app, int handle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, handle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as Map) + .cast(); } - Future> taskCancel( - PigeonStorageFirebaseApp arg_app, int arg_handle) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_app, arg_handle]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as Map?)!.cast(); - } + Future> taskCancel( + InternalStorageFirebaseApp app, int handle) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([app, handle]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return (pigeonVar_replyValue! as Map) + .cast(); } } diff --git a/packages/firebase_storage/firebase_storage_platform_interface/pigeons/messages.dart b/packages/firebase_storage/firebase_storage_platform_interface/pigeons/messages.dart index bd36f3a6b57d..2cade8ddbef5 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/pigeons/messages.dart +++ b/packages/firebase_storage/firebase_storage_platform_interface/pigeons/messages.dart @@ -24,8 +24,8 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', ), ) -class PigeonStorageFirebaseApp { - const PigeonStorageFirebaseApp({ +class InternalStorageFirebaseApp { + const InternalStorageFirebaseApp({ required this.appName, required this.tenantId, required this.bucket, @@ -38,7 +38,7 @@ class PigeonStorageFirebaseApp { /// The type of operation that generated the action code from calling /// [TaskState]. -enum PigeonStorageTaskState { +enum InternalStorageTaskState { /// Indicates the task has been paused by the user. paused, @@ -55,8 +55,8 @@ enum PigeonStorageTaskState { error, } -class PigeonStorageReference { - const PigeonStorageReference({ +class InternalStorageReference { + const InternalStorageReference({ required this.bucket, required this.fullPath, required this.name, @@ -67,15 +67,15 @@ class PigeonStorageReference { final String name; } -class PigeonFullMetaData { - const PigeonFullMetaData({ +class InternalFullMetaData { + const InternalFullMetaData({ required this.metadata, }); final Map? metadata; } -class PigeonListOptions { - const PigeonListOptions({ +class InternalListOptions { + const InternalListOptions({ required this.maxResults, this.pageToken, }); @@ -91,9 +91,9 @@ class PigeonListOptions { final String? pageToken; } -class PigeonSettableMetadata { - /// Creates a new [PigeonSettableMetadata] instance. - PigeonSettableMetadata({ +class InternalSettableMetadata { + /// Creates a new [InternalSettableMetadata] instance. + InternalSettableMetadata({ this.cacheControl, this.contentDisposition, this.contentEncoding, @@ -131,8 +131,8 @@ class PigeonSettableMetadata { final Map? customMetadata; } -class PigeonStorageTaskSnapShot { - const PigeonStorageTaskSnapShot({ +class InternalStorageTaskSnapShot { + const InternalStorageTaskSnapShot({ required this.bytesTransferred, required this.metadata, required this.state, @@ -140,50 +140,50 @@ class PigeonStorageTaskSnapShot { }); final int bytesTransferred; - final PigeonFullMetaData? metadata; - final PigeonStorageTaskState state; + final InternalFullMetaData? metadata; + final InternalStorageTaskState state; final int totalBytes; } -class PigeonListResult { - const PigeonListResult({ +class InternalListResult { + const InternalListResult({ required this.items, required this.pageToken, required this.prefixs, }); - final List items; + final List items; final String? pageToken; - final List prefixs; + final List prefixs; } @HostApi(dartHostTestHandler: 'TestFirebaseStorageHostApi') abstract class FirebaseStorageHostApi { @async - PigeonStorageReference getReferencebyPath( - PigeonStorageFirebaseApp app, + InternalStorageReference getReferencebyPath( + InternalStorageFirebaseApp app, String path, String? bucket, ); @async void setMaxOperationRetryTime( - PigeonStorageFirebaseApp app, + InternalStorageFirebaseApp app, int time, ); @async void setMaxUploadRetryTime( - PigeonStorageFirebaseApp app, + InternalStorageFirebaseApp app, int time, ); @async void setMaxDownloadRetryTime( - PigeonStorageFirebaseApp app, + InternalStorageFirebaseApp app, int time, ); @async void useStorageEmulator( - PigeonStorageFirebaseApp app, + InternalStorageFirebaseApp app, String host, int port, ); @@ -192,101 +192,101 @@ abstract class FirebaseStorageHostApi { @async void referenceDelete( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, ); @async String referenceGetDownloadURL( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, ); @async - PigeonFullMetaData referenceGetMetaData( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalFullMetaData referenceGetMetaData( + InternalStorageFirebaseApp app, + InternalStorageReference reference, ); @async - PigeonListResult referenceList( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, - PigeonListOptions options, + InternalListResult referenceList( + InternalStorageFirebaseApp app, + InternalStorageReference reference, + InternalListOptions options, ); @async - PigeonListResult referenceListAll( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalListResult referenceListAll( + InternalStorageFirebaseApp app, + InternalStorageReference reference, ); @async Uint8List? referenceGetData( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, int maxSize, ); @async String referencePutData( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, Uint8List data, - PigeonSettableMetadata settableMetaData, + InternalSettableMetadata settableMetaData, int handle, ); @async String referencePutString( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, String data, int format, - PigeonSettableMetadata settableMetaData, + InternalSettableMetadata settableMetaData, int handle, ); @async String referencePutFile( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, String filePath, - PigeonSettableMetadata? settableMetaData, + InternalSettableMetadata? settableMetaData, int handle, ); @async String referenceDownloadFile( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, String filePath, int handle, ); @async - PigeonFullMetaData referenceUpdateMetadata( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, - PigeonSettableMetadata metadata, + InternalFullMetaData referenceUpdateMetadata( + InternalStorageFirebaseApp app, + InternalStorageReference reference, + InternalSettableMetadata metadata, ); // APIs for Task class @async Map taskPause( - PigeonStorageFirebaseApp app, + InternalStorageFirebaseApp app, int handle, ); @async Map taskResume( - PigeonStorageFirebaseApp app, + InternalStorageFirebaseApp app, int handle, ); @async Map taskCancel( - PigeonStorageFirebaseApp app, + InternalStorageFirebaseApp app, int handle, ); } diff --git a/packages/firebase_storage/firebase_storage_platform_interface/pubspec.yaml b/packages/firebase_storage/firebase_storage_platform_interface/pubspec.yaml index 33f2acba76e8..fc2d1d2a11de 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/pubspec.yaml +++ b/packages/firebase_storage/firebase_storage_platform_interface/pubspec.yaml @@ -1,25 +1,26 @@ name: firebase_storage_platform_interface description: A common platform interface for the firebase_storage plugin. -version: 5.2.19 +version: 6.0.3 +resolution: workspace homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_storage/firebase_storage_platform_interface repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_storage/firebase_storage_platform_interface environment: - sdk: '>=3.2.0 <4.0.0' - flutter: '>=3.3.0' + sdk: '^3.6.0' + flutter: '>=3.27.0' dependencies: - _flutterfire_internals: ^1.3.68 + _flutterfire_internals: ^1.3.73 collection: ^1.15.0 - firebase_core: ^4.6.0 + firebase_core: ^4.11.0 flutter: sdk: flutter meta: ^1.8.0 plugin_platform_interface: ^2.1.3 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 - pigeon: 11.0.1 + pigeon: 26.3.4 diff --git a/packages/firebase_storage/firebase_storage_platform_interface/test/pigeon/test_api.dart b/packages/firebase_storage/firebase_storage_platform_interface/test/pigeon/test_api.dart index d11d0f21236b..13214ad904ab 100644 --- a/packages/firebase_storage/firebase_storage_platform_interface/test/pigeon/test_api.dart +++ b/packages/firebase_storage/firebase_storage_platform_interface/test/pigeon/test_api.dart @@ -1,39 +1,49 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. 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 (v26.3.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, omit_obvious_local_variable_types // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; -import 'dart:typed_data' show Uint8List; -import 'package:firebase_storage_platform_interface/src/pigeon/messages.pigeon.dart'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -class _TestFirebaseStorageHostApiCodec extends StandardMessageCodec { - const _TestFirebaseStorageHostApiCodec(); +import 'package:firebase_storage_platform_interface/src/pigeon/messages.pigeon.dart'; + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is PigeonFullMetaData) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is PigeonListOptions) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is InternalStorageTaskState) { buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is PigeonListResult) { + writeValue(buffer, value.index); + } else if (value is InternalStorageFirebaseApp) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is PigeonSettableMetadata) { + } else if (value is InternalStorageReference) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is PigeonStorageFirebaseApp) { + } else if (value is InternalFullMetaData) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PigeonStorageReference) { + } else if (value is InternalListOptions) { buffer.putUint8(133); writeValue(buffer, value.encode()); + } else if (value is InternalSettableMetadata) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is InternalStorageTaskSnapShot) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is InternalListResult) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -42,18 +52,23 @@ class _TestFirebaseStorageHostApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: - return PigeonFullMetaData.decode(readValue(buffer)!); case 129: - return PigeonListOptions.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : InternalStorageTaskState.values[value]; case 130: - return PigeonListResult.decode(readValue(buffer)!); + return InternalStorageFirebaseApp.decode(readValue(buffer)!); case 131: - return PigeonSettableMetadata.decode(readValue(buffer)!); + return InternalStorageReference.decode(readValue(buffer)!); case 132: - return PigeonStorageFirebaseApp.decode(readValue(buffer)!); + return InternalFullMetaData.decode(readValue(buffer)!); case 133: - return PigeonStorageReference.decode(readValue(buffer)!); + return InternalListOptions.decode(readValue(buffer)!); + case 134: + return InternalSettableMetadata.decode(readValue(buffer)!); + case 135: + return InternalStorageTaskSnapShot.decode(readValue(buffer)!); + case 136: + return InternalListResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -63,671 +78,667 @@ class _TestFirebaseStorageHostApiCodec extends StandardMessageCodec { abstract class TestFirebaseStorageHostApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec codec = _TestFirebaseStorageHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - Future getReferencebyPath( - PigeonStorageFirebaseApp app, String path, String? bucket); + Future getReferencebyPath( + InternalStorageFirebaseApp app, String path, String? bucket); - Future setMaxOperationRetryTime(PigeonStorageFirebaseApp app, int time); + Future setMaxOperationRetryTime( + InternalStorageFirebaseApp app, int time); - Future setMaxUploadRetryTime(PigeonStorageFirebaseApp app, int time); + Future setMaxUploadRetryTime(InternalStorageFirebaseApp app, int time); - Future setMaxDownloadRetryTime(PigeonStorageFirebaseApp app, int time); + Future setMaxDownloadRetryTime( + InternalStorageFirebaseApp app, int time); Future useStorageEmulator( - PigeonStorageFirebaseApp app, String host, int port); + InternalStorageFirebaseApp app, String host, int port); Future referenceDelete( - PigeonStorageFirebaseApp app, PigeonStorageReference reference); + InternalStorageFirebaseApp app, InternalStorageReference reference); Future referenceGetDownloadURL( - PigeonStorageFirebaseApp app, PigeonStorageReference reference); + InternalStorageFirebaseApp app, InternalStorageReference reference); - Future referenceGetMetaData( - PigeonStorageFirebaseApp app, PigeonStorageReference reference); + Future referenceGetMetaData( + InternalStorageFirebaseApp app, InternalStorageReference reference); - Future referenceList(PigeonStorageFirebaseApp app, - PigeonStorageReference reference, PigeonListOptions options); + Future referenceList(InternalStorageFirebaseApp app, + InternalStorageReference reference, InternalListOptions options); - Future referenceListAll( - PigeonStorageFirebaseApp app, PigeonStorageReference reference); + Future referenceListAll( + InternalStorageFirebaseApp app, InternalStorageReference reference); - Future referenceGetData(PigeonStorageFirebaseApp app, - PigeonStorageReference reference, int maxSize); + Future referenceGetData(InternalStorageFirebaseApp app, + InternalStorageReference reference, int maxSize); Future referencePutData( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, Uint8List data, - PigeonSettableMetadata settableMetaData, + InternalSettableMetadata settableMetaData, int handle); Future referencePutString( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, String data, int format, - PigeonSettableMetadata settableMetaData, + InternalSettableMetadata settableMetaData, int handle); Future referencePutFile( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, + InternalStorageFirebaseApp app, + InternalStorageReference reference, String filePath, - PigeonSettableMetadata? settableMetaData, + InternalSettableMetadata? settableMetaData, int handle); - Future referenceDownloadFile(PigeonStorageFirebaseApp app, - PigeonStorageReference reference, String filePath, int handle); + Future referenceDownloadFile(InternalStorageFirebaseApp app, + InternalStorageReference reference, String filePath, int handle); - Future referenceUpdateMetadata( - PigeonStorageFirebaseApp app, - PigeonStorageReference reference, - PigeonSettableMetadata metadata); + Future referenceUpdateMetadata( + InternalStorageFirebaseApp app, + InternalStorageReference reference, + InternalSettableMetadata metadata); - Future> taskPause( - PigeonStorageFirebaseApp app, int handle); + Future> taskPause( + InternalStorageFirebaseApp app, int handle); - Future> taskResume( - PigeonStorageFirebaseApp app, int handle); + Future> taskResume( + InternalStorageFirebaseApp app, int handle); - Future> taskCancel( - PigeonStorageFirebaseApp app, int handle); + Future> taskCancel( + InternalStorageFirebaseApp app, int handle); - static void setup(TestFirebaseStorageHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setUp( + TestFirebaseStorageHostApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath$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.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath was null, expected non-null PigeonStorageFirebaseApp.'); - final String? arg_path = (args[1] as String?); - assert(arg_path != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.getReferencebyPath was null, expected non-null String.'); - final String? arg_bucket = (args[2] as String?); - final PigeonStorageReference output = - await api.getReferencebyPath(arg_app!, arg_path!, arg_bucket); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final String arg_path = args[1]! as String; + final String? arg_bucket = args[2] as String?; + try { + final InternalStorageReference output = + await api.getReferencebyPath(arg_app, arg_path, arg_bucket); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime$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.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime was null, expected non-null PigeonStorageFirebaseApp.'); - final int? arg_time = (args[1] as int?); - assert(arg_time != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxOperationRetryTime was null, expected non-null int.'); - await api.setMaxOperationRetryTime(arg_app!, arg_time!); - return []; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final int arg_time = args[1]! as int; + try { + await api.setMaxOperationRetryTime(arg_app, arg_time); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime$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.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime was null, expected non-null PigeonStorageFirebaseApp.'); - final int? arg_time = (args[1] as int?); - assert(arg_time != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxUploadRetryTime was null, expected non-null int.'); - await api.setMaxUploadRetryTime(arg_app!, arg_time!); - return []; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final int arg_time = args[1]! as int; + try { + await api.setMaxUploadRetryTime(arg_app, arg_time); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime$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.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime was null, expected non-null PigeonStorageFirebaseApp.'); - final int? arg_time = (args[1] as int?); - assert(arg_time != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.setMaxDownloadRetryTime was null, expected non-null int.'); - await api.setMaxDownloadRetryTime(arg_app!, arg_time!); - return []; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final int arg_time = args[1]! as int; + try { + await api.setMaxDownloadRetryTime(arg_app, arg_time); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator$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.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator was null, expected non-null PigeonStorageFirebaseApp.'); - final String? arg_host = (args[1] as String?); - assert(arg_host != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator was null, expected non-null String.'); - final int? arg_port = (args[2] as int?); - assert(arg_port != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.useStorageEmulator was null, expected non-null int.'); - await api.useStorageEmulator(arg_app!, arg_host!, arg_port!); - return []; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final String arg_host = args[1]! as String; + final int arg_port = args[2]! as int; + try { + await api.useStorageEmulator(arg_app, arg_host, arg_port); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDelete was null, expected non-null PigeonStorageReference.'); - await api.referenceDelete(arg_app!, arg_reference!); - return []; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + try { + await api.referenceDelete(arg_app, arg_reference); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetDownloadURL was null, expected non-null PigeonStorageReference.'); - final String output = - await api.referenceGetDownloadURL(arg_app!, arg_reference!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + try { + final String output = + await api.referenceGetDownloadURL(arg_app, arg_reference); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetMetaData was null, expected non-null PigeonStorageReference.'); - final PigeonFullMetaData output = - await api.referenceGetMetaData(arg_app!, arg_reference!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + try { + final InternalFullMetaData output = + await api.referenceGetMetaData(arg_app, arg_reference); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList was null, expected non-null PigeonStorageReference.'); - final PigeonListOptions? arg_options = - (args[2] as PigeonListOptions?); - assert(arg_options != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceList was null, expected non-null PigeonListOptions.'); - final PigeonListResult output = - await api.referenceList(arg_app!, arg_reference!, arg_options!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + final InternalListOptions arg_options = + args[2]! as InternalListOptions; + try { + final InternalListResult output = + await api.referenceList(arg_app, arg_reference, arg_options); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceListAll was null, expected non-null PigeonStorageReference.'); - final PigeonListResult output = - await api.referenceListAll(arg_app!, arg_reference!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + try { + final InternalListResult output = + await api.referenceListAll(arg_app, arg_reference); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData was null, expected non-null PigeonStorageReference.'); - final int? arg_maxSize = (args[2] as int?); - assert(arg_maxSize != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceGetData was null, expected non-null int.'); - final Uint8List? output = await api.referenceGetData( - arg_app!, arg_reference!, arg_maxSize!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + final int arg_maxSize = args[2]! as int; + try { + final Uint8List? output = + await api.referenceGetData(arg_app, arg_reference, arg_maxSize); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData was null, expected non-null PigeonStorageReference.'); - final Uint8List? arg_data = (args[2] as Uint8List?); - assert(arg_data != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData was null, expected non-null Uint8List.'); - final PigeonSettableMetadata? arg_settableMetaData = - (args[3] as PigeonSettableMetadata?); - assert(arg_settableMetaData != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData was null, expected non-null PigeonSettableMetadata.'); - final int? arg_handle = (args[4] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutData was null, expected non-null int.'); - final String output = await api.referencePutData(arg_app!, - arg_reference!, arg_data!, arg_settableMetaData!, arg_handle!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + final Uint8List arg_data = args[2]! as Uint8List; + final InternalSettableMetadata arg_settableMetaData = + args[3]! as InternalSettableMetadata; + final int arg_handle = args[4]! as int; + try { + final String output = await api.referencePutData(arg_app, + arg_reference, arg_data, arg_settableMetaData, arg_handle); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString was null, expected non-null PigeonStorageReference.'); - final String? arg_data = (args[2] as String?); - assert(arg_data != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString was null, expected non-null String.'); - final int? arg_format = (args[3] as int?); - assert(arg_format != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString was null, expected non-null int.'); - final PigeonSettableMetadata? arg_settableMetaData = - (args[4] as PigeonSettableMetadata?); - assert(arg_settableMetaData != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString was null, expected non-null PigeonSettableMetadata.'); - final int? arg_handle = (args[5] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutString was null, expected non-null int.'); - final String output = await api.referencePutString( - arg_app!, - arg_reference!, - arg_data!, - arg_format!, - arg_settableMetaData!, - arg_handle!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + final String arg_data = args[2]! as String; + final int arg_format = args[3]! as int; + final InternalSettableMetadata arg_settableMetaData = + args[4]! as InternalSettableMetadata; + final int arg_handle = args[5]! as int; + try { + final String output = await api.referencePutString( + arg_app, + arg_reference, + arg_data, + arg_format, + arg_settableMetaData, + arg_handle); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile was null, expected non-null PigeonStorageReference.'); - final String? arg_filePath = (args[2] as String?); - assert(arg_filePath != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile was null, expected non-null String.'); - final PigeonSettableMetadata? arg_settableMetaData = - (args[3] as PigeonSettableMetadata?); - final int? arg_handle = (args[4] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referencePutFile was null, expected non-null int.'); - final String output = await api.referencePutFile(arg_app!, - arg_reference!, arg_filePath!, arg_settableMetaData, arg_handle!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + final String arg_filePath = args[2]! as String; + final InternalSettableMetadata? arg_settableMetaData = + args[3] as InternalSettableMetadata?; + final int arg_handle = args[4]! as int; + try { + final String output = await api.referencePutFile(arg_app, + arg_reference, arg_filePath, arg_settableMetaData, arg_handle); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile was null, expected non-null PigeonStorageReference.'); - final String? arg_filePath = (args[2] as String?); - assert(arg_filePath != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile was null, expected non-null String.'); - final int? arg_handle = (args[3] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceDownloadFile was null, expected non-null int.'); - final String output = await api.referenceDownloadFile( - arg_app!, arg_reference!, arg_filePath!, arg_handle!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + final String arg_filePath = args[2]! as String; + final int arg_handle = args[3]! as int; + try { + final String output = await api.referenceDownloadFile( + arg_app, arg_reference, arg_filePath, arg_handle); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata$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.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata was null, expected non-null PigeonStorageFirebaseApp.'); - final PigeonStorageReference? arg_reference = - (args[1] as PigeonStorageReference?); - assert(arg_reference != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata was null, expected non-null PigeonStorageReference.'); - final PigeonSettableMetadata? arg_metadata = - (args[2] as PigeonSettableMetadata?); - assert(arg_metadata != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.referenceUpdateMetadata was null, expected non-null PigeonSettableMetadata.'); - final PigeonFullMetaData output = await api.referenceUpdateMetadata( - arg_app!, arg_reference!, arg_metadata!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final InternalStorageReference arg_reference = + args[1]! as InternalStorageReference; + final InternalSettableMetadata arg_metadata = + args[2]! as InternalSettableMetadata; + try { + final InternalFullMetaData output = await api + .referenceUpdateMetadata(arg_app, arg_reference, arg_metadata); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause$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.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause was null, expected non-null PigeonStorageFirebaseApp.'); - final int? arg_handle = (args[1] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskPause was null, expected non-null int.'); - final Map output = - await api.taskPause(arg_app!, arg_handle!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final int arg_handle = args[1]! as int; + try { + final Map output = + await api.taskPause(arg_app, arg_handle); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume$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.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume was null, expected non-null PigeonStorageFirebaseApp.'); - final int? arg_handle = (args[1] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskResume was null, expected non-null int.'); - final Map output = - await api.taskResume(arg_app!, arg_handle!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final int arg_handle = args[1]! as int; + try { + final Map output = + await api.taskResume(arg_app, arg_handle); + 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.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel', - codec, + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel$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.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel was null.'); - final List args = (message as List?)!; - final PigeonStorageFirebaseApp? arg_app = - (args[0] as PigeonStorageFirebaseApp?); - assert(arg_app != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel was null, expected non-null PigeonStorageFirebaseApp.'); - final int? arg_handle = (args[1] as int?); - assert(arg_handle != null, - 'Argument for dev.flutter.pigeon.firebase_storage_platform_interface.FirebaseStorageHostApi.taskCancel was null, expected non-null int.'); - final Map output = - await api.taskCancel(arg_app!, arg_handle!); - return [output]; + final List args = message! as List; + final InternalStorageFirebaseApp arg_app = + args[0]! as InternalStorageFirebaseApp; + final int arg_handle = args[1]! as int; + try { + final Map output = + await api.taskCancel(arg_app, arg_handle); + 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/firebase_storage/firebase_storage_web/CHANGELOG.md b/packages/firebase_storage/firebase_storage_web/CHANGELOG.md index 001a07a76582..c2e357a071b3 100644 --- a/packages/firebase_storage/firebase_storage_web/CHANGELOG.md +++ b/packages/firebase_storage/firebase_storage_web/CHANGELOG.md @@ -1,3 +1,23 @@ +## 3.11.9 + + - Update a dependency to the latest release. + +## 3.11.8 + + - Update a dependency to the latest release. + +## 3.11.7 + + - Update a dependency to the latest release. + +## 3.11.6 + + - **REFACTOR**: move all packages to workspace ([#18182](https://github.com/firebase/flutterfire/issues/18182)). ([6cdfcb10](https://github.com/firebase/flutterfire/commit/6cdfcb103da7be46ccb190d7e107d8c537aa1ff8)) + +## 3.11.5 + + - Update a dependency to the latest release. + ## 3.11.4 - **REFACTOR**: fix formatting and analysis issues across the repo ([#18124](https://github.com/firebase/flutterfire/issues/18124)). ([ab79fd93](https://github.com/firebase/flutterfire/commit/ab79fd93ee4ccfeb478687623134b1cf8ab71c74)) diff --git a/packages/firebase_storage/firebase_storage_web/lib/src/firebase_storage_version.dart b/packages/firebase_storage/firebase_storage_web/lib/src/firebase_storage_version.dart index 9bc0658bd391..68ee68ba7f8f 100644 --- a/packages/firebase_storage/firebase_storage_web/lib/src/firebase_storage_version.dart +++ b/packages/firebase_storage/firebase_storage_web/lib/src/firebase_storage_version.dart @@ -13,4 +13,4 @@ // limitations under the License. /// generated version number for the package, do not manually edit -const packageVersion = '13.2.0'; +const packageVersion = '13.4.3'; diff --git a/packages/firebase_storage/firebase_storage_web/pubspec.yaml b/packages/firebase_storage/firebase_storage_web/pubspec.yaml index 1defb01f2e5e..a6cc41d54e2e 100644 --- a/packages/firebase_storage/firebase_storage_web/pubspec.yaml +++ b/packages/firebase_storage/firebase_storage_web/pubspec.yaml @@ -2,18 +2,19 @@ name: firebase_storage_web description: The web implementation of firebase_storage homepage: https://github.com/firebase/flutterfire/tree/main/packages/firebase_storage/firebase_storage_web repository: https://github.com/firebase/flutterfire/tree/main/packages/firebase_storage/firebase_storage_web -version: 3.11.4 +version: 3.11.9 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - _flutterfire_internals: ^1.3.68 + _flutterfire_internals: ^1.3.73 async: ^2.5.0 - firebase_core: ^4.6.0 - firebase_core_web: ^3.5.1 - firebase_storage_platform_interface: ^5.2.19 + firebase_core: ^4.11.0 + firebase_core_web: ^3.9.0 + firebase_storage_platform_interface: ^6.0.3 flutter: sdk: flutter flutter_web_plugins: @@ -23,7 +24,7 @@ dependencies: web: ^1.0.0 dev_dependencies: - firebase_core_platform_interface: ^6.0.3 + firebase_core_platform_interface: ^7.1.0 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index c7021d5c7077..a88ddd10eb19 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,14 +1,433 @@ name: flutterfire_workspace environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '^3.6.0' + +workspace: + - packages/_flutterfire_internals + - packages/cloud_firestore/cloud_firestore + - packages/cloud_firestore/cloud_firestore/example + - packages/cloud_firestore/cloud_firestore/pipeline_example + - packages/cloud_firestore/cloud_firestore_platform_interface + - packages/cloud_firestore/cloud_firestore_web + - packages/cloud_functions/cloud_functions + - packages/cloud_functions/cloud_functions/example + - packages/cloud_functions/cloud_functions_platform_interface + - packages/cloud_functions/cloud_functions_web + - packages/firebase_ai/firebase_ai + - packages/firebase_ai/firebase_ai/example + - packages/firebase_analytics/firebase_analytics + - packages/firebase_analytics/firebase_analytics/example + - packages/firebase_analytics/firebase_analytics_platform_interface + - packages/firebase_analytics/firebase_analytics_web + - packages/firebase_app_check/firebase_app_check + - packages/firebase_app_check/firebase_app_check/example + - packages/firebase_app_check/firebase_app_check_platform_interface + - packages/firebase_app_check/firebase_app_check_web + - packages/firebase_app_installations/firebase_app_installations + - packages/firebase_app_installations/firebase_app_installations/example + - packages/firebase_app_installations/firebase_app_installations_platform_interface + - packages/firebase_app_installations/firebase_app_installations_web + - packages/firebase_auth/firebase_auth + - packages/firebase_auth/firebase_auth/example + - packages/firebase_auth/firebase_auth_platform_interface + - packages/firebase_auth/firebase_auth_web + - packages/firebase_core/firebase_core + - packages/firebase_core/firebase_core/example + - packages/firebase_core/firebase_core_platform_interface + - packages/firebase_core/firebase_core_web + - packages/firebase_crashlytics/firebase_crashlytics + - packages/firebase_crashlytics/firebase_crashlytics/example + - packages/firebase_crashlytics/firebase_crashlytics_platform_interface + - packages/firebase_data_connect/firebase_data_connect + - packages/firebase_data_connect/firebase_data_connect/example + - packages/firebase_database/firebase_database + - packages/firebase_database/firebase_database/example + - packages/firebase_database/firebase_database_platform_interface + - packages/firebase_database/firebase_database_web + - packages/firebase_in_app_messaging/firebase_in_app_messaging + - packages/firebase_in_app_messaging/firebase_in_app_messaging/example + - packages/firebase_in_app_messaging/firebase_in_app_messaging_platform_interface + - packages/firebase_messaging/firebase_messaging + - packages/firebase_messaging/firebase_messaging/example + - packages/firebase_messaging/firebase_messaging_platform_interface + - packages/firebase_messaging/firebase_messaging_web + - packages/firebase_ml_model_downloader/firebase_ml_model_downloader + - packages/firebase_ml_model_downloader/firebase_ml_model_downloader/example + - packages/firebase_ml_model_downloader/firebase_ml_model_downloader_platform_interface + - packages/firebase_performance/firebase_performance + - packages/firebase_performance/firebase_performance/example + - packages/firebase_performance/firebase_performance_platform_interface + - packages/firebase_performance/firebase_performance_web + - packages/firebase_remote_config/firebase_remote_config + - packages/firebase_remote_config/firebase_remote_config/example + - packages/firebase_remote_config/firebase_remote_config_platform_interface + - packages/firebase_remote_config/firebase_remote_config_web + - packages/firebase_storage/firebase_storage + - packages/firebase_storage/firebase_storage/example + - packages/firebase_storage/firebase_storage_platform_interface + - packages/firebase_storage/firebase_storage_web + - tests dev_dependencies: cli_util: ^0.4.1 glob: ^2.1.2 - intl: ^0.19.0 - melos: ^5.3.0 + intl: ^0.20.2 + melos: ^7.5.1 path: ^1.9.0 pub_semver: ^2.1.4 yaml: ^3.1.2 - + +melos: + repository: https://github.com/firebase/flutterfire + resolution: workspace + + command: + version: + # Generate commit links in package changelogs. + linkToCommits: true + # # Only allow versioning to happen on main branch. + # branch: main + # Additionally build a changelog at the root of the workspace. + workspaceChangelog: true + hooks: + preCommit: | + dart run scripts/generate_firebaseai_version.dart && \ + dart run scripts/generate_dataconnect_version.dart && \ + dart run scripts/generate_versions_web.dart && \ + dart run scripts/generate_versions_spm.dart && \ + git add packages/firebase_data_connect/firebase_data_connect/lib/src/dataconnect_version.dart && git add packages/*/*_web/lib/src/*_version.dart && git add packages/*/*/ios/*/Package.swift packages/*/*/macos/*/Package.swift && git add packages/*/*/ios/*/Sources/*/Constants.swift + + bootstrap: + # It seems so that running "pub get" in parallel has some issues (like + # https://github.com/dart-lang/pub/issues/3404). Disabling this feature + # makes the CI much more stable. + runPubGetInParallel: false + usePubspecOverrides: false + + scripts: + lint:all: + run: melos run analyze-ci && melos run format-ci + description: Run all static analysis checks. + + validate:workspace: + run: dart scripts/validate_workspace.dart + description: Validate that all packages are listed in the workspace. + + analyze-ci: + # We are setting the concurrency to 1 because a higher concurrency can crash + # the analysis server on low performance machines (like GitHub Actions). + run: | + melos exec -c 1 -- \ + dart analyze . --fatal-infos + description: | + Run `dart analyze` in all packages. + - Note: you can also rely on your IDEs Dart Analysis / Issues window. + + firebase:emulator: + run: | + cd .github/workflows/scripts && ./start-firebase-emulator.sh + description: | + Start the Firebase emulator suite. Used by Functions, Firestore, Auth and Storage + integration testing. + - Requires Node.js and NPM installed. + + format-ci: + run: | + dart pub global run flutter_plugin_tools format && \ + swiftformat . + description: | + Formats the code of all packages (Java, Objective-C, and Dart). + - Requires `flutter_plugin_tools` (`pub global activate flutter_plugin_tools`). + - Requires `git`. + - Requires `clang-format` (can be installed via Brew on MacOS). + - Requires `swiftformat` (can be installed via Brew on macOS). + + build:all: + run: | + melos run build:example_ios_pub --no-select && \ + melos run build:example_android_pub --no-select && \ + melos run build:example_macos --no-select + description: Build all example apps. + + build:example_android: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter build apk" + description: Build a specific example app for Android. + packageFilters: + dirExists: + - android + scope: '*example*' + + build:example_android_pub: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter build apk" + description: Build a specific example app for Android. + packageFilters: + dirExists: + - android + scope: '*example*' + + build:example_ios: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter build ios --no-codesign" + description: Build a specific example app for iOS. + packageFilters: + dirExists: + - ios + scope: '*example*' + + build:example_ios_pub: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter build ios --no-codesign" + description: Build a specific example app for iOS. + packageFilters: + dirExists: + - ios + scope: '*example*' + + build:example_macos: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter build macos" + description: | + Build a specific example app for macOS. + packageFilters: + dirExists: + - macos + scope: '*example*' + + test:all: + run: | + melos run test --no-select && \ + melos run test:web --no-select && \ + melos run test:e2e --no-select + description: | + Run all tests available. + + test: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter test" + description: Run `flutter test` for a specific package. + packageFilters: + dirExists: + - test + ignore: + - '*web*' + - '*example*' + + test:web: + run: | + melos exec -c 1 --fail-fast -- \ + "flutter test --platform=chrome" + description: Run `flutter test --platform=chrome` for a specific '*web' package. + packageFilters: + dirExists: + - test + scope: '*web*' + + test:e2e: + run: | + melos exec -c 1 --fail-fast -- \ + "flutter test integration_test/e2e_test.dart" + description: | + Run all e2e tests. + packageFilters: + dirExists: + - integration_test + scope: '*tests*' + + test:e2e:cloud_firestore: + run: | + cd packages/cloud_firestore/cloud_firestore/example + flutter test integration_test/e2e_test.dart + description: | + Run all e2e tests for cloud_firestore. + + test:e2e:firebase_performance: + run: | + cd packages/firebase_performance/firebase_performance/example + flutter test integration_test/firebase_performance_e2e_test.dart + description: | + Run all e2e tests for firebase_performance. + + test:e2e:web: + run: | + melos exec -c 1 --fail-fast -- \ + "flutter drive --target=./integration_test/e2e_test.dart --driver=./test_driver/integration_test.dart -d chrome --dart-define=LOCAL_WEB_E2E=true" + description: | + Run all e2e tests on web platform. Please ensure you have "chromedriver" installed and running. + packageFilters: + dirExists: + - test_driver + scope: '*tests*' + + test:e2e:web:cloud_firestore: + run: | + cd packages/cloud_firestore/cloud_firestore/example + flutter drive --target=./integration_test/e2e_test.dart --driver=./test_driver/integration_test.dart -d chrome + description: | + Run all e2e tests for cloud_firestore on web platform. Please ensure you have "chromedriver" installed and running. + + test:e2e:web:firebase_performance: + run: | + cd packages/firebase_performance/firebase_performance/example + flutter drive --target=./integration_test/firebase_performance_e2e_test.dart --driver=./test_driver/integration_test.dart --release -d chrome + description: | + Run all e2e tests for firebase_performance on web platform. Please ensure you have "chromedriver" installed and running. + + clean:deep: + run: git clean -x -d -f -q + description: Clean things very deeply, can be used to establish "pristine checkout" status. + + test:e2e:windows: + run: | + melos exec -c 1 --fail-fast -- \ + "flutter test integration_test/e2e_test.dart -d windows" + description: | + Run all e2e tests. + packageFilters: + dirExists: + - integration_test + scope: '*tests*' + + qualitycheck: + run: | + melos run clean:deep && \ + melos clean && \ + melos bootstrap && \ + melos run lint:all && \ + melos run build:all && \ + melos run test:all + description: Run all targets generally expected in CI for a full local quality check. + + generate:pigeon: + run: | + melos exec -- "flutter pub run pigeon --input ./pigeons/messages.dart" && \ + melos run generate:pigeon:macos --no-select && \ + melos run generate:pigeon:android --no-select && \ + melos run format-ci --no-select + packageFilters: + fileExists: 'pigeons/messages.dart' + description: Generate the pigeon messages for all the supported packages. + + generate:pigeon:macos: + run: | + melos exec -- "sed -i '' 's;#import ;#if TARGET_OS_OSX\n#import \n#else\n#import \n#endif;g' ios/Classes/messages.g.m" + packageFilters: + fileExists: 'ios/Classes/messages.g.m' + description: Pigeon does not add the condition to import Flutter or FlutterMacOs. Add the condition + + generate:pigeon:android: + run: | + melos exec -- "find ./android -type f -name '*Generated*' | xargs sed -i '.bak' 's/ArrayList toList() {/public ArrayList toList() {/g'" && \ + melos exec -- "find ./android -type f -name '*.bak' -delete" + packageFilters: + dirExists: 'android' + description: Transform the method toList() into a public one to be used in EventChannel + + # Additional cleanup lifecycle script, executed when `melos clean` is run. + postclean: > + melos exec -c 6 -- "flutter clean" + + add-license-header: + # If you add here another --ignore flag, add it also to + # "check-license-header". + run: | + addlicense -f header_template.txt \ + --ignore "**/.dart_tool/**" \ + --ignore "**/*.cmake" \ + --ignore "**/*.g.dart" \ + --ignore "**/*.g.h" \ + --ignore "**/*.g.m" \ + --ignore "**/*.gradle" \ + --ignore "**/*.html" \ + --ignore "**/*.js" \ + --ignore "**/*.rb" \ + --ignore "**/*.sh" \ + --ignore "**/*.ts" \ + --ignore "**/*.txt" \ + --ignore "**/*.xml" \ + --ignore "**/*.yaml" \ + --ignore "**/*.yml" \ + --ignore "**/android/app/build.gradle.kts" \ + --ignore "**/android/build.gradle.kts" \ + --ignore "**/android/settings.gradle.kts" \ + --ignore "**/build/**" \ + --ignore "**/ephemeral/**" \ + --ignore "**/flutter/generated_plugin_registrant.cc" \ + --ignore "**/flutter/generated_plugin_registrant.h" \ + --ignore "**/FlutterMultiDexApplication.java" \ + --ignore "**/generated/**" \ + --ignore "**/GeneratedPluginRegistrant.h" \ + --ignore "**/GeneratedPluginRegistrant.java" \ + --ignore "**/GeneratedPluginRegistrant.m" \ + --ignore "**/GeneratedPluginRegistrant.swift" \ + --ignore "**/MainActivity.java" \ + --ignore "**/MainActivity.kt" \ + --ignore "**/Pods/**" \ + --ignore "**/Runner/AppDelegate.h" \ + --ignore "**/Runner/AppDelegate.m" \ + --ignore "**/Runner/AppDelegate.swift" \ + --ignore "**/Runner/main.m" \ + --ignore "**/Runner/MainFlutterWindow.swift" \ + --ignore "**/Runner/Runner-Bridging-Header.h" \ + --ignore "**/RunnerTests/RunnerTests.swift" \ + . + description: Add a license header to all necessary files. + + check-license-header: + # If you add here another --ignore flag, add it also to + # "add-license-header". + run: | + addlicense -f header_template.txt \ + --check \ + --ignore "**/.dart_tool/**" \ + --ignore "**/*.cmake" \ + --ignore "**/*.g.dart" \ + --ignore "**/*.g.h" \ + --ignore "**/*.g.m" \ + --ignore "**/*.gradle" \ + --ignore "**/*.html" \ + --ignore "**/*.js" \ + --ignore "**/*.rb" \ + --ignore "**/*.sh" \ + --ignore "**/*.ts" \ + --ignore "**/*.txt" \ + --ignore "**/*.xml" \ + --ignore "**/*.yaml" \ + --ignore "**/*.yml" \ + --ignore "**/android/app/build.gradle.kts" \ + --ignore "**/android/build.gradle.kts" \ + --ignore "**/android/settings.gradle.kts" \ + --ignore "**/build/**" \ + --ignore "**/ephemeral/**" \ + --ignore "**/flutter/generated_plugin_registrant.cc" \ + --ignore "**/flutter/generated_plugin_registrant.h" \ + --ignore "**/FlutterMultiDexApplication.java" \ + --ignore "**/generated/**" \ + --ignore "**/GeneratedPluginRegistrant.h" \ + --ignore "**/GeneratedPluginRegistrant.java" \ + --ignore "**/GeneratedPluginRegistrant.m" \ + --ignore "**/GeneratedPluginRegistrant.swift" \ + --ignore "**/MainActivity.java" \ + --ignore "**/MainActivity.kt" \ + --ignore "**/Pods/**" \ + --ignore "**/Runner/AppDelegate.h" \ + --ignore "**/Runner/AppDelegate.m" \ + --ignore "**/Runner/AppDelegate.swift" \ + --ignore "**/Runner/main.m" \ + --ignore "**/Runner/MainFlutterWindow.swift" \ + --ignore "**/Runner/Runner-Bridging-Header.h" \ + --ignore "**/RunnerTests/RunnerTests.swift" \ + . + description: Add a license header to all necessary files. + + bom: + run: dart scripts/generate_bom.dart + description: Generate a Bill of Materials (BOM) file for all packages. diff --git a/scripts/generate_bom.dart b/scripts/generate_bom.dart index a3f269127686..716b6677a3dd 100644 --- a/scripts/generate_bom.dart +++ b/scripts/generate_bom.dart @@ -203,7 +203,7 @@ Future appendStaticText( // Adding rows for each package for (final package in packages.entries) { sink.writeln( - '| [${package.key}](https://pub.dev/packages/${package.key}/versions/${package.value.version}) | ${package.value.version} | ${package.value.pubSpec.environment?.sdkConstraint.toString() ?? ''} | ${package.value.pubSpec.environment?.toJson()['flutter'] ?? ''} |', + '| [${package.key}](https://pub.dev/packages/${package.key}/versions/${package.value.version}) | ${package.value.version} | ${package.value.pubspec.environment['sdk']?.toString() ?? ''} | ${package.value.pubspec.environment['flutter']?.toString() ?? ''} |', ); } diff --git a/scripts/generate_versions_spm.dart b/scripts/generate_versions_spm.dart index 8bc30470bd46..886da32aeff3 100644 --- a/scripts/generate_versions_spm.dart +++ b/scripts/generate_versions_spm.dart @@ -25,14 +25,7 @@ void main(List args) async { final firebaseiOSVersion = getFirebaseiOSVersion(firebaseCoreIosVersionFile); // Update hard-coded versions in all plugin Package.swift files - final firebaseCoreVersion = - loadYaml(File('${firebaseCorePackage.path}/pubspec.yaml').readAsStringSync())['version'] - .toString(); - updatePluginPackageSwiftVersions( - workspace, - firebaseiOSVersion, - firebaseCoreVersion, - ); + updatePluginPackageSwiftVersions(workspace, firebaseiOSVersion); // Update plugin version in Constants.swift for pure Swift plugins. Unable to pass macros in pure Swift implementations updateLibraryVersionPureSwiftPlugins(); } @@ -51,7 +44,6 @@ Future getMelosWorkspace() async { logger: melos.MelosLogger(logging.Logger.standard()), packageFilters: packageFilters, ); - return workspace; } @@ -75,7 +67,6 @@ String getFirebaseiOSVersion(File firebaseCoreIosSdkVersion) { void updatePluginPackageSwiftVersions( melos.MelosWorkspace workspace, String firebaseiOSVersion, - String firebaseCoreVersion, ) { for (final package in workspace.filteredPackages.values) { for (final platform in ['ios', 'macos']) { @@ -86,19 +77,13 @@ void updatePluginPackageSwiftVersions( var content = packageSwiftFile.readAsStringSync(); - // Update firebase_sdk_version - content = content.replaceAll( - RegExp('let firebase_sdk_version: Version = "[^"]+"'), - 'let firebase_sdk_version: Version = "$firebaseiOSVersion"', - ); - - // Update shared_spm_version + // Update firebaseSdkVersion (matches Package.swift naming). content = content.replaceAll( - RegExp('let shared_spm_version: Version = "[^"]+"'), - 'let shared_spm_version: Version = "$firebaseCoreVersion-firebase-core-swift"', + RegExp(r'let firebaseSdkVersion: Version = "[^"]+"'), + 'let firebaseSdkVersion: Version = "$firebaseiOSVersion"', ); - // Update library_version or library_version_string from pubspec version + // Update libraryVersion / libraryVersionString from pubspec version. final pubspecFile = File('${package.path}/pubspec.yaml'); if (pubspecFile.existsSync()) { final pubspecYaml = loadYaml(pubspecFile.readAsStringSync()); @@ -106,12 +91,12 @@ void updatePluginPackageSwiftVersions( if (version != null) { final spmVersion = version.replaceAll('+', '-'); content = content.replaceAll( - RegExp('let library_version_string = "[^"]+"'), - 'let library_version_string = "$spmVersion"', + RegExp(r'let libraryVersionString = "[^"]+"'), + 'let libraryVersionString = "$spmVersion"', ); content = content.replaceAll( - RegExp('let library_version = "[^"]+"'), - 'let library_version = "$spmVersion"', + RegExp(r'let libraryVersion = "[^"]+"'), + 'let libraryVersion = "$spmVersion"', ); } } @@ -128,6 +113,8 @@ void updateLibraryVersionPureSwiftPlugins() { 'firebase_ml_model_downloader', 'firebase_app_installations', 'cloud_functions', + 'firebase_remote_config', + 'firebase_app_check', ]; for (final package in packages) { diff --git a/scripts/validate_workspace.dart b/scripts/validate_workspace.dart new file mode 100644 index 000000000000..db5ebe2531d4 --- /dev/null +++ b/scripts/validate_workspace.dart @@ -0,0 +1,125 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ignore_for_file: avoid_print + +/// Validates that all packages in the repository are listed in the root +/// pubspec.yaml workspace. +library; + +import 'dart:io'; +import 'package:yaml/yaml.dart'; +import 'package:path/path.dart' as p; + +void main() { + // Ensure running at the root of the git repo + final gitResult = Process.runSync('git', ['rev-parse', '--show-cdup']); + if (gitResult.exitCode != 0 || + gitResult.stdout.toString().trim().isNotEmpty) { + print( + 'Error: This script must be run from the root of the git repository.', + ); + exitCode = 1; + return; + } + + final rootDir = Directory.current.path; + final pubspecFile = File(p.join(rootDir, 'pubspec.yaml')); + + if (!pubspecFile.existsSync()) { + print('Error: pubspec.yaml not found at root.'); + exitCode = 1; + return; + } + + final pubspecContent = pubspecFile.readAsStringSync(); + final yaml = loadYaml(pubspecContent); + + final workspace = yaml['workspace']; + if (workspace == null || workspace is! YamlList) { + print('Error: No workspace list found in pubspec.yaml.'); + exitCode = 1; + return; + } + + final workspacePaths = workspace.map((e) => e.toString()).toSet(); + print('Workspace paths in pubspec.yaml: ${workspacePaths.length}'); + + final foundPackages = []; + + final packagesDir = Directory(p.join(rootDir, 'packages')); + if (packagesDir.existsSync()) { + foundPackages.addAll(_findPackages(packagesDir, rootDir)); + } + + final testsDir = Directory(p.join(rootDir, 'tests')); + if (testsDir.existsSync()) { + foundPackages.addAll(_findPackages(testsDir, rootDir)); + } + + print('Found pubspec.yaml files: ${foundPackages.length}'); + + final missingFromWorkspace = + foundPackages.where((p) => !workspacePaths.contains(p)).toList(); + + final listedButMissingFromDisk = + workspacePaths.where((p) => !foundPackages.contains(p)).toList(); + + var hasError = false; + + if (missingFromWorkspace.isNotEmpty) { + print('\nMissing from workspace:'); + for (final p in missingFromWorkspace) { + print(' - $p'); + } + hasError = true; + } + + if (listedButMissingFromDisk.isNotEmpty) { + print('\nListed in workspace but missing from disk:'); + for (final p in listedButMissingFromDisk) { + print(' - $p'); + } + hasError = true; + } + + if (hasError) { + exitCode = 1; + } else { + print('\nWorkspace is valid! All packages are listed.'); + } +} + +Iterable _findPackages(Directory dir, String rootDir) sync* { + for (final entity in dir.listSync(recursive: true)) { + if (entity is File && p.basename(entity.path) == 'pubspec.yaml') { + final path = entity.path; + final components = p.split(path); + if (components.any(_ignoredDirectories.contains)) { + continue; + } + final relPath = p.relative(p.dirname(path), from: rootDir); + if (relPath != '.') { + yield relPath; + } + } + } +} + +const _ignoredDirectories = { + '.dart_tool', + '.plugin_symlinks', + 'ephemeral', + 'build', +}; diff --git a/scripts/versions.json b/scripts/versions.json index 62f891477e54..d88c2d975598 100644 --- a/scripts/versions.json +++ b/scripts/versions.json @@ -1,4 +1,172 @@ { + "4.16.1": { + "date": "2026-06-22", + "firebase_sdk": { + "android": "34.15.0", + "ios": "12.15.0", + "web": "12.15.0", + "windows": "13.5.0" + }, + "packages": { + "cloud_firestore": "6.6.0", + "cloud_functions": "6.3.3", + "firebase_ai": "3.13.1", + "firebase_analytics": "12.4.3", + "firebase_app_check": "0.4.5", + "firebase_app_installations": "0.4.2+4", + "firebase_auth": "6.5.4", + "firebase_core": "4.11.0", + "firebase_crashlytics": "5.2.4", + "firebase_data_connect": "0.3.0+5", + "firebase_database": "12.4.4", + "firebase_in_app_messaging": "0.9.2+4", + "firebase_messaging": "16.4.1", + "firebase_ml_model_downloader": "0.4.2+4", + "firebase_performance": "0.11.4+3", + "firebase_remote_config": "6.5.3", + "firebase_storage": "13.4.3" + } + }, + "4.16.0": { + "date": "2026-06-17", + "firebase_sdk": { + "android": "34.15.0", + "ios": "12.15.0", + "web": "12.15.0", + "windows": "13.5.0" + }, + "packages": { + "cloud_firestore": "6.6.0", + "cloud_functions": "6.3.3", + "firebase_ai": "3.13.0", + "firebase_analytics": "12.4.3", + "firebase_app_check": "0.4.5", + "firebase_app_installations": "0.4.2+4", + "firebase_auth": "6.5.3", + "firebase_core": "4.11.0", + "firebase_crashlytics": "5.2.4", + "firebase_data_connect": "0.3.0+4", + "firebase_database": "12.4.3", + "firebase_in_app_messaging": "0.9.2+4", + "firebase_messaging": "16.4.0", + "firebase_ml_model_downloader": "0.4.2+4", + "firebase_performance": "0.11.4+3", + "firebase_remote_config": "6.5.3", + "firebase_storage": "13.4.3" + } + }, + "4.15.0": { + "date": "2026-06-01", + "firebase_sdk": { + "android": "34.14.0", + "ios": "12.14.0", + "web": "12.14.0", + "windows": "13.5.0" + }, + "packages": { + "cloud_firestore": "6.5.0", + "cloud_functions": "6.3.2", + "firebase_ai": "3.12.2", + "firebase_analytics": "12.4.2", + "firebase_app_check": "0.4.4+2", + "firebase_app_installations": "0.4.2+3", + "firebase_auth": "6.5.2", + "firebase_core": "4.10.0", + "firebase_crashlytics": "5.2.3", + "firebase_data_connect": "0.3.0+3", + "firebase_database": "12.4.2", + "firebase_in_app_messaging": "0.9.2+3", + "firebase_messaging": "16.3.0", + "firebase_ml_model_downloader": "0.4.2+3", + "firebase_performance": "0.11.4+2", + "firebase_remote_config": "6.5.2", + "firebase_storage": "13.4.2" + } + }, + "4.14.0": { + "date": "2026-05-14", + "firebase_sdk": { + "android": "34.13.0", + "ios": "12.13.0", + "web": "12.13.0", + "windows": "13.5.0" + }, + "packages": { + "cloud_firestore": "6.4.1", + "cloud_functions": "6.3.1", + "firebase_ai": "3.12.1", + "firebase_analytics": "12.4.1", + "firebase_app_check": "0.4.4+1", + "firebase_app_installations": "0.4.2+2", + "firebase_auth": "6.5.1", + "firebase_core": "4.9.0", + "firebase_crashlytics": "5.2.2", + "firebase_data_connect": "0.3.0+2", + "firebase_database": "12.4.1", + "firebase_in_app_messaging": "0.9.2+2", + "firebase_messaging": "16.2.2", + "firebase_ml_model_downloader": "0.4.2+2", + "firebase_performance": "0.11.4+1", + "firebase_remote_config": "6.5.1", + "firebase_storage": "13.4.1" + } + }, + "4.13.0": { + "date": "2026-05-11", + "firebase_sdk": { + "android": "34.12.0", + "ios": "12.12.0", + "web": "12.12.0", + "windows": "13.5.0" + }, + "packages": { + "cloud_firestore": "6.4.0", + "cloud_functions": "6.3.0", + "firebase_ai": "3.12.0", + "firebase_analytics": "12.4.0", + "firebase_app_check": "0.4.4", + "firebase_app_installations": "0.4.2+1", + "firebase_auth": "6.5.0", + "firebase_core": "4.8.0", + "firebase_crashlytics": "5.2.1", + "firebase_data_connect": "0.3.0+1", + "firebase_database": "12.4.0", + "firebase_in_app_messaging": "0.9.2+1", + "firebase_messaging": "16.2.1", + "firebase_ml_model_downloader": "0.4.2+1", + "firebase_performance": "0.11.4", + "firebase_remote_config": "6.5.0", + "firebase_storage": "13.4.0" + } + }, + "4.12.0": { + "date": "2026-04-13", + "firebase_sdk": { + "android": "34.12.0", + "ios": "12.12.0", + "web": "12.12.0", + "windows": "13.5.0" + }, + "packages": { + "cloud_firestore": "6.3.0", + "cloud_functions": "6.2.0", + "firebase_ai": "3.11.0", + "firebase_analytics": "12.3.0", + "firebase_app_check": "0.4.3", + "firebase_app_installations": "0.4.2", + "firebase_auth": "6.4.0", + "firebase_core": "4.7.0", + "firebase_crashlytics": "5.2.0", + "firebase_data_connect": "0.3.0", + "firebase_database": "12.3.0", + "firebase_in_app_messaging": "0.9.2", + "firebase_messaging": "16.2.0", + "firebase_ml_model_downloader": "0.4.2", + "firebase_performance": "0.11.3", + "firebase_remote_config": "6.4.0", + "firebase_storage": "13.3.0" + } + }, "4.11.0": { "date": "2026-03-23", "firebase_sdk": { diff --git a/tests/android/app/build.gradle b/tests/android/app/build.gradle index b26d90633bf5..fc3d9adc9b24 100644 --- a/tests/android/app/build.gradle +++ b/tests/android/app/build.gradle @@ -1,7 +1,6 @@ plugins { id "com.android.application" - id "kotlin-android" - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + // The Flutter Gradle Plugin must be applied after the Android Gradle plugin. id "dev.flutter.flutter-gradle-plugin" } @@ -34,10 +33,6 @@ android { coreLibraryDesugaringEnabled true } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17 - } - defaultConfig { applicationId = "io.flutter.plugins.firebase.tests" // auth requires minSdk 23 @@ -63,3 +58,9 @@ flutter { dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' } + +kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 + } +} diff --git a/tests/android/gradle.properties b/tests/android/gradle.properties index 3b5b324f6e3f..1551eb080642 100644 --- a/tests/android/gradle.properties +++ b/tests/android/gradle.properties @@ -1,3 +1,7 @@ org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart b/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart index 05b448a961bc..66eed657b3b3 100644 --- a/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart +++ b/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart @@ -397,6 +397,24 @@ void main() { ); }); + test( + 'Result.data is Map for object-shaped JSON', + () async { + final stream = callable.stream({ + 'type': 'deepMap', + 'inputData': data.deepMap, + }); + final terminalEvent = await stream.where((e) => e is Result).last; + expect(terminalEvent, isA()); + final result = (terminalEvent as Result).result; + expect( + result.data, + isA>(), + ); + }, + skip: !kIsWeb, + ); + test('accepts a [List]', () async { final stream = callable.stream(data.list).where((event) => event is Chunk); diff --git a/tests/integration_test/e2e_test.dart b/tests/integration_test/e2e_test.dart index 75818dc8f3a0..e4ef09e84009 100644 --- a/tests/integration_test/e2e_test.dart +++ b/tests/integration_test/e2e_test.dart @@ -51,10 +51,12 @@ void main() { return; } if (kIsWeb) { - firebase_core.main(); + // Web has its own ordering because App Check runs in a separate job and + // Auth can leave emulator state that interferes with Database tests. + firebase_core.main(includeRecaptchaTests: false); firebase_ai.main(); - firebase_auth.main(); firebase_database.main(); + firebase_auth.main(); firebase_crashlytics.main(); firebase_analytics.main(); cloud_functions.main(); @@ -64,6 +66,7 @@ void main() { firebase_performance.main(); firebase_remote_config.main(); firebase_storage.main(); + firebase_core.recaptchaMain(); return; } @@ -78,6 +81,7 @@ void main() { firebase_auth.main(); firebase_remote_config.main(); firebase_storage.main(); + firebase_app_check.main(); break; default: throw UnsupportedError( @@ -88,7 +92,10 @@ void main() { } void runAllTests() { - firebase_core.main(); + // Native platforms run the full suite in package order, but keep the + // recaptcha core tests before App Check because Android App Check activation + // changes native recaptcha configuration for later secondary app checks. + firebase_core.main(includeRecaptchaTests: false); firebase_ai.main(); firebase_auth.main(); firebase_database.main(); @@ -101,5 +108,6 @@ void runAllTests() { firebase_performance.main(); firebase_remote_config.main(); firebase_storage.main(); + firebase_core.recaptchaMain(); firebase_app_check.main(); } diff --git a/tests/integration_test/firebase_ai/firebase_ai_e2e_test.dart b/tests/integration_test/firebase_ai/firebase_ai_e2e_test.dart index 75c7d67f7720..8509114ae557 100644 --- a/tests/integration_test/firebase_ai/firebase_ai_e2e_test.dart +++ b/tests/integration_test/firebase_ai/firebase_ai_e2e_test.dart @@ -1,94 +1,30 @@ -// Copyright 2026, the Chromium project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -const _channel = MethodChannel('plugins.flutter.io/firebase_ai'); +import 'firebase_ai_headers_e2e_test.dart' as headers_tests; +import 'firebase_ai_response_parsing_e2e_test.dart' as parsing_tests; +import 'firebase_ai_mock_test.dart' as mock_tests; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('firebase_ai', () { - group('platform security headers', () { - testWidgets( - 'returns non-empty headers on mobile platforms', - skip: kIsWeb, - (WidgetTester tester) async { - final headers = await _channel.invokeMapMethod( - 'getPlatformHeaders', - ); - - expect( - headers, - isNotNull, - reason: 'Native plugin should return platform headers', - ); - expect( - headers, - isNotEmpty, - reason: 'Native plugin should return non-empty platform headers', - ); - }, - ); - - testWidgets( - 'returns correct Android headers', - skip: kIsWeb || defaultTargetPlatform != TargetPlatform.android, - (WidgetTester tester) async { - final headers = await _channel.invokeMapMethod( - 'getPlatformHeaders', - ); - - expect(headers, isNotNull); - expect(headers, contains('X-Android-Package')); - expect( - headers!['X-Android-Package'], - isNotEmpty, - reason: 'Package name should not be empty', - ); - // Cert may be empty in some emulator environments, but key must exist. - expect(headers, contains('X-Android-Cert')); - }, - ); - - testWidgets( - 'returns correct iOS/macOS headers', - skip: kIsWeb || - (defaultTargetPlatform != TargetPlatform.iOS && - defaultTargetPlatform != TargetPlatform.macOS), - (WidgetTester tester) async { - final headers = await _channel.invokeMapMethod( - 'getPlatformHeaders', - ); - - expect(headers, isNotNull); - expect(headers, contains('x-ios-bundle-identifier')); - expect( - headers!['x-ios-bundle-identifier'], - isNotEmpty, - reason: 'Bundle identifier should not be empty', - ); - }, - ); - - testWidgets( - 'returns empty headers on web', - skip: !kIsWeb, - (WidgetTester tester) async { - // On web, no native plugin is registered, so the channel call - // should throw a MissingPluginException. - expect( - () => _channel.invokeMapMethod( - 'getPlatformHeaders', - ), - throwsA(isA()), - ); - }, - ); - }); + headers_tests.main(); + parsing_tests.main(); + mock_tests.main(); }); } diff --git a/tests/integration_test/firebase_ai/firebase_ai_headers_e2e_test.dart b/tests/integration_test/firebase_ai/firebase_ai_headers_e2e_test.dart new file mode 100644 index 000000000000..0650556a1997 --- /dev/null +++ b/tests/integration_test/firebase_ai/firebase_ai_headers_e2e_test.dart @@ -0,0 +1,117 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + if (!kIsWeb) { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + const MethodChannel('plugins.flutter.io/firebase_ai'), + (MethodCall call) async { + if (call.method == 'getPlatformHeaders') { + return { + 'X-Android-Package': 'com.example.test', + 'X-Android-Cert': '12345', + 'x-ios-bundle-identifier': 'com.example.test', + }; + } + return null; + }); + } + + group('platform security headers', () { + const _channel = MethodChannel('plugins.flutter.io/firebase_ai'); + testWidgets( + 'returns non-empty headers on mobile platforms', + skip: kIsWeb, + (WidgetTester tester) async { + final headers = await _channel.invokeMapMethod( + 'getPlatformHeaders', + ); + + expect( + headers, + isNotNull, + reason: 'Native plugin should return platform headers', + ); + expect( + headers, + isNotEmpty, + reason: 'Native plugin should return non-empty platform headers', + ); + }, + ); + + testWidgets( + 'returns correct Android headers', + skip: kIsWeb || defaultTargetPlatform != TargetPlatform.android, + (WidgetTester tester) async { + final headers = await _channel.invokeMapMethod( + 'getPlatformHeaders', + ); + + expect(headers, isNotNull); + expect(headers, contains('X-Android-Package')); + expect( + headers!['X-Android-Package'], + isNotEmpty, + reason: 'Package name should not be empty', + ); + // Cert may be empty in some emulator environments, but key must exist. + expect(headers, contains('X-Android-Cert')); + }, + ); + + testWidgets( + 'returns correct iOS/macOS headers', + skip: kIsWeb || + (defaultTargetPlatform != TargetPlatform.iOS && + defaultTargetPlatform != TargetPlatform.macOS), + (WidgetTester tester) async { + final headers = await _channel.invokeMapMethod( + 'getPlatformHeaders', + ); + + expect(headers, isNotNull); + expect(headers, contains('x-ios-bundle-identifier')); + expect( + headers!['x-ios-bundle-identifier'], + isNotEmpty, + reason: 'Bundle identifier should not be empty', + ); + }, + ); + + testWidgets( + 'returns empty headers on web', + skip: !kIsWeb, + (WidgetTester tester) async { + // On web, no native plugin is registered, so the channel call + // should throw a MissingPluginException. + expect( + () => _channel.invokeMapMethod( + 'getPlatformHeaders', + ), + throwsA(isA()), + ); + }, + ); + }); +} diff --git a/tests/integration_test/firebase_ai/firebase_ai_mock_test.dart b/tests/integration_test/firebase_ai/firebase_ai_mock_test.dart new file mode 100644 index 000000000000..26ebddba04a8 --- /dev/null +++ b/tests/integration_test/firebase_ai/firebase_ai_mock_test.dart @@ -0,0 +1,208 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:async'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_core_platform_interface/test.dart'; +import 'package:firebase_ai/src/api.dart'; +import 'package:firebase_ai/src/client.dart'; +import 'package:firebase_ai/src/base_model.dart'; +import 'package:firebase_ai/src/content.dart'; +import 'package:firebase_ai/src/chat.dart'; +import 'package:tests/firebase_options.dart'; + +class MockApiClient implements ApiClient { + final List> requests = []; + Map mockResponse = {}; + + @override + Future> makeRequest( + Uri uri, Map body,) async { + requests.add({'uri': uri, 'body': body}); + return mockResponse; + } + + @override + Stream> streamRequest( + Uri uri, Map body,) async* { + requests.add({'uri': uri, 'body': body}); + yield mockResponse; + } +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('firebase_ai mock tests', () { + setUpAll(() async { + setupFirebaseCoreMocks(); + // Use a named app to avoid conflict with the default app initialized by mocks + await Firebase.initializeApp( + name: 'mockTestApp', + options: DefaultFirebaseOptions.currentPlatform, + ); + }); + + test('Verify Request Payload for Grounding', () async { + final mockClient = MockApiClient(); + mockClient.mockResponse = { + 'candidates': [ + { + 'content': { + 'parts': [ + {'text': 'Hello!'}, + ], + }, + } + ], + }; + + // Using the package-private test method via src import + final model = createModelWithClient( + app: Firebase.app('mockTestApp'), + location: 'us-central1', + model: 'gemini-pro', + client: mockClient, + useVertexBackend: true, + ); + + // We need to construct a request that uses Grounding. + // Assuming there is a way to set tools or similar. + // Let's just call a simple generateContent first to verify the mock works. + final response = await model.generateContent([Content.text('Hi')]); + + expect(response.text, equals('Hello!')); + expect(mockClient.requests, hasLength(1)); + + final requestBody = + mockClient.requests.first['body']! as Map; + expect(requestBody, contains('contents')); + }); + + test('Verify Request Payload for JSON Schema', () async { + final mockClient = MockApiClient(); + mockClient.mockResponse = { + 'candidates': [ + { + 'content': { + 'parts': [ + {'text': '{"name": "Apple", "price": 1.2}'}, + ], + }, + } + ], + }; + + final model = createModelWithClient( + app: Firebase.app('mockTestApp'), + location: 'us-central1', + model: 'gemini-pro', + client: mockClient, + useVertexBackend: true, + ); + + final schema = { + 'type': 'OBJECT', + 'properties': { + 'name': {'type': 'STRING'}, + 'price': {'type': 'NUMBER'}, + }, + 'required': ['name', 'price'], + }; + + await model.generateContent( + [Content.text('Give me a fruit')], + generationConfig: GenerationConfig( + responseMimeType: 'application/json', + responseJsonSchema: schema, + ), + ); + + expect(mockClient.requests, hasLength(1)); + final requestBody = mockClient.requests.first['body']! as Map; + expect(requestBody, contains('generationConfig')); + + final genConfig = requestBody['generationConfig']! as Map; + expect(genConfig['responseMimeType'], equals('application/json')); + expect(genConfig['responseJsonSchema'], equals(schema)); + }); + + test('Verify Request Payload for Multi-turn Chat', () async { + final mockClient = MockApiClient(); + // Mock response for first turn + mockClient.mockResponse = { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': 'Hello!'}, + ], + }, + } + ], + }; + + final model = createModelWithClient( + app: Firebase.app('mockTestApp'), + location: 'us-central1', + model: 'gemini-pro', + client: mockClient, + useVertexBackend: true, + ); + + final chat = model.startChat(); + + // First turn + await chat.sendMessage(Content.text('Hi')); + + // Mock response for second turn + mockClient.mockResponse = { + 'candidates': [ + { + 'content': { + 'role': 'model', + 'parts': [ + {'text': 'I am good.'}, + ], + }, + } + ], + }; + + // Second turn + await chat.sendMessage(Content.text('How are you?')); + + // Verify that the second request contains the history + expect(mockClient.requests, hasLength(2)); + + final secondRequest = mockClient.requests[1]['body']! as Map; + expect(secondRequest, contains('contents')); + + final contents = secondRequest['contents']! as List; + expect(contents, hasLength(3)); // User 'Hi', Model 'Hello!', User 'How are you?' + + // Verify roles and text + expect(contents[0]['role'], equals('user')); + expect(contents[1]['role'], equals('model')); + expect(contents[2]['role'], equals('user')); + + expect(contents[0]['parts'][0]['text'], equals('Hi')); + expect(contents[1]['parts'][0]['text'], equals('Hello!')); + expect(contents[2]['parts'][0]['text'], equals('How are you?')); + }); + }); +} diff --git a/tests/integration_test/firebase_ai/firebase_ai_response_parsing_e2e_test.dart b/tests/integration_test/firebase_ai/firebase_ai_response_parsing_e2e_test.dart new file mode 100644 index 000000000000..fe0b91644945 --- /dev/null +++ b/tests/integration_test/firebase_ai/firebase_ai_response_parsing_e2e_test.dart @@ -0,0 +1,110 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:http/http.dart' as http; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_core_platform_interface/test.dart'; +import 'package:firebase_ai/src/api.dart'; +import 'package:firebase_ai/src/developer/api.dart'; +import 'package:tests/firebase_options.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('firebase_ai mock json parsing', () { + setUpAll(() async { + setupFirebaseCoreMocks(); + // Use a named app to avoid conflict with the default app initialized by mocks + await Firebase.initializeApp( + name: 'parsingTestApp', + options: DefaultFirebaseOptions.currentPlatform, + ); + }); + + test('test against all json responses from vertexai-sdk-test-data', + () async { + final treeUrl = Uri.parse( + 'https://api.github.com/repos/FirebaseExtended/vertexai-sdk-test-data/git/trees/main?recursive=1', + ); + final treeResponse = await http.get(treeUrl); + if (treeResponse.statusCode != 200) { + if (treeResponse.statusCode == 403 || treeResponse.statusCode == 429) { + // ignore: avoid_print + print( + 'Skipping test: Failed to fetch tree due to rate limit (status ${treeResponse.statusCode})', + ); + return; + } + fail('Failed to fetch tree: ${treeResponse.statusCode}'); + } + final treeData = jsonDecode(treeResponse.body); + final tree = treeData['tree'] as List; + + final jsonFiles = tree.where((item) { + final path = item['path'] as String; + return path.startsWith('mock-responses/') && path.endsWith('.json'); + }).toList(); + + for (final file in jsonFiles) { + final path = file['path'] as String; + final rawUrl = Uri.parse( + 'https://raw.githubusercontent.com/FirebaseExtended/vertexai-sdk-test-data/main/$path', + ); + final response = await http.get(rawUrl); + if (response.statusCode != 200) { + continue; + } + + final jsonData = jsonDecode(response.body); + + final isVertex = path.contains('vertexai'); + final serializer = + isVertex ? VertexSerialization() : DeveloperSerialization(); + + try { + if (path.contains('total-tokens') || path.contains('token')) { + if (jsonData is Map && + (jsonData.containsKey('totalTokens') || + jsonData.containsKey('error'))) { + serializer.parseCountTokensResponse(jsonData); + } else { + serializer.parseGenerateContentResponse(jsonData); + } + } else { + serializer.parseGenerateContentResponse(jsonData); + } + + if (path.contains('failure') && !path.contains('success')) { + fail('Expected parsing to fail for $path, but it succeeded.'); + } + } catch (e) { + if (path.contains('failure') && !path.contains('success')) { + // Expected to fail + expect( + e, + isA(), + reason: 'Expected an Exception but got $e for $path', + ); + } else { + fail('Failed to parse success file $path: $e'); + } + } + } + }); + }); +} diff --git a/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart b/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart index 2046367d76b1..777444ae5d64 100644 --- a/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart +++ b/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart @@ -2,6 +2,8 @@ // for details. 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: do_not_use_environment + import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; @@ -9,6 +11,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:tests/firebase_options.dart'; +const androidDebugToken = + String.fromEnvironment('APP_CHECK_ANDROID_DEBUG_TOKEN'); + +const appleDebugToken = String.fromEnvironment('APP_CHECK_APPLE_DEBUG_TOKEN'); + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -99,6 +106,56 @@ void main() { }, skip: defaultTargetPlatform != TargetPlatform.iOS, ); + + test( + 'uses Apple debug token when both Android and Apple debug tokens are configured', + () async { + await FirebaseAppCheck.instance.activate( + providerAndroid: const AndroidDebugProvider( + debugToken: androidDebugToken, + ), + providerApple: const AppleDebugProvider( + debugToken: appleDebugToken, + ), + ); + + await expectLater( + FirebaseAppCheck.instance.getToken(true), + completes, + ); + }, + skip: defaultTargetPlatform != TargetPlatform.iOS || + androidDebugToken.isEmpty || + appleDebugToken.isEmpty + ? 'Requires iOS plus APP_CHECK_ANDROID_DEBUG_TOKEN and ' + 'APP_CHECK_APPLE_DEBUG_TOKEN dart-defines.' + : null, + ); + + test( + 'uses Android debug token when both Android and Apple debug tokens are configured', + () async { + await FirebaseAppCheck.instance.activate( + providerAndroid: const AndroidDebugProvider( + debugToken: androidDebugToken, + ), + providerApple: const AppleDebugProvider( + debugToken: appleDebugToken, + ), + ); + + await expectLater( + FirebaseAppCheck.instance.getToken(true), + completes, + ); + }, + skip: defaultTargetPlatform != TargetPlatform.android || + androidDebugToken.isEmpty || + appleDebugToken.isEmpty + ? 'Requires Android plus APP_CHECK_ANDROID_DEBUG_TOKEN and ' + 'APP_CHECK_APPLE_DEBUG_TOKEN dart-defines.' + : null, + ); }, ); } diff --git a/tests/integration_test/firebase_app_installations/firebase_app_installations_e2e_test.dart b/tests/integration_test/firebase_app_installations/firebase_app_installations_e2e_test.dart index 50562a38aa53..af34b20b0267 100644 --- a/tests/integration_test/firebase_app_installations/firebase_app_installations_e2e_test.dart +++ b/tests/integration_test/firebase_app_installations/firebase_app_installations_e2e_test.dart @@ -20,6 +20,11 @@ void main() { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); + if (defaultTargetPlatform == TargetPlatform.android) { + // Android Installations can deadlock if token/id APIs race native + // heartbeat initialization immediately after manual app init. + await Future.delayed(const Duration(seconds: 2)); + } }); test( @@ -47,6 +52,16 @@ void main() { skip: defaultTargetPlatform == TargetPlatform.macOS && isCI, ); + test( + '.getToken', + () async { + final token = await FirebaseInstallations.instance.getToken(); + expect(token, isNotEmpty); + // macOS skipped because it needs keychain sharing entitlement. See: https://github.com/firebase/flutterfire/issues/9538 + }, + skip: defaultTargetPlatform == TargetPlatform.macOS, + ); + test( '.delete', () async { @@ -79,16 +94,6 @@ void main() { }, skip: defaultTargetPlatform == TargetPlatform.macOS, ); - - test( - '.getToken', - () async { - final token = await FirebaseInstallations.instance.getToken(); - expect(token, isNotEmpty); - // macOS skipped because it needs keychain sharing entitlement. See: https://github.com/firebase/flutterfire/issues/9538 - }, - skip: defaultTargetPlatform == TargetPlatform.macOS, - ); }, ); } diff --git a/tests/integration_test/firebase_auth/firebase_auth_instance_e2e_test.dart b/tests/integration_test/firebase_auth/firebase_auth_instance_e2e_test.dart index 0ee4a56f76f7..17c1448a273b 100644 --- a/tests/integration_test/firebase_auth/firebase_auth_instance_e2e_test.dart +++ b/tests/integration_test/firebase_auth/firebase_auth_instance_e2e_test.dart @@ -158,7 +158,9 @@ void main() { await Future.delayed(const Duration(seconds: 2)); }, skip: defaultTargetPlatform == TargetPlatform.macOS || - defaultTargetPlatform == TargetPlatform.windows, + defaultTargetPlatform == TargetPlatform.windows || + // TODO(SelaseKay): this is crashing iOS app when running on CI + defaultTargetPlatform == TargetPlatform.iOS, ); test( @@ -886,6 +888,7 @@ void main() { name: appName, options: DefaultFirebaseOptions.currentPlatform, ); + addTearDown(app2.delete); final auth2 = FirebaseAuth.instanceFor(app: app2); @@ -897,9 +900,10 @@ void main() { fail(e.toString()); } }, - // TODO(russellwheatley): this is crashing iOS/macOS app (reinit app), but does not when running as app. + // TODO(SelaseKay): this needs to be investigated as now failing on android skip: defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.macOS, + defaultTargetPlatform == TargetPlatform.macOS || + defaultTargetPlatform == TargetPlatform.android, ); }); diff --git a/tests/integration_test/firebase_core/firebase_core_e2e_test.dart b/tests/integration_test/firebase_core/firebase_core_e2e_test.dart index 8f31a8ddbd65..bb8349369128 100644 --- a/tests/integration_test/firebase_core/firebase_core_e2e_test.dart +++ b/tests/integration_test/firebase_core/firebase_core_e2e_test.dart @@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:tests/firebase_options.dart'; -void main() { +void main({bool includeRecaptchaTests = true}) { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('firebase_core', () { @@ -76,7 +76,63 @@ void main() { test('FirebaseApp.setAutomaticResourceManagementEnabled()', () async { FirebaseApp app = Firebase.app(testAppName); - await app.setAutomaticResourceManagementEnabled(true); + try { + await app.setAutomaticResourceManagementEnabled(true); + } finally { + await app.setAutomaticResourceManagementEnabled(false); + } + }); + }); + + if (includeRecaptchaTests) { + recaptchaMain(); + } +} + +void recaptchaMain() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('firebase_core recaptcha', () { + setUpAll(() async { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + }); + + test('Firebase.initializeApp with recaptchaSiteKey', () async { + String appName = 'recaptcha-test-app'; + FirebaseOptions options = (defaultTargetPlatform == TargetPlatform.android + ? DefaultFirebaseOptions.currentPlatform.copyWith( + appId: '1:1234567890:android:fedcba0987654321fedcba', + ) + : DefaultFirebaseOptions.currentPlatform) + .copyWith( + recaptchaSiteKey: 'test-recaptcha-site-key', + ); + + await Firebase.initializeApp( + name: appName, + options: options, + ); + + FirebaseApp app = Firebase.app(appName); + expect(app.options.recaptchaSiteKey, 'test-recaptcha-site-key'); + + await app.delete(); + }); + + test('Default app recaptchaSiteKey precedence test', () async { + // Natively initialized default app has no recaptchaSiteKey. + // Trying to initialize it again with different recaptchaSiteKey in Dart. + FirebaseApp app = await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform.copyWith( + recaptchaSiteKey: 'dart-recaptcha-key', + ), + ); + + // It should NOT update the key, because native initializeApp was skipped. + // (It returns the natively initialized app which has null key). + expect(app.options.recaptchaSiteKey, isNull); }); }); } diff --git a/tests/integration_test/firebase_database/database_reference_e2e.dart b/tests/integration_test/firebase_database/database_reference_e2e.dart index 78fc9616ffe9..0c59b4af6ccb 100644 --- a/tests/integration_test/firebase_database/database_reference_e2e.dart +++ b/tests/integration_test/firebase_database/database_reference_e2e.dart @@ -126,6 +126,41 @@ void setupDatabaseReferenceTests() { expect(result.snapshot.value, 5); }); + test('does not emit local transaction events when disabled', () async { + final ref = database.ref('tests/transaction-apply-locally-false'); + await ref.set({'count': 0}); + + final initialEvent = Completer(); + final events = []; + final subscription = ref.onValue.listen((event) { + if (!initialEvent.isCompleted) { + initialEvent.complete(); + return; + } + + events.add(event.snapshot.value); + }); + + try { + await initialEvent.future.timeout(const Duration(seconds: 5)); + + await ref.runTransaction( + (value) => Transaction.success({ + 'count': ((value as Map?)?['count'] as int? ?? 0) + 1, + 'timestamp': ServerValue.timestamp, + }), + applyLocally: false, + ); + + await Future.delayed(const Duration(seconds: 1)); + + expect(events, hasLength(1)); + } finally { + await database.goOnline(); + await subscription.cancel(); + } + }); + test('executes transaction', () async { final ref = database.ref('tests/transaction-exec'); await ref.set(0); diff --git a/tests/integration_test/firebase_database/firebase_database_configuration_e2e.dart b/tests/integration_test/firebase_database/firebase_database_configuration_e2e.dart index 7cb4b66344da..3e7a124039b2 100644 --- a/tests/integration_test/firebase_database/firebase_database_configuration_e2e.dart +++ b/tests/integration_test/firebase_database/firebase_database_configuration_e2e.dart @@ -2,8 +2,11 @@ // for details. 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:firebase_core/firebase_core.dart'; +import 'package:firebase_database/firebase_database.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:tests/firebase_options.dart'; import 'firebase_database_e2e_test.dart'; @@ -37,5 +40,45 @@ void setupConfigurationTests() { test('setLoggingEnabled to false', () { database.setLoggingEnabled(false); }); + + test( + 'setPersistenceEnabled can be followed immediately by goOnline', + () async { + final apps = []; + + try { + for (var i = 0; i < 5; i++) { + final app = await Firebase.initializeApp( + name: + 'firebase-database-persistence-${DateTime.now().microsecondsSinceEpoch}-$i', + options: DefaultFirebaseOptions.currentPlatform, + ); + apps.add(app); + + final database = FirebaseDatabase.instanceFor(app: app); + + database.setPersistenceEnabled(true); + await database.goOnline(); + + await database + .ref('persistence-enabled-regression') + .keepSynced(true); + await database + .ref('persistence-enabled-regression') + .keepSynced(false); + await database.goOffline(); + } + } finally { + // setPersistenceEnabled is intentionally fire-and-forget on Dart. + // Let the native call queue drain before deleting throwaway apps. + await Future.delayed(const Duration(milliseconds: 500)); + for (final app in apps.reversed) { + await app.delete(); + } + } + }, + // TODO(SelaseKay): this needs to be investigated as now failing on android (should only run on android) + skip: true, + ); }); } diff --git a/tests/integration_test/firebase_database/query_e2e.dart b/tests/integration_test/firebase_database/query_e2e.dart index ab020ff4cab6..8b2ec17a3da5 100644 --- a/tests/integration_test/firebase_database/query_e2e.dart +++ b/tests/integration_test/firebase_database/query_e2e.dart @@ -608,6 +608,43 @@ void setupQueryTests() { ); }); + test( + 'cancels overlapping query streams without missing plugin', + () async { + const subscriptionCount = 128; + final queryRef = ref.child('overlapping-query-streams'); + await queryRef.set({'value': 1}); + + final errors = []; + final subscriptions = >[]; + final firstEventsReceived = Completer(); + var firstEventCount = 0; + + for (var i = 0; i < subscriptionCount; i++) { + subscriptions.add( + queryRef.onValue.listen( + (_) { + firstEventCount++; + if (firstEventCount >= subscriptionCount && + !firstEventsReceived.isCompleted) { + firstEventsReceived.complete(); + } + }, + onError: errors.add, + ), + ); + } + + await firstEventsReceived.future.timeout(const Duration(seconds: 10)); + await Future.wait( + subscriptions.map((subscription) => subscription.cancel()), + ); + + expect(errors, isEmpty); + }, + skip: defaultTargetPlatform != TargetPlatform.android, + ); + test( 'throw a `permission-denied` exception when accessing restricted data', () async { diff --git a/tests/integration_test/firebase_storage/task_e2e.dart b/tests/integration_test/firebase_storage/task_e2e.dart index fd00288c3398..75f845e822b3 100644 --- a/tests/integration_test/firebase_storage/task_e2e.dart +++ b/tests/integration_test/firebase_storage/task_e2e.dart @@ -6,9 +6,12 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:tests/firebase_options.dart'; import './test_utils.dart'; @@ -346,6 +349,40 @@ void setupTaskTests() { // Windows `task.cancel()` is returning "false", same code on example app works as intended skip: defaultTargetPlatform == TargetPlatform.windows, ); + + test( + 'cancels multiple in-progress Android tasks during core reinitialization', + () async { + final tasks = [ + for (var i = 0; i < 3; i++) + storage + .ref('flutter-tests/regression-18240-$i.txt') + .putString('A' * 20000000), + ]; + final completions = tasks + .map( + (task) => task.then( + (_) {}, + onError: (_) {}, + ), + ) + .toList(); + + try { + MethodChannelFirebase.isCoreInitialized = false; + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ).timeout(const Duration(seconds: 30)); + } finally { + MethodChannelFirebase.isCoreInitialized = true; + completions.forEach(unawaited); + } + }, + // TODO(SelaseKay): move this white-box core reinitialization + // regression to an isolated test. Forcing global core reinit in the + // shared E2E process can race unrelated plugin app lifecycle tests. + skip: true, + ); }, ); diff --git a/tests/pubspec.yaml b/tests/pubspec.yaml index c91c640a39d6..68b6296dcb78 100644 --- a/tests/pubspec.yaml +++ b/tests/pubspec.yaml @@ -3,49 +3,50 @@ name: tests description: A an app for FlutterFire testing (e2e). publish_to: 'none' version: 1.0.0+1 +resolution: workspace environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '^3.6.0' flutter: '>=3.22.0' dependencies: - cloud_functions: ^6.1.0 - cloud_functions_platform_interface: ^5.8.11 - cloud_functions_web: ^5.1.4 + cloud_functions: ^6.3.3 + cloud_functions_platform_interface: ^6.0.3 + cloud_functions_web: ^5.1.9 collection: ^1.15.0 - firebase_ai: ^3.10.0 - firebase_analytics: ^12.2.0 - firebase_analytics_platform_interface: ^5.1.0 - firebase_analytics_web: ^0.6.1+4 - firebase_app_check: ^0.4.2 - firebase_app_check_platform_interface: ^0.2.2 - firebase_app_check_web: ^0.2.3 - firebase_app_installations: ^0.4.1 - firebase_app_installations_platform_interface: ^0.1.4+67 - firebase_app_installations_web: ^0.1.7+4 - firebase_auth: ^6.3.0 - firebase_auth_platform_interface: ^8.1.8 - firebase_auth_web: ^6.1.4 - firebase_core: ^4.6.0 - firebase_core_platform_interface: ^6.0.3 - firebase_core_web: ^3.5.1 - firebase_crashlytics: ^5.1.0 - firebase_crashlytics_platform_interface: ^3.8.19 - firebase_database: ^12.2.0 - firebase_database_platform_interface: ^0.3.1 - firebase_database_web: ^0.2.7+5 - firebase_messaging: ^16.1.3 - firebase_messaging_platform_interface: ^4.7.8 - firebase_messaging_web: ^4.1.4 - firebase_ml_model_downloader: ^0.4.1 - firebase_ml_model_downloader_platform_interface: ^0.1.5+19 - firebase_performance: ^0.11.2 - firebase_remote_config: ^6.3.0 - firebase_remote_config_platform_interface: ^2.1.1 - firebase_remote_config_web: ^1.10.5 - firebase_storage: ^13.2.0 - firebase_storage_platform_interface: ^5.2.19 - firebase_storage_web: ^3.11.4 + firebase_ai: ^3.13.1 + firebase_analytics: ^12.4.3 + firebase_analytics_platform_interface: ^6.0.3 + firebase_analytics_web: ^0.6.1+9 + firebase_app_check: ^0.4.5 + firebase_app_check_platform_interface: ^0.4.1 + firebase_app_check_web: ^0.2.5 + firebase_app_installations: ^0.4.2+4 + firebase_app_installations_platform_interface: ^0.1.4+72 + firebase_app_installations_web: ^0.1.7+9 + firebase_auth: ^6.5.4 + firebase_auth_platform_interface: ^9.0.3 + firebase_auth_web: ^6.2.3 + firebase_core: ^4.11.0 + firebase_core_platform_interface: ^7.1.0 + firebase_core_web: ^3.9.0 + firebase_crashlytics: ^5.2.4 + firebase_crashlytics_platform_interface: ^3.8.24 + firebase_database: ^12.4.4 + firebase_database_platform_interface: ^0.4.0+3 + firebase_database_web: ^0.2.7+10 + firebase_messaging: ^16.4.1 + firebase_messaging_platform_interface: ^4.9.0 + firebase_messaging_web: ^4.2.1 + firebase_ml_model_downloader: ^0.4.2+4 + firebase_ml_model_downloader_platform_interface: ^0.1.5+24 + firebase_performance: ^0.11.4+3 + firebase_remote_config: ^6.5.3 + firebase_remote_config_platform_interface: ^3.0.3 + firebase_remote_config_web: ^1.10.10 + firebase_storage: ^13.4.3 + firebase_storage_platform_interface: ^6.0.3 + firebase_storage_web: ^3.11.9 flutter: sdk: flutter http: ^1.0.0 diff --git a/tests/windows/flutter/generated_plugin_registrant.cc b/tests/windows/flutter/generated_plugin_registrant.cc index 9a180bb54481..b6d55c18d245 100644 --- a/tests/windows/flutter/generated_plugin_registrant.cc +++ b/tests/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -13,6 +14,8 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + FirebaseAppCheckPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseAppCheckPluginCApi")); FirebaseAuthPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); FirebaseCorePluginCApiRegisterWithRegistrar( diff --git a/tests/windows/flutter/generated_plugins.cmake b/tests/windows/flutter/generated_plugins.cmake index 1a9da22be867..89f9a20f171a 100644 --- a/tests/windows/flutter/generated_plugins.cmake +++ b/tests/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + firebase_app_check firebase_auth firebase_core firebase_database