diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..5c356cf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,114 @@ +name: Bug Report +description: Submit a bug report. +body: + - type: markdown + attributes: + value: | + Before filling out the form, please consider the following: + - Make sure to search the [existing issues](https://github.com/AgoraIO-Extensions/Agora-Flutter-RTM-SDK/issues) to see if your bug has already been reported. + - For urgent issues, please submit a ticket to [Agora Support](https://www.agora.io/en/customer-support/) for a prompt response. + - type: textarea + attributes: + label: Version of the agora_rtm + description: The version of the agora_rtm. + placeholder: | + 2.2.1 + validations: + required: true + - type: checkboxes + attributes: + label: Platforms affected + options: + - label: Android + - label: iOS + - label: macOS + - label: Windows + - label: Web + - type: textarea + attributes: + label: Steps to reproduce + description: Please tell us exactly how to reproduce the problem you are running into. + placeholder: | + 1. ... + 2. ... + 3. ... + validations: + required: true + - type: textarea + attributes: + label: Expected results + description: Please tell us what is expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Actual results + description: Please tell us what is actually happening. + validations: + required: true + - type: textarea + attributes: + label: Code sample + description: | + To help us better understand and address the issue, please provide a minimal reproducible sample that demonstrates the problem. + Instead of uploading screenshots of text, we kindly ask you to use code blocks or the methods mentioned above to share your code sample. + This will greatly assist us in diagnosing and resolving the issue effectively. + value: | +
Code sample + + ```dart + [Paste your code here] + ``` + +
+ validations: + required: false + - type: textarea + attributes: + label: Screenshots or Video + description: | + Upload any screenshots or video of the bug if applicable. + value: | +
+ Screenshots / Video demonstration + + [Upload media here] + +
+ - type: textarea + attributes: + label: Logs + description: | + To help us diagnose and resolve the issue effectively, please include the full logs when you encounter the problem. + + Here are the default log paths for different platforms: + - Android: /storage/emulated/0/Android/data//files/agora-iris.log + - iOS: App Sandbox/Library/caches/agora-iris.log + - macOS: /Users//Library/Containers//Data/Library/Logs/agora-iris.log + - Windows: C:\Users\\AppData\Local\Agora\\agora-iris.log + + Please avoid uploading screenshots of text. Instead, use code blocks or the methods mentioned above to share the logs. + Remember to remove any sensitive information before sharing. + value: | +
Logs + + ```console + [Paste your logs here] + ``` + +
+ - type: textarea + attributes: + label: Flutter Doctor output + description: | + Please provide the full output of running `flutter doctor -v` + value: | +
Doctor output + + ```console + [Paste your output here] + ``` + +
+ validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/ triage-agora-support.yaml b/.github/workflows/ triage-agora-support.yaml new file mode 100644 index 0000000..0c1bf2d --- /dev/null +++ b/.github/workflows/ triage-agora-support.yaml @@ -0,0 +1,18 @@ +name: Triage to Agora Support +on: + issues: + types: + - labeled +jobs: + add-comment: + if: github.event.label.name == 'triage agora support' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Add comment + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.issue.number }} + body: | + Please submit a ticket to [Agora Support](https://www.agora.io/en/customer-support/) for further investigation of this issue. If you have any conclusions, you can share them here which may help other developers. Thanks! \ No newline at end of file diff --git a/.github/workflows/auto-close-daily-doc-update.yaml b/.github/workflows/auto-close-daily-doc-update.yaml new file mode 100644 index 0000000..c906588 --- /dev/null +++ b/.github/workflows/auto-close-daily-doc-update.yaml @@ -0,0 +1,24 @@ +name: Auto-close Stale Daily Doc Update Pull Requests + +on: + schedule: + - cron: '0 16 * * *' # Runs daily at midnight Beijing time + +permissions: + contents: write # only for delete-branch option + pull-requests: write + +jobs: + close-stale-pr: + runs-on: ubuntu-latest + steps: + - name: Close Stale Pull Requests + uses: actions/stale@v8 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-pr-message: 'This auto daily doc update PR is stale because it has been open for 3 days with no activity. Close.' + days-before-stale: 1 + days-before-close: 1 + remove-stale-when-updated: false + delete-branch: true + only-labels: 'ci:doc' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..77068c4 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,325 @@ +name: CI + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + workflow_call: + secrets: + MY_APP_ID: + required: true + +jobs: + pub_publish_check: + name: Flutter codestyle/analyze check + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Check Dart Format + run: bash scripts/dart_pub_publish_check.sh + - uses: axel-op/dart-package-analyzer@v3 + id: analysis + with: + githubToken: ${{ secrets.GITHUB_TOKEN }} + - name: Check scores + env: + TOTAL: ${{ steps.analysis.outputs.total }} + TOTAL_MAX: ${{ steps.analysis.outputs.total_max }} + run: | + if (( $TOTAL_MAX - $TOTAL > 30 )) + then + echo Pub Score too low. + exit 1 + fi + + flutter_ut: + name: Flutter unit test + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Run flutter test + run: flutter test + working-directory: test_shard/integration_test_app + + # pub_publish_check: + # name: pub publish check + # if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - uses: subosito/flutter-action@v2 + # with: + # channel: 'stable' + # cache: true + # - run: bash ci/dart_pub_publish_check.sh + + integration_test_android: + name: Run Flutter Android Integration Tests + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + matrix: + version: ["3.10.0", "3.24.0"] + runs-on: ubuntu-latest + timeout-minutes: 120 + env: + TEST_APP_ID: ${{ secrets.MY_APP_ID }} + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-java@v1 + with: + java-version: "11" + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + - 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: run flutter android integration tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 31 + arch: x86_64 + profile: pixel_5 + ram-size: 2048M + heap-size: 4096M + disk-size: 8192M + script: bash scripts/run_flutter_integration_test.sh "android" + + integration_test_ios: + name: Run Flutter iOS Integration Tests + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + matrix: + version: ["3.10.0", "3.16"] + runs-on: macos-12 + timeout-minutes: 120 + env: + TEST_APP_ID: ${{ secrets.MY_APP_ID }} + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - uses: futureware-tech/simulator-action@v3 + with: + model: 'iPhone 14 Pro Max' + - run: | + brew install coreutils + bash scripts/run_flutter_integration_test.sh "ios" + - name: Get ios crash logs + if: always() + run: | + sleep 30 + + mkdir logs-ios + + CRASH_DIR="${HOME}/Library/Logs/DiagnosticReports/" + OUTPUT_CRASH_DIR=./logs-ios/crash + mkdir -p ${OUTPUT_CRASH_DIR} + + # Copy all files + cp -RP $CRASH_DIR* $OUTPUT_CRASH_DIR + + - name: Upload ios logs + uses: actions/upload-artifact@v4 + if: always() + with: + name: logs-ios-${{ matrix.version }} + path: logs-ios/* + + # integration_test_macos: + # name: Run Flutter macOS Integration Tests + # if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + # strategy: + # matrix: + # version: ["3.10.0", "3.24.0"] + # runs-on: macos-12 + # timeout-minutes: 120 + # env: + # TEST_APP_ID: ${{ secrets.MY_APP_ID }} + # steps: + # - uses: actions/checkout@v3 + # - uses: subosito/flutter-action@v2 + # with: + # flutter-version: ${{ matrix.version }} + # cache: true + # - run: flutter config --enable-macos-desktop + # - run: bash ci/run_flutter_macos_integration_test.sh + # - name: Get iris logs macos + # if: always() + # run: | + # sleep 30 + + # mkdir iris-logs-macos + # if [ -f "${HOME}/Library/Containers/com.example.fakeTestApp/Data/Library/agora-iris.log" ]; then + # cp -RP ${HOME}/Library/Containers/com.example.fakeTestApp/Data/Library/agora-iris.log ./iris-logs-macos/agora-iris-fake-test.log + # fi + # if [ -f "${HOME}/Library/Containers/com.example.integrationTestApp/Data/Library/agora-iris.log" ]; then + # cp -RP ${HOME}/Library/Containers/com.example.integrationTestApp/Data/Library/agora-iris.log ./iris-logs-macos/agora-iris-integration-test.log + # fi + + # CRASH_DIR="${HOME}/Library/Logs/DiagnosticReports/" + # OUTPUT_CRASH_DIR=./iris-logs-macos/crash + # mkdir -p ${OUTPUT_CRASH_DIR} + + # # Copy all files + # cp -RP $CRASH_DIR* $OUTPUT_CRASH_DIR + + # - name: Upload iris logs macos + # uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: iris-logs-macos-${{ matrix.version }} + # path: iris-logs-macos/* + + # integration_test_windows: + # name: Run Flutter Windows Integration Tests + # if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + # strategy: + # matrix: + # version: ["3.10.0", "3.24.0"] + # runs-on: windows-2019 + # timeout-minutes: 120 + # env: + # TEST_APP_ID: ${{ secrets.MY_APP_ID }} + # steps: + # - uses: actions/checkout@v3 + # - uses: subosito/flutter-action@v2 + # with: + # flutter-version: ${{ matrix.version }} + # cache: true + # - name: Set up crash dump environment + # run: .\ci\setup-crash-dumps.ps1 + # shell: powershell + # - run: flutter config --enable-windows-desktop + # - name: Run windows integration test + # shell: bash + # run: | + # bash ci/run_flutter_windows_integration_test.sh + + # - name: Upload crash dumps if exist + # uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: windows-crash-dumps-${{ matrix.version }} + # path: ./CrashDumps/* + + # integration_test_web: + # name: Run Flutter Web Integration Tests + # if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + # strategy: + # matrix: + # version: ['3.x'] + # runs-on: ubuntu-latest + # timeout-minutes: 60 + # env: + # TEST_APP_ID: ${{ secrets.MY_APP_ID }} + # steps: + # - uses: actions/checkout@v3 + # - uses: subosito/flutter-action@v2 + # with: + # flutter-version: ${{ matrix.version }} + # cache: true + # - name: Run web integration test + # shell: bash + # run: | + # chromedriver --port=4444 --trace-buffer-size=100000 & + # bash ci/run_flutter_integration_test.sh web + + build_android_ubuntu: + name: Build Android on Ubuntu + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + matrix: + version: ["3.10.0", "3.24.0"] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v1 + with: + java-version: "11" + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + - run: flutter pub get + - name: Run flutter build apk + run: flutter build apk + working-directory: example + + build_android_windows: + name: Build Android on Windows + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + matrix: + version: ["3.10.0", "3.24.0"] + runs-on: windows-2019 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v1 + with: + java-version: '11' + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - run: flutter pub get + - name: Run flutter build apk + run: flutter build apk + working-directory: example + + build_ios: + name: Build iOS + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + matrix: + version: ["3.10.0", "3.24.0"] + runs-on: macos-12 + timeout-minutes: 120 + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - run: flutter pub get + - name: Run flutter build ios --no-codesign + run: flutter build ios --no-codesign + working-directory: example + + # # This job aim to cover https://github.com/flutter/flutter/issues/135739 + build_ios_xcode_15: + name: Build iOS with xcode 15.x + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + matrix: + version: ['3.x'] + runs-on: macos-13 + timeout-minutes: 120 + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - run: | # https://github.com/actions/runner-images/issues/6746#issuecomment-1380042553 + # set xcode version to use for build + sudo xcode-select -switch /Applications/Xcode_15.0.1.app + # Print used xCode version + xcode-select -print-path + xcodebuild -version + - run: flutter pub get + - name: Run flutter build ios --no-codesign + run: flutter build ios --no-codesign + working-directory: example diff --git a/.github/workflows/code-gen.yaml b/.github/workflows/code-gen.yaml new file mode 100644 index 0000000..9a7549c --- /dev/null +++ b/.github/workflows/code-gen.yaml @@ -0,0 +1,71 @@ +name: Code gen + +on: + workflow_dispatch: + inputs: + target_branch: + description: The branch to run code-gen + type: string + required: true + default: 'main' + + version: + description: 'The version of native sdk in terra-script' + required: false + type: string + default: '' + +jobs: + run_code_gen: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ inputs.target_branch }} + fetch-depth: 0 + - name: enable corepack + run: corepack enable + - name: set node + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - name: Reconfigure git to use HTTP authentication + run: | + git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf ssh://git@github.com/ + - name: Install LLVM and Clang + uses: KyleMayes/install-llvm-action@v1 + with: + version: "15.0.6" # Need ping version to 15.x + directory: ${{ runner.temp }}/llvm + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Run code-gen + run: | + bash scripts/code_gen.sh ${{ inputs.version }} + + - name: Get current date + id: date + run: echo "date=$(date +'%Y%m%d')" >> "$GITHUB_OUTPUT" + + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "Auto generate codes for native sdk version ${{ inputs.version }}" + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + branch: auto-code-gen-${{ steps.date.outputs.date }} + base: ${{ inputs.target_branch }} + delete-branch: true + title: "Auto generate codes for native sdk ${{ inputs.version }}" + body: | + Auto generate codes for native sdk version ${{ inputs.version }} + + *This pull request is opened by bot* + labels: | + ci:skip + reviewers: | + littleGnAl diff --git a/.github/workflows/integration-test-iris-artifacts.yml b/.github/workflows/integration-test-iris-artifacts.yml new file mode 100644 index 0000000..421df45 --- /dev/null +++ b/.github/workflows/integration-test-iris-artifacts.yml @@ -0,0 +1,206 @@ +name: Integration test with iris artifacts + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + pull_request: + types: [labeled, synchronize] + branches: + - main + - release/** + - special/** + - dev/** + +jobs: + integration_test_android: + name: Run Flutter Android Integration Tests + if: ${{ contains(github.event.pull_request.labels.*.name, 'integration_test:iris_artifacts') }} + strategy: + matrix: + version: ['2.10.5', '3.0.0'] + runs-on: macos-12 + timeout-minutes: 120 + env: + TEST_APP_ID: ${{ secrets.MY_APP_ID }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v1 + with: + java-version: '11' + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - name: Checkout hoe + uses: actions/checkout@v3 + with: + repository: littleGnAl/hoe + ref: littlegnal/update + path: hoe + - name: Download iris artifacts + run: | + source scripts/artifacts_version.sh + + PROJECT_DIR=$(pwd) + + mkdir -p output + cd hoe + dart pub get + dart run bin/hoe.dart build-agora-flutter-example \ + --setup-local-dev \ + --project-dir=${PROJECT_DIR} \ + --artifacts-output-dir=${PROJECT_DIR}/output \ + --platforms=android,macos \ + --apple-package-name=io.agora.agoraRtcEngineExample \ + --flutter-package-name=agora_rtc_engine \ + --iris-android-cdn-url=${IRIS_CDN_URL_ANDROID} \ + --iris-macos-cdn-url=${IRIS_CDN_URL_MACOS} + - run: flutter config --enable-macos-desktop + - name: run flutter android integration tests + uses: reactivecircus/android-emulator-runner@v2.21.0 + with: + api-level: 31 + arch: x86_64 + profile: Nexus 6 + ram-size: 2048M + heap-size: 4096M + disk-size: 8192M + script: bash ci/run_flutter_integration_test_android.sh 0 + + integration_test_ios: + name: Run Flutter iOS Integration Tests + if: ${{ contains(github.event.pull_request.labels.*.name, 'integration_test:iris_artifacts') }} + strategy: + matrix: + version: ['2.10.5', '3.0.0'] + runs-on: macos-12 + timeout-minutes: 120 + env: + TEST_APP_ID: ${{ secrets.MY_APP_ID }} + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - uses: futureware-tech/simulator-action@v1 + with: + model: 'iPhone 13 Pro Max' + - name: Checkout hoe + uses: actions/checkout@v3 + with: + repository: littleGnAl/hoe + ref: littlegnal/update + path: hoe + - name: Download iris artifacts + run: | + source scripts/artifacts_version.sh + + PROJECT_DIR=$(pwd) + + mkdir -p output + cd hoe + dart pub get + dart run bin/hoe.dart build-agora-flutter-example \ + --setup-local-dev \ + --project-dir=${PROJECT_DIR} \ + --artifacts-output-dir=${PROJECT_DIR}/output \ + --platforms=ios \ + --apple-package-name=io.agora.agoraRtcEngineExample \ + --flutter-package-name=agora_rtc_engine \ + --iris-ios-cdn-url=${IRIS_CDN_URL_IOS} + - run: bash ci/run_flutter_integration_test_ios.sh 0 + + integration_test_macos: + name: Run Flutter macOS Integration Tests + if: ${{ contains(github.event.pull_request.labels.*.name, 'integration_test:iris_artifacts') }} + strategy: + matrix: + version: ['2.10.5', '3.0.0'] + runs-on: macos-12 + timeout-minutes: 120 + env: + TEST_APP_ID: ${{ secrets.MY_APP_ID }} + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - name: Checkout hoe + uses: actions/checkout@v3 + with: + repository: littleGnAl/hoe + ref: littlegnal/update + path: hoe + - name: Download iris artifacts + run: | + source scripts/artifacts_version.sh + + PROJECT_DIR=$(pwd) + + echo "project dir: ${PROJECT_DIR}" + + ls ${PROJECT_DIR} + + mkdir -p output + cd hoe + dart pub get + dart run bin/hoe.dart build-agora-flutter-example \ + --setup-local-dev \ + --project-dir=${PROJECT_DIR} \ + --artifacts-output-dir=${PROJECT_DIR}/output \ + --platforms=macos \ + --apple-package-name=io.agora.agoraRtcEngineExample \ + --flutter-package-name=agora_rtc_engine \ + --iris-macos-cdn-url=${IRIS_CDN_URL_MACOS} + - run: flutter config --enable-macos-desktop + - run: bash ci/run_flutter_macos_integration_test.sh 0 + + integration_test_windows: + name: Run Flutter Windows Integration Tests + if: ${{ contains(github.event.pull_request.labels.*.name, 'integration_test:iris_artifacts') }} + strategy: + matrix: + version: ['2.10.5', '3.0.0'] + runs-on: windows-2019 + timeout-minutes: 120 + env: + TEST_APP_ID: ${{ secrets.MY_APP_ID }} + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.version }} + cache: true + - name: Checkout hoe + uses: actions/checkout@v3 + with: + repository: littleGnAl/hoe + ref: littlegnal/update + path: hoe + - name: Download iris artifacts + shell: bash + run: | + source scripts/artifacts_version.sh + + PROJECT_DIR=$(pwd) + + mkdir -p output + cd hoe + dart pub get + dart run bin/hoe.dart build-agora-flutter-example \ + --setup-local-dev \ + --project-dir=${PROJECT_DIR} \ + --artifacts-output-dir=${PROJECT_DIR}/output \ + --platforms=windows \ + --apple-package-name=io.agora.agoraRtcEngineExample \ + --flutter-package-name=agora_rtc_engine \ + --iris-windows-cdn-url=${IRIS_CDN_URL_WINDOWS} + - run: flutter config --enable-windows-desktop + - name: Run windows integration test + shell: bash + run: | + bash ci/run_flutter_windows_integration_test.sh 0 \ No newline at end of file diff --git a/.github/workflows/lock.yaml b/.github/workflows/lock.yaml new file mode 100644 index 0000000..585c027 --- /dev/null +++ b/.github/workflows/lock.yaml @@ -0,0 +1,31 @@ +# Borrow from https://github.com/flutter/flutter/blob/master/.github/workflows/lock.yaml +# Configuration for Lock Threads - https://github.com/dessant/lock-threads + +name: 'Lock Threads' + +# By specifying the access of one of the scopes, all of those that are not +# specified are set to 'none'. +permissions: + issues: write + +on: + schedule: + - cron: '0 * * * *' + +jobs: + lock: + permissions: + issues: write + runs-on: ubuntu-latest + if: ${{ github.repository == 'AgoraIO-Extensions/Agora-Flutter-SDK' }} + steps: + - uses: dessant/lock-threads@c1b35aecc5cdb1a34539d14196df55838bb2f836 + with: + process-only: 'issues' + github-token: ${{ github.token }} + # Number of days of inactivity before a closed issue is locked. + issue-inactive-days: '7' + issue-comment: > + This thread has been automatically locked since there has not been + any recent activity after it was closed. If you are still experiencing a + similar issue, please raise a new issue. \ No newline at end of file diff --git a/.github/workflows/more-detail-wanted.yaml b/.github/workflows/more-detail-wanted.yaml new file mode 100644 index 0000000..f39222c --- /dev/null +++ b/.github/workflows/more-detail-wanted.yaml @@ -0,0 +1,20 @@ +name: Ask the user for more detail of the issue +on: + issues: + types: + - labeled +jobs: + add-comment: + if: github.event.label.name == 'more detail wanted' + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Add comment + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.issue.number }} + + body: | + Please follow the [issue template](https://github.com/AgoraIO-Extensions/Agora-Flutter-SDK/blob/main/.github/ISSUE_TEMPLATE/bug_report.yml) to fill in more details about this issue so that we can investigate this issue more easily, thanks! + \ No newline at end of file diff --git a/.github/workflows/no-response.yaml b/.github/workflows/no-response.yaml new file mode 100644 index 0000000..511622e --- /dev/null +++ b/.github/workflows/no-response.yaml @@ -0,0 +1,39 @@ +# Borrow from https://github.com/flutter/flutter/blob/master/.github/workflows/no-response.yaml +name: No Response + +# Both `issue_comment` and `scheduled` event types are required for this Action +# to work properly. +on: + issue_comment: + types: [created] + schedule: + # Schedule for five minutes after the hour, every hour + - cron: '5 * * * *' + +# By specifying the access of one of the scopes, all of those that are not +# specified are set to 'none'. +permissions: + issues: write + +jobs: + noResponse: + runs-on: ubuntu-latest + if: ${{ github.repository == 'AgoraIO-Extensions/Agora-Flutter-SDK' }} + steps: + - uses: godofredoc/no-response@0ce2dc0e63e1c7d2b87752ceed091f6d32c9df09 + with: + token: ${{ github.token }} + # Comment to post when closing an Issue for lack of response. Set to `false` to disable + closeComment: > + Without additional information, we are unfortunately not sure how to + resolve this issue. We are therefore reluctantly going to close this + bug for now. + If you find this problem please file a new issue with the same description, + what happens, logs and the output. All system setups + can be slightly different so it's always better to open new issues and reference + the related ones. + Thanks for your contribution. + # Number of days of inactivity before an issue is closed for lack of response. + daysUntilClose: 14 + # Label requiring a response. + responseRequiredLabel: "waiting for customer response" diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 0000000..bf511c7 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,84 @@ +# This action will create a change logs via release-it and open a pull request, +# after the PR is merged, the action release.yml will be trigger. +name: Prepare release + +on: + workflow_dispatch: + inputs: + release_branch: + description: The branch to be released + type: string + required: true + default: 'main' + + version: + description: 'The version to be released' + required: true + type: string + +jobs: + prepare_release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ inputs.release_branch }} + fetch-depth: 0 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Install release-it + run: | + npm install -g release-it + npm install -g release-it/bumper + npm install -g release-it/conventional-changelog + - name: git config + run: | + git config user.name "${GITHUB_ACTOR}" + git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" + - run: | + release-it ${{ inputs.version }} \ + --git.commit \ + --'git.commitMessage="chore: release ${version}"' \ + --no-git.tag \ + --'git.tagAnnotation="Release ${version}"' \ + --no-git.push \ + --no-github.release \ + --no-github.web \ + --ci + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Get current version of pubspec.yaml + id: pubspec + run: | + PUBSPEC_VERSION=$(grep 'version: ' pubspec.yaml | sed -e 's,.*: \(.*\),\1,') + echo "pubspec version: ${PUBSPEC_VERSION}" + echo "{version}=${PUBSPEC_VERSION}" >> $GITHUB_OUTPUT + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: release ${{ inputs.version }}" + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + signoff: false + branch: prepare-release-${{ inputs.version }} + base: main + delete-branch: true + title: "chore: release ${{ inputs.version }}" + body: | + Prepare releasing **${{ inputs.version }}** + + After this pull request is merged, the following steps below will be going on: + + * Create tag for **${{ inputs.version }}** + * Publish to pub.dev + * Create Github release for **${{ inputs.version }}** + + *This pull request is opened by bot* + labels: | + ci:skip + ci:prepare_release + reviewers: | + littleGnAl diff --git a/.github/workflows/pubdev-publishing.yml b/.github/workflows/pubdev-publishing.yml new file mode 100644 index 0000000..ae2fe72 --- /dev/null +++ b/.github/workflows/pubdev-publishing.yml @@ -0,0 +1,40 @@ +name: Publish to Pub.dev 🚀 + +on: + workflow_dispatch: + inputs: + release_branch: + description: The branch to be released + type: string + required: true + default: 'main' + +jobs: + publishing: + runs-on: ubuntu-latest + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + ref: ${{ inputs.release_branch }} + - name: Get tags + run: git fetch --tags origin + - name: Check Versions 🔎 + run: | + set -eo pipefail + export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + echo "LIB_VERSION: ${LIB_VERSION}" + export PUBSPEC_VERSION=$(grep 'version: ' pubspec.yaml | sed -e 's,.*: \(.*\),\1,') + echo "PUBSPEC_VERSION: ${PUBSPEC_VERSION}" + if [ "$LIB_VERSION" != "${PUBSPEC_VERSION}" ]; then + echo "Version in 'pubspec.yaml' does not match tag."; + exit 1; + fi + - name: Publish Dart Package 🚢 + id: publish + uses: k-paxian/dart-package-publisher@1.5 + with: + accessToken: ${{ secrets.OAUTH_ACCESS_TOKEN }} + refreshToken: ${{ secrets.OAUTH_REFRESH_TOKEN }} + force: true # We have checked the `dart pub publish --dry-run` `in build.yaml`, it's ok to force publish here. + skipTests: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2400ec8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,59 @@ +name: Release to Github release/pub.dev and tag 🚀 + +on: + pull_request: + types: + - closed + +jobs: + release_if_merged: + if: ${{ github.event.pull_request.merged == true && + github.event.pull_request.base.ref == 'main' && + contains(github.event.pull_request.labels.*.name, 'ci:prepare_release') }} + outputs: + release_version: ${{steps.release.outputs.version}} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: main + fetch-depth: 0 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Install release-it + run: | + npm install -g release-it + npm install -g release-it/bumper + npm install -g release-it/conventional-changelog + - name: git config + run: | + git config user.name "${GITHUB_ACTOR}" + git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" + - name: Release + id: release + run: | + PUBSPEC_VERSION=$(grep 'version: ' pubspec.yaml | sed -e 's,.*: \(.*\),\1,') + echo "pubspec version: ${PUBSPEC_VERSION}" + + release-it ${PUBSPEC_VERSION} \ + --no-git.commit \ + --'git.commitMessage="chore: release ${version}"' \ + --git.tag \ + --'git.tagName="${version}"' \ + --'git.tagAnnotation="Release ${version}"' \ + --git.push \ + --github.release \ + --no-github.web \ + --ci + echo "::set-output name=version::${PUBSPEC_VERSION}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Publish Dart Package 🚢 + id: publish + uses: k-paxian/dart-package-publisher@master + with: + accessToken: ${{ secrets.OAUTH_ACCESS_TOKEN }} + refreshToken: ${{ secrets.OAUTH_REFRESH_TOKEN }} + force: true # We have checked the `dart pub publish --dry-run` `in build.yaml`, it's ok to force publish here. + skipTests: true diff --git a/.github/workflows/release_special_version.yml b/.github/workflows/release_special_version.yml new file mode 100644 index 0000000..5241716 --- /dev/null +++ b/.github/workflows/release_special_version.yml @@ -0,0 +1,45 @@ +name: Tag the special version 🚀 + +on: + pull_request: + types: + - closed + +jobs: + release_special_if_merged: + if: ${{ github.event.pull_request.merged == true && + contains(github.event.pull_request.labels.*.name, 'ci:ready_release_special') }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.base.ref }} + fetch-depth: 0 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Install release-it + run: | + npm install -g release-it + npm install -g release-it/bumper + npm install -g release-it/conventional-changelog + - name: git config + run: | + git config user.name "${GITHUB_ACTOR}" + git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" + - run: | + PUBSPEC_VERSION=$(grep 'version: ' pubspec.yaml | sed -e 's,.*: \(.*\),\1,') + echo "pubspec version: ${PUBSPEC_VERSION}" + + release-it ${PUBSPEC_VERSION} \ + --no-git.commit \ + --'git.commitMessage="chore: release ${version}"' \ + --git.tag \ + --'git.tagName="${version}"' \ + --'git.tagAnnotation="Release ${version}"' \ + --git.push \ + --no-github.release \ + --no-github.web \ + --ci + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/schedule-run-ci-external.yml b/.github/workflows/schedule-run-ci-external.yml new file mode 100644 index 0000000..bf5f505 --- /dev/null +++ b/.github/workflows/schedule-run-ci-external.yml @@ -0,0 +1,80 @@ +# This workflow is aim to let external contributors to run the integration tests. +# * Check if the user is a external or internal contributor or not (who is in the org or not). +# * If the user is a external contributor, remove the label "ci:schedule_run_ci" for each push. +# * The PR with label "ci:schedule_run_ci" can trigger the `workflows/build.yml`. +# +# If the user is a external contributor, the repo owener or the users with write access should response for add the +# label "ci:schedule_run_ci" to allow the external contributor to run the CI. +name: Schedule run ci external + +on: + pull_request_target: + types: [labeled, synchronize] + +jobs: + check_permission: + name: Check permission + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Check if external contributors + id: check-contributors-result + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.LABEL_ACTION_PAT }} + debug: true + script: | + const org = "AgoraIO-Extensions" + const userName = "${{ github.event.pull_request.user.login }}" + + let contributorStatus = -1; // unknown + + try { + // see https://docs.github.com/en/rest/orgs/members?apiVersion=2022-11-28#check-organization-membership-for-a-user + const response = await github.request('GET /orgs/{org}/members/{username}', { + org: org, + username: userName, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + + console.log(`response: ${response.data}`); + if (response.status == 204) { + console.log(`Internal contributor: ${userName}`); + } else { + console.log(`External contributor: ${userName}`); + } + + // 0: internal contributors + // 1: external contributors + contributorStatus = response.status == 204 ? 0 : 1 + } catch (error) { + console.log(`Error: ${error.message}`); + // 404 mean external contributors + contributorStatus = error.status == 404 ? 1 : contributorStatus + console.log(`contributorStatus: ${contributorStatus}`); + } + + return contributorStatus; + + - name: Remove label ci:schedule_run_ci if necessary + # If the external contributor (steps.check-contributors-result.outputs.result > 0) push a new commit (github.event.action == 'synchronize'), + # remove the label: ci:schedule_run_ci to avoid the contributor do some harmful things. + if: ${{ github.event.action == 'synchronize' && steps.check-contributors-result.outputs.result > 0 }} + uses: actions-ecosystem/action-remove-labels@v1 + with: + github_token: ${{ secrets.LABEL_ACTION_PAT }} + repo: 'AgoraIO-Extensions' + number: 'Agora-Flutter-SDK' + labels: ci:schedule_run_ci + + schedule_ci: + name: Schedule run ci for external contributors + needs: check_permission + if: ${{ contains(github.event.pull_request.labels.*.name, 'ci:schedule_run_ci') }} + uses: ./.github/workflows/build.yml + secrets: + MY_APP_ID: ${{ secrets.APP_ID_RTM }} + \ No newline at end of file diff --git a/.github/workflows/schedule-run-ci-internal.yml b/.github/workflows/schedule-run-ci-internal.yml new file mode 100644 index 0000000..34531e2 --- /dev/null +++ b/.github/workflows/schedule-run-ci-internal.yml @@ -0,0 +1,42 @@ +name: Schedule run ci internal + +# concurrency: +# group: ${{ github.workflow }}-${{ github.ref }} +# cancel-in-progress: true + +on: + pull_request: + push: + +jobs: + check_permission: + name: Check permission + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + runs-on: ubuntu-latest + timeout-minutes: 60 + outputs: + is_schedule_run_ci: ${{ steps.check-label-step.outputs.is_schedule_run_ci }} + steps: + - name: Check for Secret availability + id: check-label-step + # perform secret check & put boolean result as an output + shell: bash + run: | + APP_ID="${{ secrets.APP_ID_RTM }}" + if [ ! -z "${APP_ID}" ]; then + echo "is_schedule_run_ci=1" >> $GITHUB_OUTPUT; + echo "secrets.APP_ID_RTM is not empty, PR opened by the internal contributors" + else + echo "is_schedule_run_ci=-1" >> $GITHUB_OUTPUT; + echo "secrets.APP_ID_RTM is empty, PR opened by the external contributors" + fi + + schedule_ci: + name: Schedule run ci + needs: check_permission + # If the PR requested by a external contributor, the secrets.APP_ID_RTM should be empty, and skip this workflow + if: ${{ needs.check_permission.outputs.is_schedule_run_ci == 1 }} + uses: ./.github/workflows/build.yml + secrets: + MY_APP_ID: ${{ secrets.APP_ID_RTM }} + \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6cabbfd --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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 +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ + +.plugin_dev diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..aeeea81 --- /dev/null +++ b/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + - platform: android + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + - platform: ios + create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3 + + # 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/.release-it.json b/.release-it.json new file mode 100644 index 0000000..46595e3 --- /dev/null +++ b/.release-it.json @@ -0,0 +1,25 @@ +{ + "plugins": { + "@release-it/conventional-changelog": { + "infile": "CHANGELOG.md", + "header": "# Changelog", + "preset": { + "name": "conventionalcommits", + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + } + ] + } + }, + "@release-it/bumper": { + "in": "pubspec.yaml", + "out": "pubspec.yaml" + } + } +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..91412e4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,169 @@ +# Changelog + +## [1.5.9](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.8...v1.5.9) (2023-11-23) + + +### Bug Fixes + +* some function not call result ([8b75257](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/8b75257ec075dc4bb9fa155618cbe62de91395e7)) + +## [1.5.8](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.7...v1.5.8) (2023-08-07) + + +### Bug Fixes + +* callManager not working on iOS ([5cd710c](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/5cd710c281319c9b211e9d099537e166129fb4c1)) +* getChannelMemberCount ([29140eb](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/29140eb260acb729b0c913d5c436c2ca910cbb1e)) + +## [1.5.7](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.6...v1.5.7) (2023-07-17) + + +### Bug Fixes + +* ios crash while close app ([a25010e](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/a25010e2bab0a6bc90398505d95182a5a62d1847)) + +## [1.5.6](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.5...v1.5.6) (2023-06-28) + + +### Bug Fixes + +* [android] Fix acceptRemoteInvitation return error code -1 ([#152](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/152)) ([a8481ec](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/a8481ec4926134ca71edf8741cfd2457eddb3c14)) + +## [1.5.5](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.4...v1.5.5) (2023-06-09) + + +### Bug Fixes + +* `onMemberCountUpdated` issue on iOS ([9d425f8](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/9d425f8d8446a61c84439023eaa214bc64513bd0)) +* `setLogFileSize` throw ([4570c3d](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/4570c3df11fd1add9d0d370492173c0f5fd2c2fb)) +* fromJson case ([c6b1cc5](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/c6b1cc57d2c5a57048e9d741a3cf07ce6aede343)) + +## [1.5.4](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.3...v1.5.4) (2023-06-05) + + +### Bug Fixes + +* login not working on iOS & close [#148](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/148) ([#149](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/149)) ([6dbc424](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/6dbc4240c2e280abe3b124797edefda001b73e47)) + +## [1.5.3](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.2...v1.5.3) (2023-06-05) + + +### Bug Fixes + +* FlutterStandardTypedData & init with wrong appId ([3bba6a9](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/3bba6a9806b0a505da6a329dc77f8342dc066d44)) + + +### Features + +* Flutter - Specify AreaCode in RTM [#87](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/87) close [#146](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/146) ([#147](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/147)) ([c86657d](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/c86657d46d756ef413d6deb3bbedad5cd0fe445b)) + +## [1.5.2](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.1...v1.5.2) (2023-06-05) + + +### Bug Fixes + +* 1.5.1 crash [#145](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/145) Unhandled Exception when calling onPeersOnlineStatusChanged [#146](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/146) ([df4bcec](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/df4bcec5e65e8ae434781c52d093a4499b6c5321)) + +## [1.5.1](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/compare/v1.5.0...v1.5.1) (2023-06-02) + + +### Bug Fixes + +* some issues [close [#142](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/142) [#141](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/141)] ([#144](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/issues/144)) ([79af48b](https://github.com/AgoraIO/Agora-Flutter-RTM-SDK/commit/79af48b1054ee4da37277a8e94e6953a7793c598)) + +# [1.5.0](https://github.com/AgoraIO/Flutter-RTM/compare/v1.1.1...v1.5.0) (2022-11-30) + + +### Features + +* upgrade native to 1.5.x ([69738fc](https://github.com/AgoraIO/Flutter-RTM/commit/69738fcd67324256bf2c5a67be0b4eb97a22d4aa)) + +# [1.1.1](https://github.com/AgoraIO/Flutter-RTM/compare/v1.1.0...v1.1.1) (2022-03-08) + +* Update package contents to pass all pub.dev\'s checks + +> No significant code changes + +# [1.1.0](https://github.com/AgoraIO/Flutter-RTM/compare/v1.0.1...v1.1.0) (2022-03-08) + + +### Features + +* upgrade to 1.4.10 ([52ef541](https://github.com/AgoraIO/Flutter-RTM/commit/52ef5414d8a4c1fedb897ffc12f87143a4b7ff42)) + +## [1.0.1](https://github.com/AgoraIO/Flutter-RTM/compare/v1.0.0...v1.0.1) (2021-09-27) + + +### Bug Fixes + +* null-safety error [#96](https://github.com/AgoraIO/Flutter-RTM/issues/96) ([62ab0f4](https://github.com/AgoraIO/Flutter-RTM/commit/62ab0f45feafc4d6b0bee9d588f90f996956e80b)) +* pub get error on flutter 2.5 [#98](https://github.com/AgoraIO/Flutter-RTM/issues/98) ([5beb5f7](https://github.com/AgoraIO/Flutter-RTM/commit/5beb5f7e23684785ec28eaca38aeb1b6e0131f9f)) + +# [1.0.0](https://github.com/AgoraIO/Flutter-RTM/compare/v1.0.0-rc.1...v1.0.0) (2021-08-25) + + +### Bug Fixes + +* Removed unnecessary blank in `README.md` ([0e64bc3](https://github.com/AgoraIO/Flutter-RTM/commit/0e64bc352952ca0fb04f062352462bb4375251a9)) + + +### Features + +* upgrade to native 1.4.+ ([39dda38](https://github.com/AgoraIO/Flutter-RTM/commit/39dda380a23b94b077e3bd19b2c830b6bd816501)) + + + +# [1.0.0-rc.0](https://github.com/AgoraIO/Flutter-RTM/compare/v1.0.0-rc.1...v1.0.0) (2021-04-21) + +# Change log + +## 1.0.0-rc.1 +* fix error of get value + +## 1.0.0-rc.0 +* support null safety + +## 0.9.14 +* fix iOS build error + +## 0.9.13 +* add `offline` and `historical` for `sendMessage` and `sendMessageToPeer` + +## 0.9.12 +* make channel and client listeners null-safe + +## 0.9.11 +* upgrade to rtm 1.2.2 +* support channel attributes + +## 0.9.10 +* fix iOS native crash + +## 0.9.9 +* fix Android `Kotlin Gradle plugin version` bug, now use your root project kotlin version + +## 0.9.8 +* fix Android `flutter pub get` `Please verify that this file has read permission and try again` + +## 0.9.7 +* fix iOS cocoapods `target has transitive dependencies that include static binaries` + +## 0.9.6 +* upgrade to rtm 1.0.1: Support all agora_rtm native api. +* refactor: ios & android, use FlutterEventChannel to serve agora_rtm event handler +* fix multiple instance conflicts + +## 0.9.5 +* upgrade to rtm 1.0.0 +* add method: setLocalUserAttributes, addOrUpdateLocalUserAttributes, deleteLocalUserAttributesByKeys clearLocalUserAttributes getUserAttributes getUserAttributesByKeys + +## 0.9.4 +* fix android pending exception java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread + +## 0.9.3 + +* Bump kotlin version to 1.3.0 + +## 0.9.2 + +* Flutter for Agora RTM SDK first release \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..61bd225 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# Contribute your code + +We are glad you are here! Everyone is welcome to contribute code via pull requests, to help people who ask for help, to add to our documentation, or to help out in any other way. + +This document briefly describes some guidance on how you can contribute to this repository. + +## Source code structures + +1. **[lib/src/*.dart](lib/src)**: The entry of Dart API. +2. **[example](example)**: API Examples. +3. **[android](android)**: Android native implementation. +4. **[ios](ios)**: iOS native implementation. +5. **[macos](macos)**: Mac native implementation. +6. **[windows](windows)**: Windows native implementation. + +While creating PR, below materials could be helpful: + +* [Effective Dart](https://www.dartlang.org/guides/language/effective-dart) +* [Style guide for flutter](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3e8626c --- /dev/null +++ b/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2019 Agora Lab, Inc (http://www.agora.io/) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..82fb6fd --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# AgoraRtm + +This Flutter plugin is a wapper for [Agora RTM SDK](https://docs.agora.io/en). + +

+ Pub.dev likes + + Pub.dev points
+ latest version + Platform + License + + RTE Dev Slack Link + +

+ + +Agora.io provides building blocks for you to add real-time messaging through a simple and powerful SDK. You can integrate the Agora RTM SDK to enable real-time messaging in your own application quickly. + +> NOTE: The `main` branch is major update base on the Agora RTM SDK 2.x, which introduces some break changes. previous releases please see the following branches(the version < 2.x): +> +> - [1.x](https://github.com/AgoraIO-Extensions/Agora-Flutter-RTM-SDK/tree/master) + +## Usage + +To use this plugin, add `agora_rtm` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). + +## Getting Started + +* See the [example](example) directory for a sample app using AgoraRtm. + +## Feedback + +If you have any problems or suggestions regarding the sample projects, feel free to file an [issue](https://github.com/AgoraIO-Extensions/Agora-Flutter-RTM-SDK/issues) OR pull request. + +For urgent issues, please submit a ticket to [Agora Support](https://www.agora.io/en/customer-support/) to receive a timely response. + +## How to contribute + +To help work on this sdk, see our [contributor guide](https://github.com/AgoraIO/Flutter-RTM/blob/master/CONTRIBUTING.md). diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..4def2af --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx + +libs/ diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..7578cdf --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,77 @@ +group = "io.agora.agorartm.agora_rtm" +version = "1.0" + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:7.3.0") + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: "com.android.library" + +android { + if (project.android.hasProperty("namespace")) { + namespace = "io.agora.agorartm.agora_rtm" + } + + compileSdk = 34 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdk = 16 + consumerProguardFiles 'consumer-rules.pro' + } + + sourceSets { + if (isDev(project)) { + println("Include libs/ to jniLibs.srcDirs for debugging.") + main.jniLibs.srcDirs += 'libs' + } + } + + dependencies { + testImplementation("junit:junit:4.13.2") + testImplementation("org.mockito:mockito-core:5.0.0") + } + + testOptions { + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} + +dependencies { + if (isDev(project)) { + println("Include libs/*jar for debugging.") + api fileTree(dir: "libs", include: ["*.jar"]) + } else { + api 'io.agora.rtm:iris-rtm:2.2.1-build.1' + api 'io.agora:agora-rtm:2.2.1' + } +} + +static boolean isDev(Project project) { + def devFile = new File(project.projectDir, '../.plugin_dev') + return devFile.exists() +} diff --git a/android/consumer-rules.pro b/android/consumer-rules.pro new file mode 100644 index 0000000..b5b5975 --- /dev/null +++ b/android/consumer-rules.pro @@ -0,0 +1,5 @@ +-keepattributes *Annotation* +-keep class kotlin.** { *; } +-keep class org.jetbrains.** { *; } + +-keep class io.agora.**{*;} diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..b18bc36 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'agora_rtm' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e34453a --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/android/src/main/java/io/agora/agorartm/agora_rtm/AgoraRtmPlugin.java b/android/src/main/java/io/agora/agorartm/agora_rtm/AgoraRtmPlugin.java new file mode 100644 index 0000000..a2745cd --- /dev/null +++ b/android/src/main/java/io/agora/agorartm/agora_rtm/AgoraRtmPlugin.java @@ -0,0 +1,42 @@ +package io.agora.agorartm.agora_rtm; + +import androidx.annotation.NonNull; + +import io.flutter.Log; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +/** AgoraRtmPlugin */ +public class AgoraRtmPlugin implements FlutterPlugin, MethodCallHandler { + private MethodChannel channel; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "agora_rtm"); + channel.setMethodCallHandler(this); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + if ("androidInit".equals(call.method)) { + // dart ffi DynamicLibrary.open do not trigger JNI_OnLoad in iris, so we need call java + // System.loadLibrary here to trigger the JNI_OnLoad explicitly. + try { + System.loadLibrary("AgoraRtmWrapper"); + } catch (Exception e) { + Log.e("AgoraRtmPlugin", "androidInit error:\n" + e); + } + result.success(0); + } else { + result.notImplemented(); + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } +} diff --git a/android/src/test/java/io/agora/agorartm/agora_rtm/AgoraRtmPluginTest.java b/android/src/test/java/io/agora/agorartm/agora_rtm/AgoraRtmPluginTest.java new file mode 100644 index 0000000..8d05078 --- /dev/null +++ b/android/src/test/java/io/agora/agorartm/agora_rtm/AgoraRtmPluginTest.java @@ -0,0 +1,29 @@ +package io.agora.agorartm.agora_rtm; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import org.junit.Test; + +/** + * This demonstrates a simple unit test of the Java portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +public class AgoraRtmPluginTest { + @Test + public void onMethodCall_getPlatformVersion_returnsExpectedValue() { + AgoraRtmPlugin plugin = new AgoraRtmPlugin(); + + final MethodCall call = new MethodCall("getPlatformVersion", null); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.onMethodCall(call, mockResult); + + verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE); + } +} diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..7bfa1cd --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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 +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# 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 + +pubspec.lock diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..5adfdf1 --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# agora_rtm_example + +Demonstrates how to use the agora_rtm plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://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/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# 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 + +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/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..93e3edc --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,75 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 34 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "io.agora.agorartm.agora_rtm_example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + def devFile = project.file("${rootProject.projectDir.getParentFile().parentFile.parentFile}/android/.plugin_dev") + if (devFile.exists()) { + implementation fileTree(dir: "${rootProject.projectDir.getParentFile().parentFile.parentFile}/android/libs", include: ["*.aar"]) + } + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} + diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f3535f4 --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/java/io/agora/agorartm/agora_rtm_example/MainActivity.java b/example/android/app/src/main/java/io/agora/agorartm/agora_rtm_example/MainActivity.java new file mode 100644 index 0000000..62fba9f --- /dev/null +++ b/example/android/app/src/main/java/io/agora/agorartm/agora_rtm_example/MainActivity.java @@ -0,0 +1,6 @@ +package io.agora.agorartm.agora_rtm_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..6617c1e --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.0' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..3b5b324 --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e1ca574 --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 0000000..993c497 --- /dev/null +++ b/example/ios/.gitignore @@ -0,0 +1,36 @@ +**/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 + +Podfile.lock diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/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/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/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/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 0000000..c9339a0 --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + 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/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..51f2801 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,745 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 331C80F3294D02FB00263BE5 /* RunnerTests.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 8577C49CF711DE37F7352BA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C88C4489759DC63543E283A /* libPods-Runner.a */; }; + 8D2EF03A53CA5B50F1D828FE /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9579CEECC6D70C80EB478B18 /* libPods-RunnerTests.a */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1C972335EC0A8F7FCC3D9BD4 /* 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 = ""; }; + 1CDE6FD80983C85C78C08663 /* 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 = ""; }; + 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80F3294D02FB00263BE5 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 4C88C4489759DC63543E283A /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 5CBD9AA61DBAFD02FA7744B4 /* 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 = ""; }; + 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 = ""; }; + 8064681CB591E39ECF381DDF /* 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 = ""; }; + 9579CEECC6D70C80EB478B18 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B24851577BAF785162601448 /* 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 = ""; }; + B60449CBF2EAF273F71BFC57 /* 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80EE294D02FB00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D2EF03A53CA5B50F1D828FE /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8577C49CF711DE37F7352BA4 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80F2294D02FB00263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80F3294D02FB00263BE5 /* RunnerTests.m */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 331C80F2294D02FB00263BE5 /* RunnerTests */, + 97C146EF1CF9000F007C117D /* Products */, + BDF33E57AF664C3B1C77F68C /* Pods */, + B11F60838EA81B459149E4D1 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + B11F60838EA81B459149E4D1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4C88C4489759DC63543E283A /* libPods-Runner.a */, + 9579CEECC6D70C80EB478B18 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + BDF33E57AF664C3B1C77F68C /* Pods */ = { + isa = PBXGroup; + children = ( + 1C972335EC0A8F7FCC3D9BD4 /* Pods-Runner.debug.xcconfig */, + 1CDE6FD80983C85C78C08663 /* Pods-Runner.release.xcconfig */, + 8064681CB591E39ECF381DDF /* Pods-Runner.profile.xcconfig */, + B24851577BAF785162601448 /* Pods-RunnerTests.debug.xcconfig */, + 5CBD9AA61DBAFD02FA7744B4 /* Pods-RunnerTests.release.xcconfig */, + B60449CBF2EAF273F71BFC57 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80F0294D02FB00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 9228D83EC8BC42D76F48CE9A /* [CP] Check Pods Manifest.lock */, + 331C80ED294D02FB00263BE5 /* Sources */, + 331C80EE294D02FB00263BE5 /* Frameworks */, + 331C80EF294D02FB00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80F6294D02FB00263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80F1294D02FB00263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 478491549642680C16F1F13C /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 81B00DEBBE3E8C2DA3E9FDA9 /* [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 = { + 331C80F0294D02FB00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + 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 */, + 331C80F0294D02FB00263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80EF294D02FB00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 478491549642680C16F1F13C /* [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; + }; + 81B00DEBBE3E8C2DA3E9FDA9 /* [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; + }; + 9228D83EC8BC42D76F48CE9A /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80ED294D02FB00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80F4294D02FB00263BE5 /* RunnerTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80F6294D02FB00263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C80F5294D02FB00263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.agora.agorartm.agoraRtmExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRIP_STYLE = "non-global"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C80F8294D02FB00263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B24851577BAF785162601448 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.agorartm.agoraRtmExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C80F9294D02FB00263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5CBD9AA61DBAFD02FA7744B4 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.agorartm.agoraRtmExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C80FA294D02FB00263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B60449CBF2EAF273F71BFC57 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.agorartm.agoraRtmExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.agora.agorartm.agoraRtmExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRIP_STYLE = "non-global"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.agora.agorartm.agoraRtmExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRIP_STYLE = "non-global"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80F7294D02FB00263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80F8294D02FB00263BE5 /* Debug */, + 331C80F9294D02FB00263BE5 /* Release */, + 331C80FA294D02FB00263BE5 /* 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/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..3d0fb00 --- /dev/null +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..36e21bb --- /dev/null +++ b/example/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..70e8393 --- /dev/null +++ b/example/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#import "AppDelegate.h" +#import "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/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/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..7353c41 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..6ed2d93 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cd7b00 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..fe73094 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..321773c Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..502f463 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..e9f5fea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..84ac32a Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..8953cba Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..0467bf1 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/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/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/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/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist new file mode 100644 index 0000000..7cf6315 --- /dev/null +++ b/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Agora Rtm + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + agora_rtm_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/example/ios/RunnerTests/RunnerTests.m b/example/ios/RunnerTests/RunnerTests.m new file mode 100644 index 0000000..c86f5e3 --- /dev/null +++ b/example/ios/RunnerTests/RunnerTests.m @@ -0,0 +1,33 @@ +#import +#import +#import + +@import agora_rtm; + +// This demonstrates a simple unit test of the Objective-C portion of this plugin's implementation. +// +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. + +@interface RunnerTests : XCTestCase + +@end + +@implementation RunnerTests + +- (void)testExample { + AgoraRtmPlugin *plugin = [[AgoraRtmPlugin alloc] init]; + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getPlatformVersion" + arguments:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"result block must be called"]; + [plugin handleMethodCall:call + result:^(id result) { + NSString *expected = [NSString + stringWithFormat:@"iOS %@", UIDevice.currentDevice.systemVersion]; + XCTAssertEqualObjects(result, expected); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +@end diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..592a006 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,51 @@ +import 'package:agora_rtm_example/src/rtm_api_demo.dart'; +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'src/log_sink.dart'; + +void main() { + FlutterError.onError = (details) { + FlutterError.presentError(details); + logSink.log(details.toString()); + }; + + // TODO(littlegnal): The newer version of Flutter SDK doc shows use of the + // `PlatformDispatcher.instance.onError` but not `runZonedGuarded` to + // handle "Errors not caught by Flutter", + // see: https://docs.flutter.dev/testing/errors#handling-all-types-of-errors, + // follow the Flutter SDK doc after we can bump the mini supported Flutter SDK (currently 2.10.x) + // to the newer version of Flutter SDK. + runZonedGuarded(() { + runApp(const MyApp()); + }, (error, stackTrace) { + logSink.log(error.toString()); + }); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('RTM example app'), + actions: const [LogActionWidget()], + ), + body: const RtmApiDemo(), + ), + ); + } +} diff --git a/example/lib/src/config.dart b/example/lib/src/config.dart new file mode 100644 index 0000000..a24381c --- /dev/null +++ b/example/lib/src/config.dart @@ -0,0 +1,23 @@ +/// Get your own App ID at https://dashboard.agora.io/ +const String appId = ''; + +/// Please refer to https://docs.agora.io/en/Agora%20Platform/token +const String token = ''; + +/// Your channel ID +const String channelId = ''; + +/// Your int user ID +const int uid = 0; + +/// Your user ID for the screen sharing +const int screenSharingUid = 10; + +/// Your string user ID +const String stringUid = '0'; + +String get musicCenterAppId { + // Allow pass a `token` as an environment variable with name `TEST_TOKEN` by using --dart-define + return const String.fromEnvironment('MUSIC_CENTER_APPID', + defaultValue: ''); +} diff --git a/example/lib/src/example_actions_widget.dart b/example/lib/src/example_actions_widget.dart new file mode 100644 index 0000000..bdd9c77 --- /dev/null +++ b/example/lib/src/example_actions_widget.dart @@ -0,0 +1,115 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +typedef ExampleActionsBuilder = Widget Function( + BuildContext context, bool isLayoutHorizontal); + +class ExampleActionsWidget extends StatelessWidget { + const ExampleActionsWidget({ + Key? key, + required this.displayContentBuilder, + this.actionsBuilder, + }) : super(key: key); + + final ExampleActionsBuilder displayContentBuilder; + + final ExampleActionsBuilder? actionsBuilder; + + @override + Widget build(BuildContext context) { + final mediaData = MediaQuery.of(context); + final bool isLayoutHorizontal = mediaData.size.aspectRatio >= 1.5 || + (kIsWeb || + !(defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.iOS)); + + if (actionsBuilder == null) { + return displayContentBuilder(context, isLayoutHorizontal); + } + + const actionsTitle = Text( + 'Actions', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24), + ); + + if (isLayoutHorizontal) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + flex: 1, + child: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.fromLTRB(16, 24, 16, 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + actionsTitle, + actionsBuilder!(context, isLayoutHorizontal), + ], + ), + ), + ), + ), + Container( + color: Colors.grey.shade100, + width: 20, + ), + Expanded( + flex: 2, + child: displayContentBuilder(context, isLayoutHorizontal), + ), + ], + ); + } + + return Stack( + children: [ + SizedBox.expand( + child: Container( + padding: const EdgeInsets.only(bottom: 150), + child: displayContentBuilder(context, isLayoutHorizontal), + ), + ), + DraggableScrollableSheet( + initialChildSize: 0.25, + snap: true, + maxChildSize: 0.7, + builder: (BuildContext context, ScrollController scrollController) { + return Container( + decoration: const BoxDecoration( + color: Color.fromARGB(255, 253, 253, 253), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(24.0), + topRight: Radius.circular(24.0), + ), + boxShadow: [ + BoxShadow( + blurRadius: 20.0, + color: Colors.grey, + ), + ]), + padding: const EdgeInsets.fromLTRB(16, 24, 16, 0), + child: SingleChildScrollView( + controller: scrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + actionsTitle, + actionsBuilder!(context, isLayoutHorizontal), + ], + ), + ), + ); + }, + ) + ], + ); + } +} diff --git a/example/lib/src/internal/internal_config.dart b/example/lib/src/internal/internal_config.dart new file mode 100644 index 0000000..85221d2 --- /dev/null +++ b/example/lib/src/internal/internal_config.dart @@ -0,0 +1,16 @@ +import '../config.dart' as config; + +String get appId { + const appId = String.fromEnvironment('TEST_RTM_APP_ID', defaultValue: ''); + return appId.isNotEmpty ? appId : config.appId; +} + +String get token { + const token = String.fromEnvironment('TEST_RTM_TOKEN', defaultValue: ''); + return token.isNotEmpty ? token : config.token; +} + +String get channelId { + const channelId = String.fromEnvironment('TEST_CHANNEL_ID', defaultValue: ''); + return channelId.isNotEmpty ? channelId : config.channelId; +} diff --git a/example/lib/src/log_sink.dart b/example/lib/src/log_sink.dart new file mode 100644 index 0000000..7c71355 --- /dev/null +++ b/example/lib/src/log_sink.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; + +/// [AppBar] action that shows a [Overlay] with log. +class LogActionWidget extends StatefulWidget { + /// Construct the [LogActionWidget] + const LogActionWidget({Key? key}) : super(key: key); + + @override + _LogActionWidgetState createState() => _LogActionWidgetState(); +} + +class _LogActionWidgetState extends State { + bool _isOverlayShowed = false; + + OverlayEntry? _overlayEntry; + + @override + void dispose() { + _removeOverlay(); + + super.dispose(); + } + + void _removeOverlay() { + if (_overlayEntry != null) { + _overlayEntry!.remove(); + _overlayEntry = null; + } + } + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: () { + if (_isOverlayShowed) { + _removeOverlay(); + } else { + _overlayEntry = OverlayEntry(builder: (c) { + return Positioned( + left: 0, + top: 0, + height: MediaQuery.of(context).size.height * 0.5, + width: MediaQuery.of(context).size.width, + child: Container( + color: Colors.black87, + child: SafeArea( + bottom: false, + child: Material( + color: Colors.transparent, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + onPressed: () { + logSink.clear(); + }, + child: const Text( + 'Clear log', + style: TextStyle(color: Colors.white), + ), + ), + Expanded( + child: Align( + alignment: Alignment.topRight, + child: IconButton( + color: Colors.transparent, + onPressed: () { + _removeOverlay(); + _isOverlayShowed = !_isOverlayShowed; + }, + icon: const Icon( + Icons.close, + color: Colors.white, + )), + ), + ), + ], + ), + const Expanded( + child: SingleChildScrollView( + child: LogWidget(), + ), + ) + ], + ), + ), + ), + ), + ); + }); + Overlay.of(context).insert(_overlayEntry!); + } + _isOverlayShowed = !_isOverlayShowed; + // setState(() { + + // }); + }, + child: const Text( + 'Log', + style: TextStyle(color: Colors.white), + )); + } +} + +/// LogWidget +class LogWidget extends StatefulWidget { + /// Construct the [LogWidget] + const LogWidget({ + Key? key, + this.logSink, + this.textStyle = const TextStyle(fontSize: 15, color: Colors.white), + }) : super(key: key); + + /// This [LogSink] is used to add log. + final LogSink? logSink; + + /// The text style of the log. + final TextStyle textStyle; + + @override + _LogWidgetState createState() => _LogWidgetState(); +} + +class _LogWidgetState extends State { + VoidCallback? _listener; + late final LogSink _logSink; + + @override + void initState() { + super.initState(); + + _logSink = widget.logSink ?? _defaultLogSink; + + _listener ??= () { + setState(() {}); + }; + + _logSink.addListener(_listener!); + } + + @override + void dispose() { + if (_listener != null) { + _logSink.removeListener(_listener!); + _listener = null; + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Text( + _logSink.output(), + style: widget.textStyle, + ); + } +} + +/// Class that add and update the log in [LogActionWidget] +class LogSink extends ChangeNotifier { + final StringBuffer _stringBuffer = StringBuffer(); + + /// Add log to [LogActionWidget] + void log(String log) { + _stringBuffer.writeln(log); + notifyListeners(); + } + + /// Get all logs + String output() { + return _stringBuffer.toString(); + } + + /// Clear logs + void clear() { + _stringBuffer.clear(); + notifyListeners(); + } +} + +final LogSink _defaultLogSink = LogSink(); + +/// The global [LogSink] +LogSink get logSink => _defaultLogSink; diff --git a/example/lib/src/rtm_api_demo.dart b/example/lib/src/rtm_api_demo.dart new file mode 100644 index 0000000..3456c86 --- /dev/null +++ b/example/lib/src/rtm_api_demo.dart @@ -0,0 +1,545 @@ +import 'dart:collection'; +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:flutter/material.dart'; + +import 'package:agora_rtm_example/src/internal/internal_config.dart' as config; + +import 'log_sink.dart'; + +class RtmApiDemo extends StatefulWidget { + const RtmApiDemo({super.key}); + + @override + State createState() => _RtmApiDemoState(); +} + +class _RtmApiDemoState extends State { + late TextEditingController _userIdController; + late TextEditingController _channelNameController; + + bool _isSubscribeWithMessage = true; + bool _isSubscribeWithMetadata = false; + bool _isSubscribeWithPresence = true; + bool _isSubscribeWithLock = false; + bool _isSubscribeBeQuiet = false; + + bool _isSetChannelMetadataRecordTs = false; + bool _isSetChannelMetadataRecordUserId = false; + late TextEditingController _setChannelMetadataLockNameController; + late TextEditingController _setChannelMetadataRevisionController; + late TextEditingController _setChannelMetadataMajorRevisionController; + + bool _isWhoNowIncludeUserId = true; + bool _isWhoNowIncludeState = false; + late TextEditingController _isWhoNowPageController; + + late TextEditingController _rtmClientMessageController; + late TextEditingController _rtmClientPublishCustomTypeController; + + late KeyValueInputGroupWidgetController _keyValueInputGroupWidgetController; + + RtmChannelType _rtmChannelType = RtmChannelType.message; + + late RtmClient _rtmClient; + + @override + void initState() { + super.initState(); + + _userIdController = TextEditingController(); + _channelNameController = TextEditingController(text: config.channelId); + _rtmClientMessageController = TextEditingController(); + _setChannelMetadataLockNameController = TextEditingController(); + _setChannelMetadataRevisionController = TextEditingController(); + _setChannelMetadataMajorRevisionController = TextEditingController(); + _isWhoNowPageController = TextEditingController(); + _rtmClientPublishCustomTypeController = TextEditingController(); + _keyValueInputGroupWidgetController = KeyValueInputGroupWidgetController(); + } + + @override + void dispose() { + _userIdController.dispose(); + _channelNameController.dispose(); + _rtmClientMessageController.dispose(); + _setChannelMetadataLockNameController.dispose(); + _setChannelMetadataRevisionController.dispose(); + _setChannelMetadataMajorRevisionController.dispose(); + _isWhoNowPageController.dispose(); + _rtmClientPublishCustomTypeController.dispose(); + _keyValueInputGroupWidgetController.dispose(); + super.dispose(); + } + + Widget _textField(TextEditingController controller, String hintText) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + TextField( + controller: controller, + decoration: InputDecoration(hintText: hintText), + ), + const SizedBox( + height: 10, + ), + ], + ); + } + + Widget _button(String text, Future Function() call) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + ElevatedButton( + child: Text(text), + onPressed: () { + _logCall(text, call); + }, + ), + const SizedBox( + height: 10, + ), + ], + ); + } + + Widget _switch(String title, bool value, ValueChanged callback) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text('$title: '), + Switch( + value: value, + onChanged: callback, + ) + ]); + } + + Widget _card(List children) { + return Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: children, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 8.0, right: 8.0), + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + _textField(_userIdController, 'Input user id'), + _button('create RTM', () async { + final (status, client) = + await RTM(config.appId, _userIdController.text); + if (status.error) { + logSink.log( + '[error] errorCode: ${status.errorCode}, operation: ${status.operation}, reason: ${status.reason}'); + return; + } + _rtmClient = client; + + logSink.log('create RTM success.'); + _rtmClient.addListener( + linkState: (event) { + logSink.log('[linkState] ${event.toJson()}'); + }, + message: (event) { + logSink.log('[message] event: ${event.toJson()}'); + }, + presence: (event) { + logSink.log('[presence] event: ${event.toJson()}'); + }, + topic: (event) { + logSink.log('[topic] event: ${event.toJson()}'); + }, + lock: (event) { + logSink.log('[lock] event: ${event.toJson()}'); + }, + storage: (event) { + logSink.log('[storage] event: ${event.toJson()}'); + }, + token: (channelName) { + logSink.log('[token] channelName: $channelName'); + }, + ); + await _rtmClient.setParameters('{"rtm.log_filter":2063}'); + }), + _textField(_channelNameController, 'Input channel name'), + _button('RtmClient.login', () async { + final (status, _) = await _rtmClient.login(config.token); + logSink.log('[LoginResult] errorCode: ${status.errorCode}'); + }), + _button('RtmClient.logout', () async { + final (status, _) = await _rtmClient.logout(); + logSink.log('[LogoutResult] errorCode: ${status.errorCode}'); + }), + const Text('RtmChannelType: '), + DropdownButton( + items: RtmChannelType.values + .map((e) => DropdownMenuItem( + value: e, + child: Text( + e.toString().split('.')[1], + ), + )) + .toList(), + value: _rtmChannelType, + onChanged: (v) { + setState(() { + _rtmChannelType = v!; + }); + }, + ), + _card( + [ + Wrap( + children: [ + _switch('withMessage', _isSubscribeWithMessage, (v) { + setState(() { + _isSubscribeWithMessage = v; + }); + }), + _switch('withMetadata', _isSubscribeWithMetadata, (v) { + setState(() { + _isSubscribeWithMetadata = v; + }); + }), + _switch('withPresence', _isSubscribeWithPresence, (v) { + setState(() { + _isSubscribeWithPresence = v; + }); + }), + _switch('withLock', _isSubscribeWithLock, (v) { + setState(() { + _isSubscribeWithLock = v; + }); + }), + _switch('beQuiet', _isSubscribeBeQuiet, (v) { + setState(() { + _isSubscribeBeQuiet = v; + }); + }), + ], + ), + _button('RtmClient.subscribe', () async { + final (status, _) = await _rtmClient.subscribe( + _channelNameController.text, + withMessage: _isSubscribeWithMessage, + withMetadata: _isSubscribeWithMetadata, + withPresence: _isSubscribeWithPresence, + withLock: _isSubscribeWithLock, + beQuiet: _isSubscribeBeQuiet, + ); + + logSink + .log('[SubscribeResult] errorCode: ${status.errorCode}'); + }), + _button('RtmClient.unsubscribe', () async { + final (status, result) = + await _rtmClient.unsubscribe(_channelNameController.text); + + logSink.log( + '[UnsubscribeResult] channelName: ${result?.channelName}, errorCode: ${status.errorCode}'); + }), + ], + ), + _card( + [ + Wrap( + children: [ + _switch('recordTs', _isSetChannelMetadataRecordTs, (v) { + setState(() { + _isSetChannelMetadataRecordTs = v; + }); + }), + _switch('recordUserId', _isSetChannelMetadataRecordUserId, + (v) { + setState(() { + _isSetChannelMetadataRecordUserId = v; + }); + }), + ], + ), + KeyValueInputGroupWidget( + controller: _keyValueInputGroupWidgetController), + _textField( + _setChannelMetadataLockNameController, 'Input lock name'), + _textField(_setChannelMetadataMajorRevisionController, + 'Input majorRevision'), + _textField( + _setChannelMetadataRevisionController, 'Input revision'), + _button('RtmStorage.setChannelMetadata', () async { + final storage = _rtmClient.getStorage(); + final (status, result) = await storage.setChannelMetadata( + _channelNameController.text, + _rtmChannelType, + _keyValueInputGroupWidgetController.keyValuePairs + .map((e) => MetadataItem( + key: e.key, + value: e.value, + )) + .toList(), + recordTs: _isSetChannelMetadataRecordTs, + recordUserId: _isSetChannelMetadataRecordUserId, + lockName: _setChannelMetadataLockNameController.text, + ); + + logSink.log( + '[SetChannelMetadataResult] channelName: ${result?.channelName}, channelType: ${result?.channelType}, errorCode: ${status.errorCode}'); + }), + _button('RtmStorage.updateChannelMetadata', () async { + final storage = _rtmClient.getStorage(); + + final (status, result) = await storage.updateChannelMetadata( + _channelNameController.text, + _rtmChannelType, + _keyValueInputGroupWidgetController.keyValuePairs + .map((e) => MetadataItem( + key: e.key, + value: e.value, + revision: int.tryParse( + _setChannelMetadataRevisionController + .text) ?? + 0)) + .toList(), + recordTs: _isSetChannelMetadataRecordTs, + recordUserId: _isSetChannelMetadataRecordUserId, + lockName: _setChannelMetadataLockNameController.text, + ); + + logSink.log( + '[UpdateChannelMetadataResult] channelName: ${result?.channelName}, channelType: ${result?.channelType}, errorCode: ${status.errorCode}'); + }), + _button('RtmStorage.removeChannelMetadata', () async { + final storage = _rtmClient.getStorage(); + + final (status, result) = await storage.removeChannelMetadata( + _channelNameController.text, + _rtmChannelType, + metadata: _keyValueInputGroupWidgetController.keyValuePairs + .map((e) => MetadataItem( + key: e.key, + value: e.value, + )) + .toList(), + lockName: _setChannelMetadataLockNameController.text, + ); + + logSink.log( + '[RemoveChannelMetadataResult] channelName: ${result?.channelName}, channelType: ${result?.channelType}, errorCode: ${status.errorCode}'); + }), + _button('RtmStorage.getChannelMetadata', () async { + final storage = _rtmClient.getStorage(); + final (status, result) = await storage.getChannelMetadata( + _channelNameController.text, _rtmChannelType); + + logSink.log( + '[GetChannelMetadataResult] channelName: ${result?.channelName}, channelType: ${result?.channelType}, data: ${result?.data.toJson()} errorCode: ${status.errorCode}'); + }), + ], + ), + _card( + [ + Wrap( + children: [ + _switch('includeState', _isWhoNowIncludeState, (v) { + setState(() { + _isWhoNowIncludeState = v; + }); + }), + _switch('includeUserId', _isWhoNowIncludeUserId, (v) { + setState(() { + _isWhoNowIncludeUserId = v; + }); + }), + ], + ), + _textField(_isWhoNowPageController, 'Input page'), + _button('RtmPresence.whoNow', () async { + final presence = _rtmClient.getPresence(); + final (status, result) = await presence.whoNow( + _channelNameController.text, + _rtmChannelType, + includeState: _isWhoNowIncludeState, + includeUserId: _isWhoNowIncludeUserId, + page: _isWhoNowPageController.text, + ); + + logSink.log( + '[WhoNowResult] userStateList: [${(result?.userStateList ?? []).map((e) => e.toJson())}], nextPage: ${result?.nextPage}, errorCode: ${status.errorCode}'); + }), + ], + ), + _card( + [ + _textField(_rtmClientMessageController, 'Input message'), + _textField( + _rtmClientPublishCustomTypeController, 'Input customType'), + _button('RtmClient.publish', () async { + final (status, _) = await _rtmClient.publish( + _channelNameController.text, + _rtmClientMessageController.text, + channelType: _rtmChannelType, + customType: _rtmClientPublishCustomTypeController.text); + + logSink.log('[PublishResult] errorCode: ${status.errorCode}'); + }), + _button('RtmClient.publishBinaryMessage', () async { + final (status, _) = await _rtmClient.publishBinaryMessage( + _channelNameController.text, + Uint8List.fromList( + utf8.encode(_rtmClientMessageController.text)), + channelType: _rtmChannelType, + customType: _rtmClientPublishCustomTypeController.text); + + logSink.log('[PublishResult] errorCode: ${status.errorCode}'); + }), + ], + ), + ], + ), + ), + ); + } +} + +void _logCall(String func, Future Function() call) async { + void log_(String log) { + debugPrint(log); + logSink.log(log); + } + + log_(func); + try { + await call(); + } catch (e) { + final log = '[$func] error:\n $e'; + log_(log); + } +} + +class KeyValueInputGroupWidgetPair { + KeyValueInputGroupWidgetPair(this.key, this.value); + final String key; + final String value; +} + +class KeyValueInputGroupWidgetController { + final List<_Holder> _holders = []; + + List get keyValuePairs => + UnmodifiableListView(_holders.map((e) => KeyValueInputGroupWidgetPair( + e.keyController.text, e.valueController.text))); + + void dispose() { + for (final holder in _holders) { + holder.keyController.dispose(); + holder.valueController.dispose(); + } + + _holders.clear(); + } +} + +class KeyValueInputGroupWidget extends StatefulWidget { + const KeyValueInputGroupWidget({super.key, required this.controller}); + + final KeyValueInputGroupWidgetController controller; + + @override + State createState() => + _KeyValueInputGroupWidgetState(); +} + +class _KeyValueInputGroupWidgetState extends State { + Widget _keyValuePairInputs(_Holder holder) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + onPressed: () { + setState(() { + widget.controller._holders.removeWhere((e) => e.id == holder.id); + }); + }, + icon: const Icon(Icons.remove_circle_outline), + ), + Expanded( + child: TextField( + controller: holder.keyController, + decoration: const InputDecoration(hintText: 'Input key'), + ), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: TextField( + controller: holder.valueController, + decoration: const InputDecoration(hintText: 'Input value'), + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ...(widget.controller._holders.map((e) { + return _keyValuePairInputs(e); + }).toList()), + ElevatedButton( + onPressed: () { + setState(() { + widget.controller._holders.add(_Holder( + widget.controller._holders.length + 1, + TextEditingController(), + TextEditingController())); + }); + }, + child: const Text('Add key/value')), + ], + ); + } +} + +class _Holder { + _Holder(this.id, this.keyController, this.valueController); + final int id; + final TextEditingController keyController; + final TextEditingController valueController; +} diff --git a/example/lib/src/rtm_chat.dart b/example/lib/src/rtm_chat.dart new file mode 100644 index 0000000..4bdbe67 --- /dev/null +++ b/example/lib/src/rtm_chat.dart @@ -0,0 +1,1014 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm_example/src/example_actions_widget.dart'; +import 'package:agora_rtm_example/src/log_sink.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:agora_rtm_example/src/internal/internal_config.dart' as config; + +void _logCall(String func, Future Function() call) async { + void log_(String log) { + debugPrint(log); + logSink.log(log); + } + + log_(func); + try { + await call(); + } catch (e) { + final log = '[$func] error:\n $e'; + log_(log); + } +} + +class StreamChannelInfo { + StreamChannelInfo( + this.channelName, + this.streamChannel, + this.joined, + this.joinedTopic, + this.subscribeTopic, + this.userList, + this.receivedMessages, + this.subscribedUserList, + ); + + final String channelName; + final StreamChannel streamChannel; + final bool joined; + final Set joinedTopic; + final Set subscribeTopic; + final Set userList; + final List receivedMessages; + final UserList subscribedUserList; + + StreamChannelInfo copyWith({ + String? channelName, + StreamChannel? streamChannel, + bool? joined, + Set? joinedTopic, + Set? subscribeTopic, + Set? userList, + List? receivedMessages, + UserList? subscribedUserList, + }) { + return StreamChannelInfo( + channelName ?? this.channelName, + streamChannel ?? this.streamChannel, + joined ?? this.joined, + joinedTopic ?? this.joinedTopic, + subscribeTopic ?? this.subscribeTopic, + userList ?? this.userList, + receivedMessages ?? this.receivedMessages, + subscribedUserList ?? this.subscribedUserList, + ); + } +} + +class StreamChannelInfoMap + extends StateNotifier> { + StreamChannelInfoMap() : super({}); + + StreamChannelInfo getStreamChannelInfo(String channelName) { + return state[channelName]!; + } + + void put(String channelName, StreamChannel streamChannel) { + final pre = state; + + state = { + ...pre, + channelName: StreamChannelInfo( + channelName, streamChannel, false, {}, {}, {}, [], const UserList()), + }; + } + + void remove(String channelName) { + final pre = state; + pre.remove(channelName); + state = Map.from(pre); + } + + void updateStreamChannelInfo( + String channelName, { + StreamChannel? streamChannel, + bool? joined, + Set? joinedTopic, + Set? subscribeTopic, + Set? userList, + List? receivedMessages, + UserList? subscribedUserList, + }) { + final pre = state; + final preInfo = state[channelName]!; + pre[channelName] = preInfo.copyWith( + joined: joined, + joinedTopic: joinedTopic, + subscribeTopic: subscribeTopic, + userList: userList, + receivedMessages: receivedMessages, + subscribedUserList: subscribedUserList, + ); + + state = Map.from(pre); + } +} + +class RtmChatModel { + RtmChatModel(this.ref); + + final WidgetRef ref; + + final streamChannelInfoListProvider = StateNotifierProvider< + StreamChannelInfoMap, Map>((ref) { + return StreamChannelInfoMap(); + }); + + final isRtmClientInit = StateProvider((_) => false); + void setRtmClientInit(bool isInit) { + ref.read(isRtmClientInit.notifier).state = true; + } + + StreamChannelInfo getStreamChannelInfo(String channelName) { + return ref + .read(streamChannelInfoListProvider.notifier) + .getStreamChannelInfo(channelName); + } + + void put(String channelName, StreamChannel streamChannel) { + ref + .read(streamChannelInfoListProvider.notifier) + .put(channelName, streamChannel); + } + + void remove(String channelName) { + ref.read(streamChannelInfoListProvider.notifier).remove(channelName); + } + + void updateStreamChannelInfo( + String channelName, { + StreamChannel? streamChannel, + bool? joined, + Set? joinedTopic, + Set? subscribeTopic, + Set? userList, + List? receivedMessages, + UserList? subscribedUserList, + }) { + ref.read(streamChannelInfoListProvider.notifier).updateStreamChannelInfo( + channelName, + streamChannel: streamChannel, + joined: joined, + joinedTopic: joinedTopic, + subscribeTopic: subscribeTopic, + userList: userList, + receivedMessages: receivedMessages, + subscribedUserList: subscribedUserList, + ); + } +} + +/// RtmChat Example +class RtmChat extends StatelessWidget { + const RtmChat({Key? key}) : super(key: key); + + Route _onGenerateRoute(RouteSettings settings) { + late Widget page; + switch (settings.name) { + case '/': + page = const RtmChatConsumerWidget(); + break; + case 'StreamChannelPage': + final arguments = settings.arguments as Map; + final channelName = arguments['channelName']; + final rtmChatModel = arguments['rtmChatModel']; + final token = arguments['token']; + page = StreamChannelPage( + channelName: channelName, rtmChatModel: rtmChatModel, token: token); + break; + } + + return MaterialPageRoute( + builder: (context) { + return page; + }, + settings: settings, + ); + } + + @override + Widget build(BuildContext context) { + return ProviderScope(child: Builder(builder: (cc) { + return Navigator( + initialRoute: '/', + onGenerateRoute: _onGenerateRoute, + ); + })); + } +} + +class RtmChatConsumerWidget extends ConsumerStatefulWidget { + /// Construct the [RtmChat] + const RtmChatConsumerWidget({Key? key}) : super(key: key); + + @override + ConsumerState createState() => + _RtmChatConsumerWidgetState(); +} + +class _RtmChatConsumerWidgetState extends ConsumerState { + late final RtmClient _rtmClient; + + bool isJoined = false, switchCamera = true, switchRender = true; + Set remoteUid = {}; + late TextEditingController _userIdController; + late TextEditingController _tokenController; + late TextEditingController _rtmChannelNameController; + + final Map _streamChannel = {}; + + late final RtmChatModel _rtmChatModel; + + @override + void initState() { + super.initState(); + + _userIdController = TextEditingController(); + + _tokenController = TextEditingController(text: config.token); + _rtmChannelNameController = TextEditingController(); + + _rtmChatModel = RtmChatModel(ref); + + // _init(); + } + + @override + void dispose() { + _dispose(); + super.dispose(); + } + + Future _dispose() async { + _userIdController.dispose(); + _tokenController.dispose(); + _rtmChannelNameController.dispose(); + + for (final sc in _streamChannel.values) { + await sc.release(); + } + await _rtmClient.release(); + } + + Future _initRtmClient() async { + _logCall('createAgoraRtmClient', () async { + final (status, client) = await RTM(config.appId, _userIdController.text); + if (status.error) { + return; + } + _rtmClient = client; + + _rtmClient.addListener( + message: (event) { + logSink.log('[message] event: ${event.toJson()}'); + + final info = _rtmChatModel.getStreamChannelInfo(event.channelName!); + final receivedMessages = info.receivedMessages; + receivedMessages.add(event); + + _rtmChatModel.updateStreamChannelInfo(event.channelName!, + receivedMessages: receivedMessages); + }, + presence: (event) { + logSink.log('[presence] event: ${event.toJson()}'); + }, + ); + await _rtmClient.setParameters('{"rtm.log_filter":2063}'); + + _rtmChatModel.setRtmClientInit(true); + }); + } + + @override + Widget build(BuildContext context) { + return ExampleActionsWidget( + displayContentBuilder: (context, isLayoutHorizontal) { + return Consumer( + builder: (context, ref, child) { + final streamChannels = + ref.watch(_rtmChatModel.streamChannelInfoListProvider); + + return ListView.builder( + itemCount: streamChannels.length, + itemBuilder: (context, index) { + final streamChannel = streamChannels.values.elementAt(index); + return GestureDetector( + child: SizedBox( + height: 50, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded(child: Text(streamChannel.channelName)), + IconButton( + onPressed: () async { + _logCall('StreamChannel.release', () async { + await streamChannel.streamChannel.release(); + + _streamChannel.remove(streamChannel.channelName); + + _rtmChatModel.remove(streamChannel.channelName); + }); + }, + icon: const Icon(Icons.close), + ), + ], + ), + ), + onTap: () { + Navigator.push( + context, + PageRouteBuilder(pageBuilder: + (context, animation, secondaryAnimation) { + return Scaffold( + body: StreamChannelPage( + channelName: streamChannel.channelName, + rtmChatModel: _rtmChatModel, + token: _tokenController.text, + ), + ); + }, transitionsBuilder: (BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child) { + return SlideTransition( + position: Tween( + begin: const Offset(0.0, 1.0), + end: Offset.zero, + ) + .chain(CurveTween(curve: Curves.ease)) + .animate(animation), + child: child, + ); + }), + ); + }, + ); + }, + ); + }, + ); + }, + actionsBuilder: (context, isLayoutHorizontal) { + return Consumer( + builder: (context, ref, child) { + final isInit = ref.watch(_rtmChatModel.isRtmClientInit); + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: _userIdController, + decoration: const InputDecoration(hintText: 'Input user id'), + ), + const SizedBox( + height: 20, + ), + Row( + children: [ + Expanded( + flex: 1, + child: ElevatedButton( + onPressed: isInit + ? null + : () { + _initRtmClient(); + }, + child: const Text('Initialize RtmClient'), + ), + ) + ], + ), + Row( + children: [ + Expanded( + flex: 1, + child: ElevatedButton( + onPressed: () { + _logCall('RtmClient.login', () async { + final (status, _) = + await _rtmClient.login(_tokenController.text); + logSink.log( + '[LoginResult] channelName: errorCode: ${status.errorCode}'); + }); + }, + child: const Text('RtmClient join'), + ), + ) + ], + ), + TextField( + controller: _rtmChannelNameController, + decoration: + const InputDecoration(hintText: 'Input rtm channel name'), + ), + const SizedBox( + height: 20, + ), + Row( + children: [ + Expanded( + flex: 1, + child: ElevatedButton( + onPressed: isInit + ? () async { + _logCall('RtmClient.createStreamChannel', + () async { + final channelName = + _rtmChannelNameController.text; + final (_, streamChannel) = await _rtmClient + .createStreamChannel(channelName); + + // For demostrate StreamChannel.getChannelName only + final (_, channelNameFromApi) = + await streamChannel!.getChannelName(); + + _rtmChatModel.put( + channelNameFromApi!, streamChannel); + + _streamChannel[channelNameFromApi] = + streamChannel; + }); + } + : null, + child: const Text('Create StreamChannel'), + ), + ) + ], + ), + ], + ); + }, + ); + }, + ); + } +} + +class StreamChannelPage extends StatefulWidget { + /// Construct the [StreamChannelPage] + const StreamChannelPage( + {Key? key, + required this.channelName, + required this.rtmChatModel, + required this.token}) + : super(key: key); + + final String channelName; + + final RtmChatModel rtmChatModel; + + final String token; + + @override + State createState() => _StreamChannelPageState(); +} + +class _StreamChannelPageState extends State { + late final String _channelName; + late final RtmChatModel _rtmChatModel; + late final String _token; + + bool isJoined = false, switchCamera = true, switchRender = true; + + late TextEditingController _topicController; + late TextEditingController _topicMessageController; + late TextEditingController _subscribeTopicController; + late TextEditingController _subscribeUserController; + late TextEditingController _subscribeUserTopicController; + + @override + void initState() { + super.initState(); + + _topicController = TextEditingController(); + _topicMessageController = TextEditingController(); + _subscribeTopicController = TextEditingController(); + _subscribeUserController = TextEditingController(); + _subscribeUserTopicController = TextEditingController(); + + _channelName = widget.channelName; + _rtmChatModel = widget.rtmChatModel; + _token = widget.token; + } + + @override + void dispose() { + _topicController.dispose(); + _topicMessageController.dispose(); + _subscribeTopicController.dispose(); + _subscribeUserController.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Container( + color: Colors.grey[200], + padding: const EdgeInsets.only(left: 16, right: 16), + child: Consumer( + builder: (context, ref, child) { + final streamChannelInfos = + ref.watch(_rtmChatModel.streamChannelInfoListProvider); + + final streamChannelInfo = streamChannelInfos[_channelName]!; + + return Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Channel Name: ${streamChannelInfo.channelName}'), + Expanded( + child: ListView.builder( + itemBuilder: (context, index) { + final message = + streamChannelInfo.receivedMessages[index]; + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('channelType: ${message.channelType}'), + Text('channelName: ${message.channelName}'), + Text('channelTopic: ${message.channelTopic}'), + Text( + 'message: ${utf8.decode(message.message ?? Uint8List(0))}'), + Text( + 'messageLength: ${message.messageLength}'), + Text('publisher: ${message.publisher}'), + Container( + color: Colors.black12, + height: 1, + ), + ]); + }, + itemCount: streamChannelInfo.receivedMessages.length, + ), + ), + ], + ); + }, + ), + )), + Expanded(child: SingleChildScrollView( + child: Consumer( + builder: (context, ref, child) { + final streamChannelInfos = + ref.watch(_rtmChatModel.streamChannelInfoListProvider); + + final streamChannelInfo = streamChannelInfos[_channelName]; + + final streamChannel = streamChannelInfo!.streamChannel; + + return Container( + margin: const EdgeInsets.only(left: 16, right: 16), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 20, + ), + ElevatedButton( + onPressed: () async { + if (streamChannelInfo.joined) { + _logCall('StreamChannel.leave', () async { + await streamChannel.leave(); + _rtmChatModel.updateStreamChannelInfo( + _channelName, + joined: false); + }); + } else { + _logCall('StreamChannel.join', () async { + final (status, result) = + await streamChannel.join(token: _token); + logSink.log( + '[StreamChannel.join result] channelName: ${result!.channelName}, userId: ${result.userId}, errorCode: ${status.errorCode}'); + + _rtmChatModel.updateStreamChannelInfo( + _channelName, + joined: true); + }); + } + }, + child: Text( + '${streamChannelInfo.joined ? 'Leave' : 'Join'} rtm channel'), + ), + const SizedBox( + height: 20, + ), + const Text( + 'Topic Name', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: TextField( + controller: _topicController, + decoration: const InputDecoration( + hintText: 'Input topic name'), + ), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () { + _logCall('StreamChannel.joinTopic', + () async { + final ( + status, + result + ) = await streamChannel + .joinTopic(_topicController.text); + logSink.log( + '[JoinTopicResult] channelName: ${result!.channelName}, userId: ${result.userId}, topic: ${result.topic}, meta: ${result.meta}, errorCode: ${status.errorCode}'); + + final pre = + _rtmChatModel.getStreamChannelInfo( + result.channelName); + final joinedTopic = { + ...pre.joinedTopic, + result.topic + }; + + _rtmChatModel.updateStreamChannelInfo( + result.channelName, + joinedTopic: joinedTopic); + }); + } + : null, + child: const Text('Join'), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () { + _logCall('StreamChannel.leaveTopic', + () async { + final ( + status, + result + ) = await streamChannel + .leaveTopic(_topicController.text); + logSink.log( + '[LeaveTopicResult] channelName: ${result!.channelName}, userId: ${result.userId}, errorCode: ${status.errorCode}'); + + _rtmChatModel.updateStreamChannelInfo( + result.channelName, + joined: false); + }); + } + : null, + child: const Text('Leave'), + ), + ], + ), + const SizedBox( + height: 20, + ), + const Text( + 'User List', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: TextField( + controller: _subscribeUserController, + decoration: const InputDecoration( + hintText: 'Input user name'), + ), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () { + if (_subscribeUserController + .text.isEmpty) { + logSink.log( + 'Subscribe user name can not be empty'); + return; + } + final userList = + streamChannelInfo.userList; + userList + .add(_subscribeUserController.text); + + ref + .read(_rtmChatModel + .streamChannelInfoListProvider + .notifier) + .updateStreamChannelInfo( + streamChannelInfo.channelName, + userList: userList); + } + : null, + child: const Text('Add'), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () { + if (_subscribeUserController + .text.isEmpty) { + logSink.log( + 'Subscribe user name can not be empty'); + return; + } + + final userList = + streamChannelInfo.userList; + userList.removeWhere((element) => + element == + _subscribeUserController.text); + + ref + .read(_rtmChatModel + .streamChannelInfoListProvider + .notifier) + .updateStreamChannelInfo( + streamChannelInfo.channelName, + userList: userList); + } + : null, + child: const Text('Remove'), + ), + ], + ), + const SizedBox( + height: 10, + ), + Wrap( + spacing: 10, + children: [ + for (final u in streamChannelInfo.userList) + Container( + decoration: BoxDecoration( + color: Colors.green[200]!, + border: Border.all( + color: Colors.green[200]!, + ), + borderRadius: const BorderRadius.all( + Radius.circular(20))), + padding: const EdgeInsets.all(8), + child: Text(u), + ), + ], + ), + const SizedBox( + height: 20, + ), + const Text( + 'Subscribe Topic', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Row( + children: [ + Expanded( + child: TextField( + controller: _subscribeTopicController, + decoration: const InputDecoration( + hintText: 'Intput subscribe topic name'), + ), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () { + _logCall( + 'StreamChannel.subscribeTopic', + () async { + final (status, result) = + await streamChannel + .subscribeTopic( + _subscribeTopicController.text, + users: streamChannelInfo.userList + .toList(), + ); + logSink.log( + '[SubscribeTopicResult] channelName: ${result!.channelName}, userId: ${result.userId}, topic: ${result.topic}, succeedUsers: ${result.succeedUsers}, failedUsers: ${result.failedUsers}, errorCode: ${status.errorCode}'); + + final pre = _rtmChatModel + .getStreamChannelInfo( + result.channelName); + final subscribeTopic = { + ...pre.subscribeTopic, + result.topic + }; + + _rtmChatModel.updateStreamChannelInfo( + result.channelName, + subscribeTopic: subscribeTopic); + }, + ); + } + : null, + child: const Text('Subscribe'), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () { + _logCall( + 'StreamChannel.unsubscribeTopic', + () async { + final (status, result) = + await streamChannel + .unsubscribeTopic( + _subscribeTopicController + .text, + users: streamChannelInfo + .userList + .toList()); + logSink.log( + '[UnsubscribeTopicResult] channelName: ${result!.channelName}, topic: ${result.topic}, errorCode: ${status.errorCode}'); + + final pre = _rtmChatModel + .getStreamChannelInfo( + result.channelName); + final joinedTopic = pre.joinedTopic; + joinedTopic.removeWhere((element) => + element == result.topic); + + _rtmChatModel.updateStreamChannelInfo( + result.channelName, + joinedTopic: joinedTopic); + }, + ); + } + : null, + child: const Text('Unsubscribe'), + ), + ], + ), + const SizedBox(height: 10), + const Text( + 'Subscribed User List', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: TextField( + controller: _subscribeUserTopicController, + decoration: const InputDecoration( + hintText: + 'Input subscribed user list topic name'), + ), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () async { + _logCall( + 'StreamChannel.getSubscribedUserList', + () async { + final ( + _, + result + ) = await streamChannelInfo + .streamChannel + .getSubscribedUserList( + _subscribeUserTopicController + .text); + + ref + .read(_rtmChatModel + .streamChannelInfoListProvider + .notifier) + .updateStreamChannelInfo( + streamChannelInfo.channelName, + subscribedUserList: + result!.users); + }); + } + : null, + child: const Text('getSubscribedUserList'), + ), + ], + ), + const SizedBox( + height: 10, + ), + Wrap( + spacing: 10, + children: [ + for (final u in (streamChannelInfo + .subscribedUserList.users ?? + [])) + Container( + decoration: BoxDecoration( + color: Colors.green[200]!, + border: Border.all( + color: Colors.green[200]!, + ), + borderRadius: const BorderRadius.all( + Radius.circular(20))), + padding: const EdgeInsets.all(8), + child: Text(u), + ), + ], + ), + const SizedBox( + height: 20, + ), + const Text( + 'Topic message', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Row( + children: [ + Expanded( + flex: 1, + child: TextField( + controller: _topicMessageController, + decoration: const InputDecoration( + hintText: 'Input topic message'), + ), + ), + ElevatedButton( + onPressed: streamChannelInfo.joined + ? () { + _logCall( + 'StreamChannel.publishTopicMessage', + () async { + final message = + _topicMessageController.text; + + final (status, result) = + await streamChannel + .publishTextMessage( + _topicController.text, + message, + ); + + logSink.log( + '[PublishTopicMessageResult] channelName: ${result!.channelName}, topic: ${result.topic}, errorCode: ${status.errorCode}'); + }, + ); + } + : null, + child: const Text('Send'), + ), + ], + ), + const SizedBox( + height: 20, + ), + ], + ), + ); + }, + ), + )), + ], + ), + Align( + alignment: Alignment.topRight, + child: IconButton( + icon: const Icon(Icons.close), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ], + ); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..c1cf8d3 --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,84 @@ +name: agora_rtm_example +description: "Demonstrates how to use the agora_rtm plugin." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.10.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + agora_rtm: + # When depending on this package from a real application you should use: + # agora_rtm: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + flutter_riverpod: ^1.0.0 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_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: '>=1.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/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000..2128747 --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// 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:agora_rtm_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..034771f --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ios/Classes/AgoraRtmPlugin.h b/ios/Classes/AgoraRtmPlugin.h new file mode 100644 index 0000000..ea5cb3d --- /dev/null +++ b/ios/Classes/AgoraRtmPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface AgoraRtmPlugin : NSObject +@end diff --git a/ios/Classes/AgoraRtmPlugin.m b/ios/Classes/AgoraRtmPlugin.m new file mode 100644 index 0000000..458a354 --- /dev/null +++ b/ios/Classes/AgoraRtmPlugin.m @@ -0,0 +1,20 @@ +#import "AgoraRtmPlugin.h" + +@implementation AgoraRtmPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"agora_rtm" + binaryMessenger:[registrar messenger]]; + AgoraRtmPlugin* instance = [[AgoraRtmPlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + if ([@"getPlatformVersion" isEqualToString:call.method]) { + result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); + } else { + result(FlutterMethodNotImplemented); + } +} + +@end diff --git a/ios/agora_rtm.podspec b/ios/agora_rtm.podspec new file mode 100644 index 0000000..b714d99 --- /dev/null +++ b/ios/agora_rtm.podspec @@ -0,0 +1,34 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint agora_rtm.podspec` to validate before publishing. +# +require "yaml" +require "ostruct" +project = OpenStruct.new YAML.load_file("../pubspec.yaml") +Pod::Spec.new do |s| + s.name = project.name + s.version = project.version + s.summary = 'A new Flutter plugin project.' + s.description = project.description + s.homepage = project.homepage + s.license = { :file => '../LICENSE' } + s.author = { 'Agora' => 'developer@agora.io' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + s.platform = :ios, '9.0' + s.libraries = 'stdc++' + + plugin_dev_path = File.join(File.dirname(File.realpath(__FILE__)), '..', '.plugin_dev') + if File.exist?(plugin_dev_path) + puts '[plugin_dev] Found .plugin_dev file, use vendored_frameworks instead.' + s.vendored_frameworks = 'libs/*.xcframework' + else + s.dependency 'AgoraIrisRTM_iOS', '2.2.1-build.1' + s.dependency 'AgoraRtm', '2.2.1' + end + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } +end diff --git a/lib/agora_rtm.dart b/lib/agora_rtm.dart new file mode 100644 index 0000000..57fa3c0 --- /dev/null +++ b/lib/agora_rtm.dart @@ -0,0 +1,8 @@ +export 'src/agora_rtm_base.dart'; +export 'src/agora_rtm_client.dart'; +export 'src/agora_rtm_lock.dart'; +export 'src/agora_rtm_presence.dart'; +export 'src/agora_rtm_storage.dart'; +export 'src/agora_stream_channel.dart'; + +export 'src/agora_rtm_client_ext.dart'; diff --git a/lib/src/agora_rtm_base.dart b/lib/src/agora_rtm_base.dart new file mode 100644 index 0000000..51bd9c4 --- /dev/null +++ b/lib/src/agora_rtm_base.dart @@ -0,0 +1,1211 @@ +import 'binding_forward_export.dart'; +part 'agora_rtm_base.g.dart'; + +/// @nodoc +const defaultLogSizeInKb = 1024; + +@JsonEnum(alwaysCreate: true) +enum RtmLinkState { + @JsonValue(0) + idle, + + @JsonValue(1) + connecting, + + @JsonValue(2) + connected, + + @JsonValue(3) + disconnected, + + @JsonValue(4) + suspended, + + @JsonValue(5) + failed, +} + +extension RtmLinkStateExt on RtmLinkState { + /// @nodoc + static RtmLinkState fromValue(int value) { + return $enumDecode(_$RtmLinkStateEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmLinkStateEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmLinkOperation { + @JsonValue(0) + login, + + @JsonValue(1) + logout, + + @JsonValue(2) + join, + + @JsonValue(3) + leave, + + @JsonValue(4) + serverReject, + + @JsonValue(5) + autoReconnect, + + @JsonValue(6) + reconnected, + + @JsonValue(7) + heartbeatLost, + + @JsonValue(8) + serverTimeout, + + @JsonValue(9) + networkChange, +} + +extension RtmLinkOperationExt on RtmLinkOperation { + /// @nodoc + static RtmLinkOperation fromValue(int value) { + return $enumDecode(_$RtmLinkOperationEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmLinkOperationEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmServiceType { + @JsonValue(0x00000000) + none, + + @JsonValue(0x00000001) + message, + + @JsonValue(0x00000002) + stream, +} + +extension RtmServiceTypeExt on RtmServiceType { + /// @nodoc + static RtmServiceType fromValue(int value) { + return $enumDecode(_$RtmServiceTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmServiceTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmProtocolType { + @JsonValue(0) + tcpUdp, + + @JsonValue(1) + tcpOnly, +} + +extension RtmProtocolTypeExt on RtmProtocolType { + /// @nodoc + static RtmProtocolType fromValue(int value) { + return $enumDecode(_$RtmProtocolTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmProtocolTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmAreaCode { + @JsonValue(0x00000001) + cn, + + @JsonValue(0x00000002) + na, + + @JsonValue(0x00000004) + eu, + + @JsonValue(0x00000008) + asm, + + @JsonValue(0x00000010) + jp, + + @JsonValue(0x00000020) + ind, + + @JsonValue((0xFFFFFFFF)) + glob, +} + +extension RtmAreaCodeExt on RtmAreaCode { + /// @nodoc + static RtmAreaCode fromValue(int value) { + return $enumDecode(_$RtmAreaCodeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmAreaCodeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmLogLevel { + @JsonValue(0x0000) + none, + + @JsonValue(0x0001) + info, + + @JsonValue(0x0002) + warn, + + @JsonValue(0x0004) + error, + + @JsonValue(0x0008) + fatal, +} + +extension RtmLogLevelExt on RtmLogLevel { + /// @nodoc + static RtmLogLevel fromValue(int value) { + return $enumDecode(_$RtmLogLevelEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmLogLevelEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmEncryptionMode { + @JsonValue(0) + none, + + @JsonValue(1) + aes128Gcm, + + @JsonValue(2) + aes256Gcm, +} + +extension RtmEncryptionModeExt on RtmEncryptionMode { + /// @nodoc + static RtmEncryptionMode fromValue(int value) { + return $enumDecode(_$RtmEncryptionModeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmEncryptionModeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmErrorCode { + @JsonValue(0) + ok, + + @JsonValue(-10001) + notInitialized, + + @JsonValue(-10002) + notLogin, + + @JsonValue(-10003) + invalidAppId, + + @JsonValue(-10004) + invalidEventHandler, + + @JsonValue(-10005) + invalidToken, + + @JsonValue(-10006) + invalidUserId, + + @JsonValue(-10007) + initServiceFailed, + + @JsonValue(-10008) + invalidChannelName, + + @JsonValue(-10009) + tokenExpired, + + @JsonValue(-10010) + loginNoServerResources, + + @JsonValue(-10011) + loginTimeout, + + @JsonValue(-10012) + loginRejected, + + @JsonValue(-10013) + loginAborted, + + @JsonValue(-10014) + invalidParameter, + + @JsonValue(-10015) + loginNotAuthorized, + + @JsonValue(-10016) + inconsistentAppid, + + @JsonValue(-10017) + duplicateOperation, + + @JsonValue(-10018) + instanceAlreadyReleased, + + @JsonValue(-10019) + invalidChannelType, + + @JsonValue(-10020) + invalidEncryptionParameter, + + @JsonValue(-10021) + operationRateExceedLimitation, + + @JsonValue(-10022) + serviceNotSupported, + + @JsonValue(-10023) + loginCanceled, + + @JsonValue(-10024) + invalidPrivateConfig, + + @JsonValue(-10025) + notConnected, + + @JsonValue(-11001) + channelNotJoined, + + @JsonValue(-11002) + channelNotSubscribed, + + @JsonValue(-11003) + channelExceedTopicUserLimitation, + + @JsonValue(-11004) + channelInReuse, + + @JsonValue(-11005) + channelInstanceExceedLimitation, + + @JsonValue(-11006) + channelInErrorState, + + @JsonValue(-11007) + channelJoinFailed, + + @JsonValue(-11008) + channelInvalidTopicName, + + @JsonValue(-11009) + channelInvalidMessage, + + @JsonValue(-11010) + channelMessageLengthExceedLimitation, + + @JsonValue(-11011) + channelInvalidUserList, + + @JsonValue(-11012) + channelNotAvailable, + + @JsonValue(-11013) + channelTopicNotSubscribed, + + @JsonValue(-11014) + channelExceedTopicLimitation, + + @JsonValue(-11015) + channelJoinTopicFailed, + + @JsonValue(-11016) + channelTopicNotJoined, + + @JsonValue(-11017) + channelTopicNotExist, + + @JsonValue(-11018) + channelInvalidTopicMeta, + + @JsonValue(-11019) + channelSubscribeTimeout, + + @JsonValue(-11020) + channelSubscribeTooFrequent, + + @JsonValue(-11021) + channelSubscribeFailed, + + @JsonValue(-11022) + channelUnsubscribeFailed, + + @JsonValue(-11023) + channelEncryptMessageFailed, + + @JsonValue(-11024) + channelPublishMessageFailed, + + @JsonValue(-11025) + channelPublishMessageTooFrequent, + + @JsonValue(-11026) + channelPublishMessageTimeout, + + @JsonValue(-11027) + channelNotConnected, + + @JsonValue(-11028) + channelLeaveFailed, + + @JsonValue(-11029) + channelCustomTypeLengthOverflow, + + @JsonValue(-11030) + channelInvalidCustomType, + + @JsonValue(-11031) + channelUnsupportedMessageType, + + @JsonValue(-11032) + channelPresenceNotReady, + + @JsonValue(-11033) + channelReceiverOffline, + + @JsonValue(-11034) + channelJoinCanceled, + + @JsonValue(-12001) + storageOperationFailed, + + @JsonValue(-12002) + storageMetadataItemExceedLimitation, + + @JsonValue(-12003) + storageInvalidMetadataItem, + + @JsonValue(-12004) + storageInvalidArgument, + + @JsonValue(-12005) + storageInvalidRevision, + + @JsonValue(-12006) + storageMetadataLengthOverflow, + + @JsonValue(-12007) + storageInvalidLockName, + + @JsonValue(-12008) + storageLockNotAcquired, + + @JsonValue(-12009) + storageInvalidKey, + + @JsonValue(-12010) + storageInvalidValue, + + @JsonValue(-12011) + storageKeyLengthOverflow, + + @JsonValue(-12012) + storageValueLengthOverflow, + + @JsonValue(-12013) + storageDuplicateKey, + + @JsonValue(-12014) + storageOutdatedRevision, + + @JsonValue(-12015) + storageNotSubscribe, + + @JsonValue(-12016) + storageInvalidMetadataInstance, + + @JsonValue(-12017) + storageSubscribeUserExceedLimitation, + + @JsonValue(-12018) + storageOperationTimeout, + + @JsonValue(-12019) + storageNotAvailable, + + @JsonValue(-13001) + presenceNotConnected, + + @JsonValue(-13002) + presenceNotWritable, + + @JsonValue(-13003) + presenceInvalidArgument, + + @JsonValue(-13004) + presenceCachedTooManyStates, + + @JsonValue(-13005) + presenceStateCountOverflow, + + @JsonValue(-13006) + presenceInvalidStateKey, + + @JsonValue(-13007) + presenceInvalidStateValue, + + @JsonValue(-13008) + presenceStateKeySizeOverflow, + + @JsonValue(-13009) + presenceStateValueSizeOverflow, + + @JsonValue(-13010) + presenceStateDuplicateKey, + + @JsonValue(-13011) + presenceUserNotExist, + + @JsonValue(-13012) + presenceOperationTimeout, + + @JsonValue(-13013) + presenceOperationFailed, + + @JsonValue(-14001) + lockOperationFailed, + + @JsonValue(-14002) + lockOperationTimeout, + + @JsonValue(-14003) + lockOperationPerforming, + + @JsonValue(-14004) + lockAlreadyExist, + + @JsonValue(-14005) + lockInvalidName, + + @JsonValue(-14006) + lockNotAcquired, + + @JsonValue(-14007) + lockAcquireFailed, + + @JsonValue(-14008) + lockNotExist, + + @JsonValue(-14009) + lockNotAvailable, +} + +extension RtmErrorCodeExt on RtmErrorCode { + /// @nodoc + static RtmErrorCode fromValue(int value) { + return $enumDecode(_$RtmErrorCodeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmErrorCodeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmConnectionState { + @JsonValue(1) + disconnected, + + @JsonValue(2) + connecting, + + @JsonValue(3) + connected, + + @JsonValue(4) + reconnecting, + + @JsonValue(5) + failed, +} + +extension RtmConnectionStateExt on RtmConnectionState { + /// @nodoc + static RtmConnectionState fromValue(int value) { + return $enumDecode(_$RtmConnectionStateEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmConnectionStateEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmConnectionChangeReason { + @JsonValue(0) + connecting, + + @JsonValue(1) + joinSuccess, + + @JsonValue(2) + interrupted, + + @JsonValue(3) + bannedByServer, + + @JsonValue(4) + joinFailed, + + @JsonValue(5) + leaveChannel, + + @JsonValue(6) + invalidAppId, + + @JsonValue(7) + invalidChannelName, + + @JsonValue(8) + invalidToken, + + @JsonValue(9) + tokenExpired, + + @JsonValue(10) + rejectedByServer, + + @JsonValue(11) + settingProxyServer, + + @JsonValue(12) + renewToken, + + @JsonValue(13) + clientIpAddressChanged, + + @JsonValue(14) + keepAliveTimeout, + + @JsonValue(15) + rejoinSuccess, + + @JsonValue(16) + lost, + + @JsonValue(17) + echoTest, + + @JsonValue(18) + clientIpAddressChangedByUser, + + @JsonValue(19) + sameUidLogin, + + @JsonValue(20) + tooManyBroadcasters, + + @JsonValue(21) + licenseValidationFailure, + + @JsonValue(22) + certificationVerifyFailure, + + @JsonValue(23) + streamChannelNotAvailable, + + @JsonValue(24) + inconsistentAppid, + + @JsonValue(10001) + loginSuccess, + + @JsonValue(10002) + logout, + + @JsonValue(10003) + presenceNotReady, +} + +extension RtmConnectionChangeReasonExt on RtmConnectionChangeReason { + /// @nodoc + static RtmConnectionChangeReason fromValue(int value) { + return $enumDecode(_$RtmConnectionChangeReasonEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmConnectionChangeReasonEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmChannelType { + @JsonValue(0) + none, + + @JsonValue(1) + message, + + @JsonValue(2) + stream, + + @JsonValue(3) + user, +} + +extension RtmChannelTypeExt on RtmChannelType { + /// @nodoc + static RtmChannelType fromValue(int value) { + return $enumDecode(_$RtmChannelTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmChannelTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmMessageType { + @JsonValue(0) + binary, + + @JsonValue(1) + string, +} + +extension RtmMessageTypeExt on RtmMessageType { + /// @nodoc + static RtmMessageType fromValue(int value) { + return $enumDecode(_$RtmMessageTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmMessageTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmStorageType { + @JsonValue(0) + none, + + @JsonValue(1) + user, + + @JsonValue(2) + channel, +} + +extension RtmStorageTypeExt on RtmStorageType { + /// @nodoc + static RtmStorageType fromValue(int value) { + return $enumDecode(_$RtmStorageTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmStorageTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmStorageEventType { + @JsonValue(0) + none, + + @JsonValue(1) + snapshot, + + @JsonValue(2) + set, + + @JsonValue(3) + update, + + @JsonValue(4) + remove, +} + +extension RtmStorageEventTypeExt on RtmStorageEventType { + /// @nodoc + static RtmStorageEventType fromValue(int value) { + return $enumDecode(_$RtmStorageEventTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmStorageEventTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmLockEventType { + @JsonValue(0) + none, + + @JsonValue(1) + snapshot, + + @JsonValue(2) + lockSet, + + @JsonValue(3) + lockRemoved, + + @JsonValue(4) + lockAcquired, + + @JsonValue(5) + lockReleased, + + @JsonValue(6) + lockExpired, +} + +extension RtmLockEventTypeExt on RtmLockEventType { + /// @nodoc + static RtmLockEventType fromValue(int value) { + return $enumDecode(_$RtmLockEventTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmLockEventTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmProxyType { + @JsonValue(0) + none, + + @JsonValue(1) + http, + + @JsonValue(2) + cloudTcp, +} + +extension RtmProxyTypeExt on RtmProxyType { + /// @nodoc + static RtmProxyType fromValue(int value) { + return $enumDecode(_$RtmProxyTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmProxyTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmTopicEventType { + @JsonValue(0) + none, + + @JsonValue(1) + snapshot, + + @JsonValue(2) + remoteJoinTopic, + + @JsonValue(3) + remoteLeaveTopic, +} + +extension RtmTopicEventTypeExt on RtmTopicEventType { + /// @nodoc + static RtmTopicEventType fromValue(int value) { + return $enumDecode(_$RtmTopicEventTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmTopicEventTypeEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmPresenceEventType { + @JsonValue(0) + none, + + @JsonValue(1) + snapshot, + + @JsonValue(2) + interval, + + @JsonValue(3) + remoteJoinChannel, + + @JsonValue(4) + remoteLeaveChannel, + + @JsonValue(5) + remoteTimeout, + + @JsonValue(6) + remoteStateChanged, + + @JsonValue(7) + errorOutOfService, +} + +extension RtmPresenceEventTypeExt on RtmPresenceEventType { + /// @nodoc + static RtmPresenceEventType fromValue(int value) { + return $enumDecode(_$RtmPresenceEventTypeEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmPresenceEventTypeEnumMap[this]!; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmLogConfig { + const RtmLogConfig( + {this.filePath, + this.fileSizeInKB = defaultLogSizeInKb, + this.level = RtmLogLevel.info}); + + @JsonKey(name: 'filePath') + final String? filePath; + + @JsonKey(name: 'fileSizeInKB') + final int? fileSizeInKB; + + @JsonKey(name: 'level') + final RtmLogLevel? level; + + factory RtmLogConfig.fromJson(Map json) => + _$RtmLogConfigFromJson(json); + + Map toJson() => _$RtmLogConfigToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class UserList { + const UserList({this.users}); + + @JsonKey(name: 'users') + final List? users; + + factory UserList.fromJson(Map json) => + _$UserListFromJson(json); + + Map toJson() => _$UserListToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class PublisherInfo { + const PublisherInfo({this.publisherUserId, this.publisherMeta}); + + @JsonKey(name: 'publisherUserId') + final String? publisherUserId; + + @JsonKey(name: 'publisherMeta') + final String? publisherMeta; + + factory PublisherInfo.fromJson(Map json) => + _$PublisherInfoFromJson(json); + + Map toJson() => _$PublisherInfoToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class TopicInfo { + const TopicInfo({this.topic, this.publishers}); + + @JsonKey(name: 'topic') + final String? topic; + + @JsonKey(name: 'publishers') + final List? publishers; + + factory TopicInfo.fromJson(Map json) => + _$TopicInfoFromJson(json); + + Map toJson() => _$TopicInfoToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StateItem { + const StateItem({this.key, this.value}); + + @JsonKey(name: 'key') + final String? key; + + @JsonKey(name: 'value') + final String? value; + + factory StateItem.fromJson(Map json) => + _$StateItemFromJson(json); + + Map toJson() => _$StateItemToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class LockDetail { + const LockDetail({this.lockName, this.owner, this.ttl = 0}); + + @JsonKey(name: 'lockName') + final String? lockName; + + @JsonKey(name: 'owner') + final String? owner; + + @JsonKey(name: 'ttl') + final int? ttl; + + factory LockDetail.fromJson(Map json) => + _$LockDetailFromJson(json); + + Map toJson() => _$LockDetailToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class UserState { + const UserState({this.userId, this.states}); + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'states') + final List? states; + + factory UserState.fromJson(Map json) => + _$UserStateFromJson(json); + + Map toJson() => _$UserStateToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class SubscribeOptions { + const SubscribeOptions( + {this.withMessage = true, + this.withMetadata = false, + this.withPresence = true, + this.withLock = false, + this.beQuiet = false}); + + @JsonKey(name: 'withMessage') + final bool? withMessage; + + @JsonKey(name: 'withMetadata') + final bool? withMetadata; + + @JsonKey(name: 'withPresence') + final bool? withPresence; + + @JsonKey(name: 'withLock') + final bool? withLock; + + @JsonKey(name: 'beQuiet') + final bool? beQuiet; + + factory SubscribeOptions.fromJson(Map json) => + _$SubscribeOptionsFromJson(json); + + Map toJson() => _$SubscribeOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class ChannelInfo { + const ChannelInfo({this.channelName, this.channelType}); + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + factory ChannelInfo.fromJson(Map json) => + _$ChannelInfoFromJson(json); + + Map toJson() => _$ChannelInfoToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class PresenceOptions { + const PresenceOptions( + {this.includeUserId = true, this.includeState = false, this.page = ''}); + + @JsonKey(name: 'includeUserId') + final bool? includeUserId; + + @JsonKey(name: 'includeState') + final bool? includeState; + + @JsonKey(name: 'page') + final String? page; + + factory PresenceOptions.fromJson(Map json) => + _$PresenceOptionsFromJson(json); + + Map toJson() => _$PresenceOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class PublishOptions { + const PublishOptions( + {this.channelType = RtmChannelType.message, + this.messageType = RtmMessageType.binary, + this.customType}); + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'messageType') + final RtmMessageType? messageType; + + @JsonKey(name: 'customType') + final String? customType; + + factory PublishOptions.fromJson(Map json) => + _$PublishOptionsFromJson(json); + + Map toJson() => _$PublishOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class TopicMessageOptions { + const TopicMessageOptions( + {this.messageType = RtmMessageType.binary, + this.sendTs = 0, + this.customType}); + + @JsonKey(name: 'messageType') + final RtmMessageType? messageType; + + @JsonKey(name: 'sendTs') + final int? sendTs; + + @JsonKey(name: 'customType') + final String? customType; + + factory TopicMessageOptions.fromJson(Map json) => + _$TopicMessageOptionsFromJson(json); + + Map toJson() => _$TopicMessageOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class GetOnlineUsersOptions { + const GetOnlineUsersOptions( + {this.includeUserId = true, this.includeState = false, this.page}); + + @JsonKey(name: 'includeUserId') + final bool? includeUserId; + + @JsonKey(name: 'includeState') + final bool? includeState; + + @JsonKey(name: 'page') + final String? page; + + factory GetOnlineUsersOptions.fromJson(Map json) => + _$GetOnlineUsersOptionsFromJson(json); + + Map toJson() => _$GetOnlineUsersOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmProxyConfig { + const RtmProxyConfig( + {this.proxyType = RtmProxyType.none, + this.server, + this.port = 0, + this.account, + this.password}); + + @JsonKey(name: 'proxyType') + final RtmProxyType? proxyType; + + @JsonKey(name: 'server') + final String? server; + + @JsonKey(name: 'port') + final int? port; + + @JsonKey(name: 'account') + final String? account; + + @JsonKey(name: 'password') + final String? password; + + factory RtmProxyConfig.fromJson(Map json) => + _$RtmProxyConfigFromJson(json); + + Map toJson() => _$RtmProxyConfigToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEncryptionConfig { + const RtmEncryptionConfig( + {this.encryptionMode = RtmEncryptionMode.none, + this.encryptionKey, + this.encryptionSalt}); + + @JsonKey(name: 'encryptionMode') + final RtmEncryptionMode? encryptionMode; + + @JsonKey(name: 'encryptionKey') + final String? encryptionKey; + + @JsonKey(name: 'encryptionSalt', ignore: true) + final Uint8List? encryptionSalt; + + factory RtmEncryptionConfig.fromJson(Map json) => + _$RtmEncryptionConfigFromJson(json); + + Map toJson() => _$RtmEncryptionConfigToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPrivateConfig { + const RtmPrivateConfig( + {this.serviceType = const {RtmServiceType.none}, this.accessPointHosts}); + + @RtmServiceTypeListConverter() + @JsonKey(name: 'serviceType') + final Set? serviceType; + + @JsonKey(name: 'accessPointHosts') + final List? accessPointHosts; + + factory RtmPrivateConfig.fromJson(Map json) => + _$RtmPrivateConfigFromJson(json); + + Map toJson() => _$RtmPrivateConfigToJson(this); +} diff --git a/lib/src/agora_rtm_base.g.dart b/lib/src/agora_rtm_base.g.dart new file mode 100644 index 0000000..d8f45aa --- /dev/null +++ b/lib/src/agora_rtm_base.g.dart @@ -0,0 +1,630 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'agora_rtm_base.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RtmLogConfig _$RtmLogConfigFromJson(Map json) => RtmLogConfig( + filePath: json['filePath'] as String?, + fileSizeInKB: + (json['fileSizeInKB'] as num?)?.toInt() ?? defaultLogSizeInKb, + level: $enumDecodeNullable(_$RtmLogLevelEnumMap, json['level']) ?? + RtmLogLevel.info, + ); + +Map _$RtmLogConfigToJson(RtmLogConfig instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('filePath', instance.filePath); + writeNotNull('fileSizeInKB', instance.fileSizeInKB); + writeNotNull('level', _$RtmLogLevelEnumMap[instance.level]); + return val; +} + +const _$RtmLogLevelEnumMap = { + RtmLogLevel.none: 0, + RtmLogLevel.info: 1, + RtmLogLevel.warn: 2, + RtmLogLevel.error: 4, + RtmLogLevel.fatal: 8, +}; + +UserList _$UserListFromJson(Map json) => UserList( + users: + (json['users'] as List?)?.map((e) => e as String).toList(), + ); + +Map _$UserListToJson(UserList instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('users', instance.users); + return val; +} + +PublisherInfo _$PublisherInfoFromJson(Map json) => + PublisherInfo( + publisherUserId: json['publisherUserId'] as String?, + publisherMeta: json['publisherMeta'] as String?, + ); + +Map _$PublisherInfoToJson(PublisherInfo instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('publisherUserId', instance.publisherUserId); + writeNotNull('publisherMeta', instance.publisherMeta); + return val; +} + +TopicInfo _$TopicInfoFromJson(Map json) => TopicInfo( + topic: json['topic'] as String?, + publishers: (json['publishers'] as List?) + ?.map((e) => PublisherInfo.fromJson(e as Map)) + .toList(), + ); + +Map _$TopicInfoToJson(TopicInfo instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('topic', instance.topic); + writeNotNull( + 'publishers', instance.publishers?.map((e) => e.toJson()).toList()); + return val; +} + +StateItem _$StateItemFromJson(Map json) => StateItem( + key: json['key'] as String?, + value: json['value'] as String?, + ); + +Map _$StateItemToJson(StateItem instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('key', instance.key); + writeNotNull('value', instance.value); + return val; +} + +LockDetail _$LockDetailFromJson(Map json) => LockDetail( + lockName: json['lockName'] as String?, + owner: json['owner'] as String?, + ttl: (json['ttl'] as num?)?.toInt() ?? 0, + ); + +Map _$LockDetailToJson(LockDetail instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('lockName', instance.lockName); + writeNotNull('owner', instance.owner); + writeNotNull('ttl', instance.ttl); + return val; +} + +UserState _$UserStateFromJson(Map json) => UserState( + userId: json['userId'] as String?, + states: (json['states'] as List?) + ?.map((e) => StateItem.fromJson(e as Map)) + .toList(), + ); + +Map _$UserStateToJson(UserState instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('userId', instance.userId); + writeNotNull('states', instance.states?.map((e) => e.toJson()).toList()); + return val; +} + +SubscribeOptions _$SubscribeOptionsFromJson(Map json) => + SubscribeOptions( + withMessage: json['withMessage'] as bool? ?? true, + withMetadata: json['withMetadata'] as bool? ?? false, + withPresence: json['withPresence'] as bool? ?? true, + withLock: json['withLock'] as bool? ?? false, + beQuiet: json['beQuiet'] as bool? ?? false, + ); + +Map _$SubscribeOptionsToJson(SubscribeOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('withMessage', instance.withMessage); + writeNotNull('withMetadata', instance.withMetadata); + writeNotNull('withPresence', instance.withPresence); + writeNotNull('withLock', instance.withLock); + writeNotNull('beQuiet', instance.beQuiet); + return val; +} + +ChannelInfo _$ChannelInfoFromJson(Map json) => ChannelInfo( + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + ); + +Map _$ChannelInfoToJson(ChannelInfo instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + return val; +} + +const _$RtmChannelTypeEnumMap = { + RtmChannelType.none: 0, + RtmChannelType.message: 1, + RtmChannelType.stream: 2, + RtmChannelType.user: 3, +}; + +PresenceOptions _$PresenceOptionsFromJson(Map json) => + PresenceOptions( + includeUserId: json['includeUserId'] as bool? ?? true, + includeState: json['includeState'] as bool? ?? false, + page: json['page'] as String? ?? '', + ); + +Map _$PresenceOptionsToJson(PresenceOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('includeUserId', instance.includeUserId); + writeNotNull('includeState', instance.includeState); + writeNotNull('page', instance.page); + return val; +} + +PublishOptions _$PublishOptionsFromJson(Map json) => + PublishOptions( + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']) ?? + RtmChannelType.message, + messageType: + $enumDecodeNullable(_$RtmMessageTypeEnumMap, json['messageType']) ?? + RtmMessageType.binary, + customType: json['customType'] as String?, + ); + +Map _$PublishOptionsToJson(PublishOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('messageType', _$RtmMessageTypeEnumMap[instance.messageType]); + writeNotNull('customType', instance.customType); + return val; +} + +const _$RtmMessageTypeEnumMap = { + RtmMessageType.binary: 0, + RtmMessageType.string: 1, +}; + +TopicMessageOptions _$TopicMessageOptionsFromJson(Map json) => + TopicMessageOptions( + messageType: + $enumDecodeNullable(_$RtmMessageTypeEnumMap, json['messageType']) ?? + RtmMessageType.binary, + sendTs: (json['sendTs'] as num?)?.toInt() ?? 0, + customType: json['customType'] as String?, + ); + +Map _$TopicMessageOptionsToJson(TopicMessageOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('messageType', _$RtmMessageTypeEnumMap[instance.messageType]); + writeNotNull('sendTs', instance.sendTs); + writeNotNull('customType', instance.customType); + return val; +} + +GetOnlineUsersOptions _$GetOnlineUsersOptionsFromJson( + Map json) => + GetOnlineUsersOptions( + includeUserId: json['includeUserId'] as bool? ?? true, + includeState: json['includeState'] as bool? ?? false, + page: json['page'] as String?, + ); + +Map _$GetOnlineUsersOptionsToJson( + GetOnlineUsersOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('includeUserId', instance.includeUserId); + writeNotNull('includeState', instance.includeState); + writeNotNull('page', instance.page); + return val; +} + +RtmProxyConfig _$RtmProxyConfigFromJson(Map json) => + RtmProxyConfig( + proxyType: + $enumDecodeNullable(_$RtmProxyTypeEnumMap, json['proxyType']) ?? + RtmProxyType.none, + server: json['server'] as String?, + port: (json['port'] as num?)?.toInt() ?? 0, + account: json['account'] as String?, + password: json['password'] as String?, + ); + +Map _$RtmProxyConfigToJson(RtmProxyConfig instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('proxyType', _$RtmProxyTypeEnumMap[instance.proxyType]); + writeNotNull('server', instance.server); + writeNotNull('port', instance.port); + writeNotNull('account', instance.account); + writeNotNull('password', instance.password); + return val; +} + +const _$RtmProxyTypeEnumMap = { + RtmProxyType.none: 0, + RtmProxyType.http: 1, + RtmProxyType.cloudTcp: 2, +}; + +RtmEncryptionConfig _$RtmEncryptionConfigFromJson(Map json) => + RtmEncryptionConfig( + encryptionMode: $enumDecodeNullable( + _$RtmEncryptionModeEnumMap, json['encryptionMode']) ?? + RtmEncryptionMode.none, + encryptionKey: json['encryptionKey'] as String?, + ); + +Map _$RtmEncryptionConfigToJson(RtmEncryptionConfig instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull( + 'encryptionMode', _$RtmEncryptionModeEnumMap[instance.encryptionMode]); + writeNotNull('encryptionKey', instance.encryptionKey); + return val; +} + +const _$RtmEncryptionModeEnumMap = { + RtmEncryptionMode.none: 0, + RtmEncryptionMode.aes128Gcm: 1, + RtmEncryptionMode.aes256Gcm: 2, +}; + +RtmPrivateConfig _$RtmPrivateConfigFromJson(Map json) => + RtmPrivateConfig( + serviceType: json['serviceType'] == null + ? const {RtmServiceType.none} + : const RtmServiceTypeListConverter() + .fromJson((json['serviceType'] as num?)?.toInt()), + accessPointHosts: (json['accessPointHosts'] as List?) + ?.map((e) => e as String) + .toList(), + ); + +Map _$RtmPrivateConfigToJson(RtmPrivateConfig instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('serviceType', + const RtmServiceTypeListConverter().toJson(instance.serviceType)); + writeNotNull('accessPointHosts', instance.accessPointHosts); + return val; +} + +const _$RtmLinkStateEnumMap = { + RtmLinkState.idle: 0, + RtmLinkState.connecting: 1, + RtmLinkState.connected: 2, + RtmLinkState.disconnected: 3, + RtmLinkState.suspended: 4, + RtmLinkState.failed: 5, +}; + +const _$RtmLinkOperationEnumMap = { + RtmLinkOperation.login: 0, + RtmLinkOperation.logout: 1, + RtmLinkOperation.join: 2, + RtmLinkOperation.leave: 3, + RtmLinkOperation.serverReject: 4, + RtmLinkOperation.autoReconnect: 5, + RtmLinkOperation.reconnected: 6, + RtmLinkOperation.heartbeatLost: 7, + RtmLinkOperation.serverTimeout: 8, + RtmLinkOperation.networkChange: 9, +}; + +const _$RtmServiceTypeEnumMap = { + RtmServiceType.none: 0, + RtmServiceType.message: 1, + RtmServiceType.stream: 2, +}; + +const _$RtmProtocolTypeEnumMap = { + RtmProtocolType.tcpUdp: 0, + RtmProtocolType.tcpOnly: 1, +}; + +const _$RtmAreaCodeEnumMap = { + RtmAreaCode.cn: 1, + RtmAreaCode.na: 2, + RtmAreaCode.eu: 4, + RtmAreaCode.asm: 8, + RtmAreaCode.jp: 16, + RtmAreaCode.ind: 32, + RtmAreaCode.glob: 4294967295, +}; + +const _$RtmErrorCodeEnumMap = { + RtmErrorCode.ok: 0, + RtmErrorCode.notInitialized: -10001, + RtmErrorCode.notLogin: -10002, + RtmErrorCode.invalidAppId: -10003, + RtmErrorCode.invalidEventHandler: -10004, + RtmErrorCode.invalidToken: -10005, + RtmErrorCode.invalidUserId: -10006, + RtmErrorCode.initServiceFailed: -10007, + RtmErrorCode.invalidChannelName: -10008, + RtmErrorCode.tokenExpired: -10009, + RtmErrorCode.loginNoServerResources: -10010, + RtmErrorCode.loginTimeout: -10011, + RtmErrorCode.loginRejected: -10012, + RtmErrorCode.loginAborted: -10013, + RtmErrorCode.invalidParameter: -10014, + RtmErrorCode.loginNotAuthorized: -10015, + RtmErrorCode.inconsistentAppid: -10016, + RtmErrorCode.duplicateOperation: -10017, + RtmErrorCode.instanceAlreadyReleased: -10018, + RtmErrorCode.invalidChannelType: -10019, + RtmErrorCode.invalidEncryptionParameter: -10020, + RtmErrorCode.operationRateExceedLimitation: -10021, + RtmErrorCode.serviceNotSupported: -10022, + RtmErrorCode.loginCanceled: -10023, + RtmErrorCode.invalidPrivateConfig: -10024, + RtmErrorCode.notConnected: -10025, + RtmErrorCode.channelNotJoined: -11001, + RtmErrorCode.channelNotSubscribed: -11002, + RtmErrorCode.channelExceedTopicUserLimitation: -11003, + RtmErrorCode.channelInReuse: -11004, + RtmErrorCode.channelInstanceExceedLimitation: -11005, + RtmErrorCode.channelInErrorState: -11006, + RtmErrorCode.channelJoinFailed: -11007, + RtmErrorCode.channelInvalidTopicName: -11008, + RtmErrorCode.channelInvalidMessage: -11009, + RtmErrorCode.channelMessageLengthExceedLimitation: -11010, + RtmErrorCode.channelInvalidUserList: -11011, + RtmErrorCode.channelNotAvailable: -11012, + RtmErrorCode.channelTopicNotSubscribed: -11013, + RtmErrorCode.channelExceedTopicLimitation: -11014, + RtmErrorCode.channelJoinTopicFailed: -11015, + RtmErrorCode.channelTopicNotJoined: -11016, + RtmErrorCode.channelTopicNotExist: -11017, + RtmErrorCode.channelInvalidTopicMeta: -11018, + RtmErrorCode.channelSubscribeTimeout: -11019, + RtmErrorCode.channelSubscribeTooFrequent: -11020, + RtmErrorCode.channelSubscribeFailed: -11021, + RtmErrorCode.channelUnsubscribeFailed: -11022, + RtmErrorCode.channelEncryptMessageFailed: -11023, + RtmErrorCode.channelPublishMessageFailed: -11024, + RtmErrorCode.channelPublishMessageTooFrequent: -11025, + RtmErrorCode.channelPublishMessageTimeout: -11026, + RtmErrorCode.channelNotConnected: -11027, + RtmErrorCode.channelLeaveFailed: -11028, + RtmErrorCode.channelCustomTypeLengthOverflow: -11029, + RtmErrorCode.channelInvalidCustomType: -11030, + RtmErrorCode.channelUnsupportedMessageType: -11031, + RtmErrorCode.channelPresenceNotReady: -11032, + RtmErrorCode.channelReceiverOffline: -11033, + RtmErrorCode.channelJoinCanceled: -11034, + RtmErrorCode.storageOperationFailed: -12001, + RtmErrorCode.storageMetadataItemExceedLimitation: -12002, + RtmErrorCode.storageInvalidMetadataItem: -12003, + RtmErrorCode.storageInvalidArgument: -12004, + RtmErrorCode.storageInvalidRevision: -12005, + RtmErrorCode.storageMetadataLengthOverflow: -12006, + RtmErrorCode.storageInvalidLockName: -12007, + RtmErrorCode.storageLockNotAcquired: -12008, + RtmErrorCode.storageInvalidKey: -12009, + RtmErrorCode.storageInvalidValue: -12010, + RtmErrorCode.storageKeyLengthOverflow: -12011, + RtmErrorCode.storageValueLengthOverflow: -12012, + RtmErrorCode.storageDuplicateKey: -12013, + RtmErrorCode.storageOutdatedRevision: -12014, + RtmErrorCode.storageNotSubscribe: -12015, + RtmErrorCode.storageInvalidMetadataInstance: -12016, + RtmErrorCode.storageSubscribeUserExceedLimitation: -12017, + RtmErrorCode.storageOperationTimeout: -12018, + RtmErrorCode.storageNotAvailable: -12019, + RtmErrorCode.presenceNotConnected: -13001, + RtmErrorCode.presenceNotWritable: -13002, + RtmErrorCode.presenceInvalidArgument: -13003, + RtmErrorCode.presenceCachedTooManyStates: -13004, + RtmErrorCode.presenceStateCountOverflow: -13005, + RtmErrorCode.presenceInvalidStateKey: -13006, + RtmErrorCode.presenceInvalidStateValue: -13007, + RtmErrorCode.presenceStateKeySizeOverflow: -13008, + RtmErrorCode.presenceStateValueSizeOverflow: -13009, + RtmErrorCode.presenceStateDuplicateKey: -13010, + RtmErrorCode.presenceUserNotExist: -13011, + RtmErrorCode.presenceOperationTimeout: -13012, + RtmErrorCode.presenceOperationFailed: -13013, + RtmErrorCode.lockOperationFailed: -14001, + RtmErrorCode.lockOperationTimeout: -14002, + RtmErrorCode.lockOperationPerforming: -14003, + RtmErrorCode.lockAlreadyExist: -14004, + RtmErrorCode.lockInvalidName: -14005, + RtmErrorCode.lockNotAcquired: -14006, + RtmErrorCode.lockAcquireFailed: -14007, + RtmErrorCode.lockNotExist: -14008, + RtmErrorCode.lockNotAvailable: -14009, +}; + +const _$RtmConnectionStateEnumMap = { + RtmConnectionState.disconnected: 1, + RtmConnectionState.connecting: 2, + RtmConnectionState.connected: 3, + RtmConnectionState.reconnecting: 4, + RtmConnectionState.failed: 5, +}; + +const _$RtmConnectionChangeReasonEnumMap = { + RtmConnectionChangeReason.connecting: 0, + RtmConnectionChangeReason.joinSuccess: 1, + RtmConnectionChangeReason.interrupted: 2, + RtmConnectionChangeReason.bannedByServer: 3, + RtmConnectionChangeReason.joinFailed: 4, + RtmConnectionChangeReason.leaveChannel: 5, + RtmConnectionChangeReason.invalidAppId: 6, + RtmConnectionChangeReason.invalidChannelName: 7, + RtmConnectionChangeReason.invalidToken: 8, + RtmConnectionChangeReason.tokenExpired: 9, + RtmConnectionChangeReason.rejectedByServer: 10, + RtmConnectionChangeReason.settingProxyServer: 11, + RtmConnectionChangeReason.renewToken: 12, + RtmConnectionChangeReason.clientIpAddressChanged: 13, + RtmConnectionChangeReason.keepAliveTimeout: 14, + RtmConnectionChangeReason.rejoinSuccess: 15, + RtmConnectionChangeReason.lost: 16, + RtmConnectionChangeReason.echoTest: 17, + RtmConnectionChangeReason.clientIpAddressChangedByUser: 18, + RtmConnectionChangeReason.sameUidLogin: 19, + RtmConnectionChangeReason.tooManyBroadcasters: 20, + RtmConnectionChangeReason.licenseValidationFailure: 21, + RtmConnectionChangeReason.certificationVerifyFailure: 22, + RtmConnectionChangeReason.streamChannelNotAvailable: 23, + RtmConnectionChangeReason.inconsistentAppid: 24, + RtmConnectionChangeReason.loginSuccess: 10001, + RtmConnectionChangeReason.logout: 10002, + RtmConnectionChangeReason.presenceNotReady: 10003, +}; + +const _$RtmStorageTypeEnumMap = { + RtmStorageType.none: 0, + RtmStorageType.user: 1, + RtmStorageType.channel: 2, +}; + +const _$RtmStorageEventTypeEnumMap = { + RtmStorageEventType.none: 0, + RtmStorageEventType.snapshot: 1, + RtmStorageEventType.set: 2, + RtmStorageEventType.update: 3, + RtmStorageEventType.remove: 4, +}; + +const _$RtmLockEventTypeEnumMap = { + RtmLockEventType.none: 0, + RtmLockEventType.snapshot: 1, + RtmLockEventType.lockSet: 2, + RtmLockEventType.lockRemoved: 3, + RtmLockEventType.lockAcquired: 4, + RtmLockEventType.lockReleased: 5, + RtmLockEventType.lockExpired: 6, +}; + +const _$RtmTopicEventTypeEnumMap = { + RtmTopicEventType.none: 0, + RtmTopicEventType.snapshot: 1, + RtmTopicEventType.remoteJoinTopic: 2, + RtmTopicEventType.remoteLeaveTopic: 3, +}; + +const _$RtmPresenceEventTypeEnumMap = { + RtmPresenceEventType.none: 0, + RtmPresenceEventType.snapshot: 1, + RtmPresenceEventType.interval: 2, + RtmPresenceEventType.remoteJoinChannel: 3, + RtmPresenceEventType.remoteLeaveChannel: 4, + RtmPresenceEventType.remoteTimeout: 5, + RtmPresenceEventType.remoteStateChanged: 6, + RtmPresenceEventType.errorOutOfService: 7, +}; diff --git a/lib/src/agora_rtm_client.dart b/lib/src/agora_rtm_client.dart new file mode 100644 index 0000000..0939910 --- /dev/null +++ b/lib/src/agora_rtm_client.dart @@ -0,0 +1,410 @@ +import 'binding_forward_export.dart'; +part 'agora_rtm_client.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmConfig { + const RtmConfig( + {this.areaCode = const {RtmAreaCode.glob}, + this.protocolType = RtmProtocolType.tcpUdp, + this.presenceTimeout = 300, + this.heartbeatInterval = 5, + this.useStringUserId = true, + this.logConfig, + this.proxyConfig, + this.encryptionConfig, + this.privateConfig}); + + @RtmAreaCodeListConverter() + @JsonKey(name: 'areaCode') + final Set? areaCode; + + @JsonKey(name: 'protocolType') + final RtmProtocolType? protocolType; + + @JsonKey(name: 'presenceTimeout') + final int? presenceTimeout; + + @JsonKey(name: 'heartbeatInterval') + final int? heartbeatInterval; + + @JsonKey(name: 'useStringUserId') + final bool? useStringUserId; + + @JsonKey(name: 'logConfig') + final RtmLogConfig? logConfig; + + @JsonKey(name: 'proxyConfig') + final RtmProxyConfig? proxyConfig; + + @JsonKey(name: 'encryptionConfig') + final RtmEncryptionConfig? encryptionConfig; + + @JsonKey(name: 'privateConfig') + final RtmPrivateConfig? privateConfig; + + factory RtmConfig.fromJson(Map json) => + _$RtmConfigFromJson(json); + + Map toJson() => _$RtmConfigToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class LinkStateEvent { + const LinkStateEvent( + {this.currentState, + this.previousState, + this.serviceType, + this.operation, + this.reason, + this.affectedChannels, + this.unrestoredChannels, + this.isResumed, + this.timestamp}); + + @JsonKey(name: 'currentState') + final RtmLinkState? currentState; + + @JsonKey(name: 'previousState') + final RtmLinkState? previousState; + + @JsonKey(name: 'serviceType') + final RtmServiceType? serviceType; + + @JsonKey(name: 'operation') + final RtmLinkOperation? operation; + + @JsonKey(name: 'reason') + final String? reason; + + @JsonKey(name: 'affectedChannels') + final List? affectedChannels; + + @JsonKey(name: 'unrestoredChannels') + final List? unrestoredChannels; + + @JsonKey(name: 'isResumed') + final bool? isResumed; + + @JsonKey(name: 'timestamp') + final int? timestamp; + + factory LinkStateEvent.fromJson(Map json) => + _$LinkStateEventFromJson(json); + + Map toJson() => _$LinkStateEventToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class MessageEvent { + const MessageEvent( + {this.channelType, + this.messageType, + this.channelName, + this.channelTopic, + this.message, + this.messageLength, + this.publisher, + this.customType, + this.timestamp}); + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'messageType') + final RtmMessageType? messageType; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelTopic') + final String? channelTopic; + + @JsonKey(name: 'message', ignore: true) + final Uint8List? message; + + @JsonKey(name: 'messageLength') + final int? messageLength; + + @JsonKey(name: 'publisher') + final String? publisher; + + @JsonKey(name: 'customType') + final String? customType; + + @JsonKey(name: 'timestamp') + final int? timestamp; + + factory MessageEvent.fromJson(Map json) => + _$MessageEventFromJson(json); + + Map toJson() => _$MessageEventToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class PresenceEvent { + const PresenceEvent( + {this.type, + this.channelType, + this.channelName, + this.publisher, + this.stateItems, + this.interval, + this.snapshot, + this.timestamp}); + + @JsonKey(name: 'type') + final RtmPresenceEventType? type; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'publisher') + final String? publisher; + + @JsonKey(name: 'stateItems') + final List? stateItems; + + @JsonKey(name: 'interval') + final IntervalInfo? interval; + + @JsonKey(name: 'snapshot') + final SnapshotInfo? snapshot; + + @JsonKey(name: 'timestamp') + final int? timestamp; + + factory PresenceEvent.fromJson(Map json) => + _$PresenceEventFromJson(json); + + Map toJson() => _$PresenceEventToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class IntervalInfo { + const IntervalInfo( + {this.joinUserList, + this.leaveUserList, + this.timeoutUserList, + this.userStateList}); + + @JsonKey(name: 'joinUserList') + final UserList? joinUserList; + + @JsonKey(name: 'leaveUserList') + final UserList? leaveUserList; + + @JsonKey(name: 'timeoutUserList') + final UserList? timeoutUserList; + + @JsonKey(name: 'userStateList') + final List? userStateList; + + factory IntervalInfo.fromJson(Map json) => + _$IntervalInfoFromJson(json); + + Map toJson() => _$IntervalInfoToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class SnapshotInfo { + const SnapshotInfo({this.userStateList}); + + @JsonKey(name: 'userStateList') + final List? userStateList; + + factory SnapshotInfo.fromJson(Map json) => + _$SnapshotInfoFromJson(json); + + Map toJson() => _$SnapshotInfoToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class TopicEvent { + const TopicEvent( + {this.type, + this.channelName, + this.publisher, + this.topicInfos, + this.timestamp}); + + @JsonKey(name: 'type') + final RtmTopicEventType? type; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'publisher') + final String? publisher; + + @JsonKey(name: 'topicInfos') + final List? topicInfos; + + @JsonKey(name: 'timestamp') + final int? timestamp; + + factory TopicEvent.fromJson(Map json) => + _$TopicEventFromJson(json); + + Map toJson() => _$TopicEventToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class LockEvent { + const LockEvent( + {this.channelType, + this.eventType, + this.channelName, + this.lockDetailList, + this.timestamp}); + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'eventType') + final RtmLockEventType? eventType; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'lockDetailList') + final List? lockDetailList; + + @JsonKey(name: 'timestamp') + final int? timestamp; + + factory LockEvent.fromJson(Map json) => + _$LockEventFromJson(json); + + Map toJson() => _$LockEventToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StorageEvent { + const StorageEvent( + {this.channelType, + this.storageType, + this.eventType, + this.target, + this.data, + this.timestamp}); + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'storageType') + final RtmStorageType? storageType; + + @JsonKey(name: 'eventType') + final RtmStorageEventType? eventType; + + @JsonKey(name: 'target') + final String? target; + + @JsonKey(name: 'data') + final Metadata? data; + + @JsonKey(name: 'timestamp') + final int? timestamp; + + factory StorageEvent.fromJson(Map json) => + _$StorageEventFromJson(json); + + Map toJson() => _$StorageEventToJson(this); +} + +class LoginResult { + LoginResult(); +} + +class LogoutResult { + LogoutResult(); +} + +class RenewTokenResult { + RenewTokenResult({required this.serverType, required this.channelName}); + + final RtmServiceType serverType; + + final String channelName; +} + +class PublishResult { + PublishResult(); +} + +class SubscribeResult { + SubscribeResult({required this.channelName}); + + final String channelName; +} + +class UnsubscribeResult { + UnsubscribeResult({required this.channelName}); + + final String channelName; +} + +class TokenEvent { + const TokenEvent(this.channelName); + final String channelName; +} + +abstract class RtmClient { + void addListener( + {void Function(LinkStateEvent event)? linkState, + void Function(MessageEvent event)? message, + void Function(PresenceEvent event)? presence, + void Function(TopicEvent event)? topic, + void Function(LockEvent event)? lock, + void Function(StorageEvent event)? storage, + void Function(TokenEvent event)? token}); + + void removeListener( + {void Function(LinkStateEvent event)? linkState, + void Function(MessageEvent event)? message, + void Function(PresenceEvent event)? presence, + void Function(TopicEvent event)? topic, + void Function(LockEvent event)? lock, + void Function(StorageEvent event)? storage, + void Function(TokenEvent event)? token}); + + Future release(); + + Future<(RtmStatus, LoginResult?)> login(String token); + + Future<(RtmStatus, LogoutResult?)> logout(); + + RtmStorage getStorage(); + + RtmLock getLock(); + + RtmPresence getPresence(); + + Future<(RtmStatus, RenewTokenResult?)> renewToken(String token); + + Future<(RtmStatus, PublishResult?)> publish( + String channelName, String message, + {RtmChannelType channelType = RtmChannelType.message, + String? customType}); + + Future<(RtmStatus, SubscribeResult?)> subscribe(String channelName, + {bool withMessage = true, + bool withMetadata = false, + bool withPresence = true, + bool withLock = false, + bool beQuiet = false}); + + Future<(RtmStatus, UnsubscribeResult?)> unsubscribe(String channelName); + + Future<(RtmStatus, StreamChannel?)> createStreamChannel(String channelName); + + Future setParameters(String parameters); + + Future<(RtmStatus, PublishResult?)> publishBinaryMessage( + String channelName, Uint8List message, + {RtmChannelType channelType = RtmChannelType.message, + String? customType}); +} diff --git a/lib/src/agora_rtm_client.g.dart b/lib/src/agora_rtm_client.g.dart new file mode 100644 index 0000000..5b84bbd --- /dev/null +++ b/lib/src/agora_rtm_client.g.dart @@ -0,0 +1,401 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'agora_rtm_client.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RtmConfig _$RtmConfigFromJson(Map json) => RtmConfig( + areaCode: json['areaCode'] == null + ? const {RtmAreaCode.glob} + : const RtmAreaCodeListConverter() + .fromJson((json['areaCode'] as num?)?.toInt()), + protocolType: + $enumDecodeNullable(_$RtmProtocolTypeEnumMap, json['protocolType']) ?? + RtmProtocolType.tcpUdp, + presenceTimeout: (json['presenceTimeout'] as num?)?.toInt() ?? 300, + heartbeatInterval: (json['heartbeatInterval'] as num?)?.toInt() ?? 5, + useStringUserId: json['useStringUserId'] as bool? ?? true, + logConfig: json['logConfig'] == null + ? null + : RtmLogConfig.fromJson(json['logConfig'] as Map), + proxyConfig: json['proxyConfig'] == null + ? null + : RtmProxyConfig.fromJson( + json['proxyConfig'] as Map), + encryptionConfig: json['encryptionConfig'] == null + ? null + : RtmEncryptionConfig.fromJson( + json['encryptionConfig'] as Map), + privateConfig: json['privateConfig'] == null + ? null + : RtmPrivateConfig.fromJson( + json['privateConfig'] as Map), + ); + +Map _$RtmConfigToJson(RtmConfig instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull( + 'areaCode', const RtmAreaCodeListConverter().toJson(instance.areaCode)); + writeNotNull('protocolType', _$RtmProtocolTypeEnumMap[instance.protocolType]); + writeNotNull('presenceTimeout', instance.presenceTimeout); + writeNotNull('heartbeatInterval', instance.heartbeatInterval); + writeNotNull('useStringUserId', instance.useStringUserId); + writeNotNull('logConfig', instance.logConfig?.toJson()); + writeNotNull('proxyConfig', instance.proxyConfig?.toJson()); + writeNotNull('encryptionConfig', instance.encryptionConfig?.toJson()); + writeNotNull('privateConfig', instance.privateConfig?.toJson()); + return val; +} + +const _$RtmProtocolTypeEnumMap = { + RtmProtocolType.tcpUdp: 0, + RtmProtocolType.tcpOnly: 1, +}; + +LinkStateEvent _$LinkStateEventFromJson(Map json) => + LinkStateEvent( + currentState: + $enumDecodeNullable(_$RtmLinkStateEnumMap, json['currentState']), + previousState: + $enumDecodeNullable(_$RtmLinkStateEnumMap, json['previousState']), + serviceType: + $enumDecodeNullable(_$RtmServiceTypeEnumMap, json['serviceType']), + operation: + $enumDecodeNullable(_$RtmLinkOperationEnumMap, json['operation']), + reason: json['reason'] as String?, + affectedChannels: (json['affectedChannels'] as List?) + ?.map((e) => e as String) + .toList(), + unrestoredChannels: (json['unrestoredChannels'] as List?) + ?.map((e) => e as String) + .toList(), + isResumed: json['isResumed'] as bool?, + timestamp: (json['timestamp'] as num?)?.toInt(), + ); + +Map _$LinkStateEventToJson(LinkStateEvent instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('currentState', _$RtmLinkStateEnumMap[instance.currentState]); + writeNotNull('previousState', _$RtmLinkStateEnumMap[instance.previousState]); + writeNotNull('serviceType', _$RtmServiceTypeEnumMap[instance.serviceType]); + writeNotNull('operation', _$RtmLinkOperationEnumMap[instance.operation]); + writeNotNull('reason', instance.reason); + writeNotNull('affectedChannels', instance.affectedChannels); + writeNotNull('unrestoredChannels', instance.unrestoredChannels); + writeNotNull('isResumed', instance.isResumed); + writeNotNull('timestamp', instance.timestamp); + return val; +} + +const _$RtmLinkStateEnumMap = { + RtmLinkState.idle: 0, + RtmLinkState.connecting: 1, + RtmLinkState.connected: 2, + RtmLinkState.disconnected: 3, + RtmLinkState.suspended: 4, + RtmLinkState.failed: 5, +}; + +const _$RtmServiceTypeEnumMap = { + RtmServiceType.none: 0, + RtmServiceType.message: 1, + RtmServiceType.stream: 2, +}; + +const _$RtmLinkOperationEnumMap = { + RtmLinkOperation.login: 0, + RtmLinkOperation.logout: 1, + RtmLinkOperation.join: 2, + RtmLinkOperation.leave: 3, + RtmLinkOperation.serverReject: 4, + RtmLinkOperation.autoReconnect: 5, + RtmLinkOperation.reconnected: 6, + RtmLinkOperation.heartbeatLost: 7, + RtmLinkOperation.serverTimeout: 8, + RtmLinkOperation.networkChange: 9, +}; + +MessageEvent _$MessageEventFromJson(Map json) => MessageEvent( + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + messageType: + $enumDecodeNullable(_$RtmMessageTypeEnumMap, json['messageType']), + channelName: json['channelName'] as String?, + channelTopic: json['channelTopic'] as String?, + messageLength: (json['messageLength'] as num?)?.toInt(), + publisher: json['publisher'] as String?, + customType: json['customType'] as String?, + timestamp: (json['timestamp'] as num?)?.toInt(), + ); + +Map _$MessageEventToJson(MessageEvent instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('messageType', _$RtmMessageTypeEnumMap[instance.messageType]); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelTopic', instance.channelTopic); + writeNotNull('messageLength', instance.messageLength); + writeNotNull('publisher', instance.publisher); + writeNotNull('customType', instance.customType); + writeNotNull('timestamp', instance.timestamp); + return val; +} + +const _$RtmChannelTypeEnumMap = { + RtmChannelType.none: 0, + RtmChannelType.message: 1, + RtmChannelType.stream: 2, + RtmChannelType.user: 3, +}; + +const _$RtmMessageTypeEnumMap = { + RtmMessageType.binary: 0, + RtmMessageType.string: 1, +}; + +PresenceEvent _$PresenceEventFromJson(Map json) => + PresenceEvent( + type: $enumDecodeNullable(_$RtmPresenceEventTypeEnumMap, json['type']), + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + channelName: json['channelName'] as String?, + publisher: json['publisher'] as String?, + stateItems: (json['stateItems'] as List?) + ?.map((e) => StateItem.fromJson(e as Map)) + .toList(), + interval: json['interval'] == null + ? null + : IntervalInfo.fromJson(json['interval'] as Map), + snapshot: json['snapshot'] == null + ? null + : SnapshotInfo.fromJson(json['snapshot'] as Map), + timestamp: (json['timestamp'] as num?)?.toInt(), + ); + +Map _$PresenceEventToJson(PresenceEvent instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('type', _$RtmPresenceEventTypeEnumMap[instance.type]); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('channelName', instance.channelName); + writeNotNull('publisher', instance.publisher); + writeNotNull( + 'stateItems', instance.stateItems?.map((e) => e.toJson()).toList()); + writeNotNull('interval', instance.interval?.toJson()); + writeNotNull('snapshot', instance.snapshot?.toJson()); + writeNotNull('timestamp', instance.timestamp); + return val; +} + +const _$RtmPresenceEventTypeEnumMap = { + RtmPresenceEventType.none: 0, + RtmPresenceEventType.snapshot: 1, + RtmPresenceEventType.interval: 2, + RtmPresenceEventType.remoteJoinChannel: 3, + RtmPresenceEventType.remoteLeaveChannel: 4, + RtmPresenceEventType.remoteTimeout: 5, + RtmPresenceEventType.remoteStateChanged: 6, + RtmPresenceEventType.errorOutOfService: 7, +}; + +IntervalInfo _$IntervalInfoFromJson(Map json) => IntervalInfo( + joinUserList: json['joinUserList'] == null + ? null + : UserList.fromJson(json['joinUserList'] as Map), + leaveUserList: json['leaveUserList'] == null + ? null + : UserList.fromJson(json['leaveUserList'] as Map), + timeoutUserList: json['timeoutUserList'] == null + ? null + : UserList.fromJson(json['timeoutUserList'] as Map), + userStateList: (json['userStateList'] as List?) + ?.map((e) => UserState.fromJson(e as Map)) + .toList(), + ); + +Map _$IntervalInfoToJson(IntervalInfo instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('joinUserList', instance.joinUserList?.toJson()); + writeNotNull('leaveUserList', instance.leaveUserList?.toJson()); + writeNotNull('timeoutUserList', instance.timeoutUserList?.toJson()); + writeNotNull( + 'userStateList', instance.userStateList?.map((e) => e.toJson()).toList()); + return val; +} + +SnapshotInfo _$SnapshotInfoFromJson(Map json) => SnapshotInfo( + userStateList: (json['userStateList'] as List?) + ?.map((e) => UserState.fromJson(e as Map)) + .toList(), + ); + +Map _$SnapshotInfoToJson(SnapshotInfo instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull( + 'userStateList', instance.userStateList?.map((e) => e.toJson()).toList()); + return val; +} + +TopicEvent _$TopicEventFromJson(Map json) => TopicEvent( + type: $enumDecodeNullable(_$RtmTopicEventTypeEnumMap, json['type']), + channelName: json['channelName'] as String?, + publisher: json['publisher'] as String?, + topicInfos: (json['topicInfos'] as List?) + ?.map((e) => TopicInfo.fromJson(e as Map)) + .toList(), + timestamp: (json['timestamp'] as num?)?.toInt(), + ); + +Map _$TopicEventToJson(TopicEvent instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('type', _$RtmTopicEventTypeEnumMap[instance.type]); + writeNotNull('channelName', instance.channelName); + writeNotNull('publisher', instance.publisher); + writeNotNull( + 'topicInfos', instance.topicInfos?.map((e) => e.toJson()).toList()); + writeNotNull('timestamp', instance.timestamp); + return val; +} + +const _$RtmTopicEventTypeEnumMap = { + RtmTopicEventType.none: 0, + RtmTopicEventType.snapshot: 1, + RtmTopicEventType.remoteJoinTopic: 2, + RtmTopicEventType.remoteLeaveTopic: 3, +}; + +LockEvent _$LockEventFromJson(Map json) => LockEvent( + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + eventType: + $enumDecodeNullable(_$RtmLockEventTypeEnumMap, json['eventType']), + channelName: json['channelName'] as String?, + lockDetailList: (json['lockDetailList'] as List?) + ?.map((e) => LockDetail.fromJson(e as Map)) + .toList(), + timestamp: (json['timestamp'] as num?)?.toInt(), + ); + +Map _$LockEventToJson(LockEvent instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('eventType', _$RtmLockEventTypeEnumMap[instance.eventType]); + writeNotNull('channelName', instance.channelName); + writeNotNull('lockDetailList', + instance.lockDetailList?.map((e) => e.toJson()).toList()); + writeNotNull('timestamp', instance.timestamp); + return val; +} + +const _$RtmLockEventTypeEnumMap = { + RtmLockEventType.none: 0, + RtmLockEventType.snapshot: 1, + RtmLockEventType.lockSet: 2, + RtmLockEventType.lockRemoved: 3, + RtmLockEventType.lockAcquired: 4, + RtmLockEventType.lockReleased: 5, + RtmLockEventType.lockExpired: 6, +}; + +StorageEvent _$StorageEventFromJson(Map json) => StorageEvent( + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + storageType: + $enumDecodeNullable(_$RtmStorageTypeEnumMap, json['storageType']), + eventType: + $enumDecodeNullable(_$RtmStorageEventTypeEnumMap, json['eventType']), + target: json['target'] as String?, + data: json['data'] == null + ? null + : Metadata.fromJson(json['data'] as Map), + timestamp: (json['timestamp'] as num?)?.toInt(), + ); + +Map _$StorageEventToJson(StorageEvent instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('storageType', _$RtmStorageTypeEnumMap[instance.storageType]); + writeNotNull('eventType', _$RtmStorageEventTypeEnumMap[instance.eventType]); + writeNotNull('target', instance.target); + writeNotNull('data', instance.data?.toJson()); + writeNotNull('timestamp', instance.timestamp); + return val; +} + +const _$RtmStorageTypeEnumMap = { + RtmStorageType.none: 0, + RtmStorageType.user: 1, + RtmStorageType.channel: 2, +}; + +const _$RtmStorageEventTypeEnumMap = { + RtmStorageEventType.none: 0, + RtmStorageEventType.snapshot: 1, + RtmStorageEventType.set: 2, + RtmStorageEventType.update: 3, + RtmStorageEventType.remove: 4, +}; diff --git a/lib/src/agora_rtm_client_ext.dart b/lib/src/agora_rtm_client_ext.dart new file mode 100644 index 0000000..be0e83c --- /dev/null +++ b/lib/src/agora_rtm_client_ext.dart @@ -0,0 +1,68 @@ +import 'agora_rtm_client.dart'; +import 'package:agora_rtm/src/impl/agora_rtm_client_impl_override.dart'; + +// ignore_for_file: non_constant_identifier_names, + +/// Error codes and error messages. +class AgoraRtmException implements Exception { + /// @nodoc + AgoraRtmException({required this.code, this.message}); + + /// The error code. See ErrorCodeType. + final int code; + + /// The error message. + final String? message; + + @override + String toString() => 'AgoraRtmException($code, $message)'; +} + +class RtmStatus { + const RtmStatus( + this.error, + this.errorCode, + this.operation, + this.reason, + ); + + const RtmStatus.error({ + required String errorCode, + required String operation, + required String reason, + }) : this(true, errorCode, operation, reason); + + const RtmStatus.success({ + required String operation, + }) : this(false, '0', operation, ''); + + final bool error; + + final String errorCode; + + final String operation; + + final String reason; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (runtimeType != other.runtimeType) return false; + return other is RtmStatus && + error == other.error && + errorCode == other.errorCode && + operation == other.operation && + reason == other.reason; + } + + @override + int get hashCode => Object.hash(error, errorCode, operation, reason); +} + +Future<(RtmStatus, RtmClient)> RTM( + String appId, + String userId, { + RtmConfig? config, +}) { + return RtmClientImplOverride.create(appId, userId, config: config); +} diff --git a/lib/src/agora_rtm_lock.dart b/lib/src/agora_rtm_lock.dart new file mode 100644 index 0000000..4a85e1e --- /dev/null +++ b/lib/src/agora_rtm_lock.dart @@ -0,0 +1,107 @@ +import 'binding_forward_export.dart'; + +class SetLockResult { + SetLockResult( + {required this.channelName, + required this.channelType, + required this.lockName}); + + final String channelName; + + final RtmChannelType channelType; + + final String lockName; +} + +class GetLocksResult { + GetLocksResult( + {required this.channelName, + required this.channelType, + required this.lockDetailList, + required this.count}); + + final String channelName; + + final RtmChannelType channelType; + + final List lockDetailList; + + final int count; +} + +class RemoveLockResult { + RemoveLockResult( + {required this.channelName, + required this.channelType, + required this.lockName}); + + final String channelName; + + final RtmChannelType channelType; + + final String lockName; +} + +class AcquireLockResult { + AcquireLockResult( + {required this.channelName, + required this.channelType, + required this.lockName, + required this.errorDetails}); + + final String channelName; + + final RtmChannelType channelType; + + final String lockName; + + final String errorDetails; +} + +class ReleaseLockResult { + ReleaseLockResult( + {required this.channelName, + required this.channelType, + required this.lockName}); + + final String channelName; + + final RtmChannelType channelType; + + final String lockName; +} + +class RevokeLockResult { + RevokeLockResult( + {required this.channelName, + required this.channelType, + required this.lockName}); + + final String channelName; + + final RtmChannelType channelType; + + final String lockName; +} + +abstract class RtmLock { + Future<(RtmStatus, SetLockResult?)> setLock( + String channelName, RtmChannelType channelType, String lockName, + {int ttl = 10}); + + Future<(RtmStatus, GetLocksResult?)> getLocks( + String channelName, RtmChannelType channelType); + + Future<(RtmStatus, RemoveLockResult?)> removeLock( + String channelName, RtmChannelType channelType, String lockName); + + Future<(RtmStatus, AcquireLockResult?)> acquireLock( + String channelName, RtmChannelType channelType, String lockName, + {bool retry = false}); + + Future<(RtmStatus, ReleaseLockResult?)> releaseLock( + String channelName, RtmChannelType channelType, String lockName); + + Future<(RtmStatus, RevokeLockResult?)> revokeLock(String channelName, + RtmChannelType channelType, String lockName, String owner); +} diff --git a/lib/src/agora_rtm_presence.dart b/lib/src/agora_rtm_presence.dart new file mode 100644 index 0000000..cba36e2 --- /dev/null +++ b/lib/src/agora_rtm_presence.dart @@ -0,0 +1,81 @@ +import 'binding_forward_export.dart'; + +class WhoNowResult { + WhoNowResult( + {required this.userStateList, + required this.count, + required this.nextPage}); + + final List userStateList; + + final int count; + + final String nextPage; +} + +class WhereNowResult { + WhereNowResult({required this.channels, required this.count}); + + final List channels; + + final int count; +} + +class SetStateResult { + SetStateResult(); +} + +class RemoveStateResult { + RemoveStateResult(); +} + +class GetStateResult { + GetStateResult({required this.state}); + + final UserState state; +} + +class GetOnlineUsersResult { + GetOnlineUsersResult( + {required this.userStateList, + required this.count, + required this.nextPage}); + + final List userStateList; + + final int count; + + final String nextPage; +} + +class GetUserChannelsResult { + GetUserChannelsResult({required this.channels, required this.count}); + + final ChannelInfo channels; + + final int count; +} + +abstract class RtmPresence { + Future<(RtmStatus, WhoNowResult?)> whoNow( + String channelName, RtmChannelType channelType, + {bool includeUserId = true, bool includeState = false, String page = ''}); + + Future<(RtmStatus, WhereNowResult?)> whereNow(String userId); + + Future<(RtmStatus, SetStateResult?)> setState(String channelName, + RtmChannelType channelType, Map state); + + Future<(RtmStatus, RemoveStateResult?)> removeState( + String channelName, RtmChannelType channelType, + {List states = const []}); + + Future<(RtmStatus, GetStateResult?)> getState( + String channelName, RtmChannelType channelType, String userId); + + Future<(RtmStatus, GetOnlineUsersResult?)> getOnlineUsers( + String channelName, RtmChannelType channelType, + {bool includeUserId = true, bool includeState = false, String? page}); + + Future<(RtmStatus, GetUserChannelsResult?)> getUserChannels(String userId); +} diff --git a/lib/src/agora_rtm_storage.dart b/lib/src/agora_rtm_storage.dart new file mode 100644 index 0000000..82d2656 --- /dev/null +++ b/lib/src/agora_rtm_storage.dart @@ -0,0 +1,204 @@ +import 'binding_forward_export.dart'; +part 'agora_rtm_storage.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class MetadataOptions { + const MetadataOptions({this.recordTs = false, this.recordUserId = false}); + + @JsonKey(name: 'recordTs') + final bool? recordTs; + + @JsonKey(name: 'recordUserId') + final bool? recordUserId; + + factory MetadataOptions.fromJson(Map json) => + _$MetadataOptionsFromJson(json); + + Map toJson() => _$MetadataOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class MetadataItem { + const MetadataItem( + {this.key, + this.value, + this.authorUserId, + this.revision = -1, + this.updateTs = 0}); + + @JsonKey(name: 'key') + final String? key; + + @JsonKey(name: 'value') + final String? value; + + @JsonKey(name: 'authorUserId') + final String? authorUserId; + + @JsonKey(name: 'revision') + final int? revision; + + @JsonKey(name: 'updateTs') + final int? updateTs; + + factory MetadataItem.fromJson(Map json) => + _$MetadataItemFromJson(json); + + Map toJson() => _$MetadataItemToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class Metadata { + const Metadata( + {this.majorRevision = -1, this.items = const [], this.itemCount = 0}); + + @JsonKey(name: 'majorRevision') + final int? majorRevision; + + @JsonKey(name: 'items') + final List? items; + + @JsonKey(name: 'itemCount') + final int? itemCount; + + factory Metadata.fromJson(Map json) => + _$MetadataFromJson(json); + + Map toJson() => _$MetadataToJson(this); +} + +class SetChannelMetadataResult { + SetChannelMetadataResult( + {required this.channelName, required this.channelType}); + + final String channelName; + + final RtmChannelType channelType; +} + +class UpdateChannelMetadataResult { + UpdateChannelMetadataResult( + {required this.channelName, required this.channelType}); + + final String channelName; + + final RtmChannelType channelType; +} + +class RemoveChannelMetadataResult { + RemoveChannelMetadataResult( + {required this.channelName, required this.channelType}); + + final String channelName; + + final RtmChannelType channelType; +} + +class GetChannelMetadataResult { + GetChannelMetadataResult( + {required this.channelName, + required this.channelType, + required this.data}); + + final String channelName; + + final RtmChannelType channelType; + + final Metadata data; +} + +class SetUserMetadataResult { + SetUserMetadataResult({required this.userId}); + + final String userId; +} + +class UpdateUserMetadataResult { + UpdateUserMetadataResult({required this.userId}); + + final String userId; +} + +class RemoveUserMetadataResult { + RemoveUserMetadataResult({required this.userId}); + + final String userId; +} + +class GetUserMetadataResult { + GetUserMetadataResult({required this.userId, required this.data}); + + final String userId; + + final Metadata data; +} + +class SubscribeUserMetadataResult { + SubscribeUserMetadataResult({required this.userId}); + + final String userId; +} + +class UnsubscribeUserMetadataResult { + UnsubscribeUserMetadataResult({required this.userId}); + + final String userId; +} + +abstract class RtmStorage { + Future<(RtmStatus, SetChannelMetadataResult?)> setChannelMetadata( + String channelName, + RtmChannelType channelType, + List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false, + String lockName = ''}); + + Future<(RtmStatus, UpdateChannelMetadataResult?)> updateChannelMetadata( + String channelName, + RtmChannelType channelType, + List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false, + String lockName = ''}); + + Future<(RtmStatus, RemoveChannelMetadataResult?)> removeChannelMetadata( + String channelName, RtmChannelType channelType, + {int majorRevision = -1, + List metadata = const [], + bool recordTs = false, + bool recordUserId = false, + String lockName = ''}); + + Future<(RtmStatus, GetChannelMetadataResult?)> getChannelMetadata( + String channelName, RtmChannelType channelType); + + Future<(RtmStatus, SetUserMetadataResult?)> setUserMetadata( + String userId, List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false}); + + Future<(RtmStatus, UpdateUserMetadataResult?)> updateUserMetadata( + String userId, List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false}); + + Future<(RtmStatus, RemoveUserMetadataResult?)> removeUserMetadata( + String userId, + {int majorRevision = -1, + List metadata = const [], + bool recordTs = false, + bool recordUserId = false}); + + Future<(RtmStatus, GetUserMetadataResult?)> getUserMetadata(String userId); + + Future<(RtmStatus, SubscribeUserMetadataResult?)> subscribeUserMetadata( + String userId); + + Future<(RtmStatus, UnsubscribeUserMetadataResult?)> unsubscribeUserMetadata( + String userId); +} diff --git a/lib/src/agora_rtm_storage.g.dart b/lib/src/agora_rtm_storage.g.dart new file mode 100644 index 0000000..e9f464b --- /dev/null +++ b/lib/src/agora_rtm_storage.g.dart @@ -0,0 +1,76 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'agora_rtm_storage.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MetadataOptions _$MetadataOptionsFromJson(Map json) => + MetadataOptions( + recordTs: json['recordTs'] as bool? ?? false, + recordUserId: json['recordUserId'] as bool? ?? false, + ); + +Map _$MetadataOptionsToJson(MetadataOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('recordTs', instance.recordTs); + writeNotNull('recordUserId', instance.recordUserId); + return val; +} + +MetadataItem _$MetadataItemFromJson(Map json) => MetadataItem( + key: json['key'] as String?, + value: json['value'] as String?, + authorUserId: json['authorUserId'] as String?, + revision: (json['revision'] as num?)?.toInt() ?? -1, + updateTs: (json['updateTs'] as num?)?.toInt() ?? 0, + ); + +Map _$MetadataItemToJson(MetadataItem instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('key', instance.key); + writeNotNull('value', instance.value); + writeNotNull('authorUserId', instance.authorUserId); + writeNotNull('revision', instance.revision); + writeNotNull('updateTs', instance.updateTs); + return val; +} + +Metadata _$MetadataFromJson(Map json) => Metadata( + majorRevision: (json['majorRevision'] as num?)?.toInt() ?? -1, + items: (json['items'] as List?) + ?.map((e) => MetadataItem.fromJson(e as Map)) + .toList() ?? + const [], + itemCount: (json['itemCount'] as num?)?.toInt() ?? 0, + ); + +Map _$MetadataToJson(Metadata instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('majorRevision', instance.majorRevision); + writeNotNull('items', instance.items?.map((e) => e.toJson()).toList()); + writeNotNull('itemCount', instance.itemCount); + return val; +} diff --git a/lib/src/agora_stream_channel.dart b/lib/src/agora_stream_channel.dart new file mode 100644 index 0000000..0e1dbd9 --- /dev/null +++ b/lib/src/agora_stream_channel.dart @@ -0,0 +1,258 @@ +import 'binding_forward_export.dart'; +part 'agora_stream_channel.g.dart'; + +@JsonEnum(alwaysCreate: true) +enum RtmMessageQos { + @JsonValue(0) + unordered, + + @JsonValue(1) + ordered, +} + +extension RtmMessageQosExt on RtmMessageQos { + /// @nodoc + static RtmMessageQos fromValue(int value) { + return $enumDecode(_$RtmMessageQosEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmMessageQosEnumMap[this]!; + } +} + +@JsonEnum(alwaysCreate: true) +enum RtmMessagePriority { + @JsonValue(0) + highest, + + @JsonValue(1) + high, + + @JsonValue(4) + normal, + + @JsonValue(8) + low, +} + +extension RtmMessagePriorityExt on RtmMessagePriority { + /// @nodoc + static RtmMessagePriority fromValue(int value) { + return $enumDecode(_$RtmMessagePriorityEnumMap, value); + } + + /// @nodoc + int value() { + return _$RtmMessagePriorityEnumMap[this]!; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class JoinChannelOptions { + const JoinChannelOptions( + {this.token, + this.withMetadata = false, + this.withPresence = true, + this.withLock = false, + this.beQuiet = false}); + + @JsonKey(name: 'token') + final String? token; + + @JsonKey(name: 'withMetadata') + final bool? withMetadata; + + @JsonKey(name: 'withPresence') + final bool? withPresence; + + @JsonKey(name: 'withLock') + final bool? withLock; + + @JsonKey(name: 'beQuiet') + final bool? beQuiet; + + factory JoinChannelOptions.fromJson(Map json) => + _$JoinChannelOptionsFromJson(json); + + Map toJson() => _$JoinChannelOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class JoinTopicOptions { + const JoinTopicOptions( + {this.qos = RtmMessageQos.unordered, + this.priority = RtmMessagePriority.normal, + this.meta = '', + this.syncWithMedia = false}); + + @JsonKey(name: 'qos') + final RtmMessageQos? qos; + + @JsonKey(name: 'priority') + final RtmMessagePriority? priority; + + @JsonKey(name: 'meta') + final String? meta; + + @JsonKey(name: 'syncWithMedia') + final bool? syncWithMedia; + + factory JoinTopicOptions.fromJson(Map json) => + _$JoinTopicOptionsFromJson(json); + + Map toJson() => _$JoinTopicOptionsToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class TopicOptions { + const TopicOptions({this.users, this.userCount = 0}); + + @JsonKey(name: 'users') + final List? users; + + @JsonKey(name: 'userCount') + final int? userCount; + + factory TopicOptions.fromJson(Map json) => + _$TopicOptionsFromJson(json); + + Map toJson() => _$TopicOptionsToJson(this); +} + +class JoinResult { + JoinResult({required this.channelName, required this.userId}); + + final String channelName; + + final String userId; +} + +class LeaveResult { + LeaveResult({required this.channelName, required this.userId}); + + final String channelName; + + final String userId; +} + +class JoinTopicResult { + JoinTopicResult( + {required this.channelName, + required this.userId, + required this.topic, + required this.meta}); + + final String channelName; + + final String userId; + + final String topic; + + final String meta; +} + +class PublishTopicMessageResult { + PublishTopicMessageResult({required this.channelName, required this.topic}); + + final String channelName; + + final String topic; +} + +class LeaveTopicResult { + LeaveTopicResult( + {required this.channelName, + required this.userId, + required this.topic, + required this.meta}); + + final String channelName; + + final String userId; + + final String topic; + + final String meta; +} + +class SubscribeTopicResult { + SubscribeTopicResult( + {required this.channelName, + required this.userId, + required this.topic, + required this.succeedUsers, + required this.failedUsers}); + + final String channelName; + + final String userId; + + final String topic; + + final List succeedUsers; + + final List failedUsers; +} + +class UnsubscribeTopicResult { + UnsubscribeTopicResult({required this.channelName, required this.topic}); + + final String channelName; + + final String topic; +} + +class GetSubscribedUserListResult { + GetSubscribedUserListResult( + {required this.channelName, required this.topic, required this.users}); + + final String channelName; + + final String topic; + + final UserList users; +} + +abstract class StreamChannel { + Future<(RtmStatus, JoinResult?)> join( + {String? token, + bool withMetadata = false, + bool withPresence = true, + bool withLock = false, + bool beQuiet = false}); + + Future<(RtmStatus, RenewTokenResult?)> renewToken(String token); + + Future<(RtmStatus, LeaveResult?)> leave(); + + Future<(RtmStatus, String?)> getChannelName(); + + Future<(RtmStatus, JoinTopicResult?)> joinTopic(String topic, + {RtmMessageQos qos = RtmMessageQos.unordered, + RtmMessagePriority priority = RtmMessagePriority.normal, + String meta = '', + bool syncWithMedia = false}); + + Future<(RtmStatus, LeaveTopicResult?)> leaveTopic(String topic); + + Future<(RtmStatus, SubscribeTopicResult?)> subscribeTopic(String topic, + {List users = const []}); + + Future<(RtmStatus, UnsubscribeTopicResult?)> unsubscribeTopic(String topic, + {List users = const []}); + + Future<(RtmStatus, GetSubscribedUserListResult?)> getSubscribedUserList( + String topic); + + Future release(); + + Future<(RtmStatus, PublishTopicMessageResult?)> publishTextMessage( + String topic, String message, + {int sendTs = 0, String? customType}); + + Future<(RtmStatus, PublishTopicMessageResult?)> publishBinaryMessage( + String topic, Uint8List message, + {int sendTs = 0, String? customType}); +} diff --git a/lib/src/agora_stream_channel.g.dart b/lib/src/agora_stream_channel.g.dart new file mode 100644 index 0000000..8987144 --- /dev/null +++ b/lib/src/agora_stream_channel.g.dart @@ -0,0 +1,92 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'agora_stream_channel.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +JoinChannelOptions _$JoinChannelOptionsFromJson(Map json) => + JoinChannelOptions( + token: json['token'] as String?, + withMetadata: json['withMetadata'] as bool? ?? false, + withPresence: json['withPresence'] as bool? ?? true, + withLock: json['withLock'] as bool? ?? false, + beQuiet: json['beQuiet'] as bool? ?? false, + ); + +Map _$JoinChannelOptionsToJson(JoinChannelOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('token', instance.token); + writeNotNull('withMetadata', instance.withMetadata); + writeNotNull('withPresence', instance.withPresence); + writeNotNull('withLock', instance.withLock); + writeNotNull('beQuiet', instance.beQuiet); + return val; +} + +JoinTopicOptions _$JoinTopicOptionsFromJson(Map json) => + JoinTopicOptions( + qos: $enumDecodeNullable(_$RtmMessageQosEnumMap, json['qos']) ?? + RtmMessageQos.unordered, + priority: + $enumDecodeNullable(_$RtmMessagePriorityEnumMap, json['priority']) ?? + RtmMessagePriority.normal, + meta: json['meta'] as String? ?? '', + syncWithMedia: json['syncWithMedia'] as bool? ?? false, + ); + +Map _$JoinTopicOptionsToJson(JoinTopicOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('qos', _$RtmMessageQosEnumMap[instance.qos]); + writeNotNull('priority', _$RtmMessagePriorityEnumMap[instance.priority]); + writeNotNull('meta', instance.meta); + writeNotNull('syncWithMedia', instance.syncWithMedia); + return val; +} + +const _$RtmMessageQosEnumMap = { + RtmMessageQos.unordered: 0, + RtmMessageQos.ordered: 1, +}; + +const _$RtmMessagePriorityEnumMap = { + RtmMessagePriority.highest: 0, + RtmMessagePriority.high: 1, + RtmMessagePriority.normal: 4, + RtmMessagePriority.low: 8, +}; + +TopicOptions _$TopicOptionsFromJson(Map json) => TopicOptions( + users: + (json['users'] as List?)?.map((e) => e as String).toList(), + userCount: (json['userCount'] as num?)?.toInt() ?? 0, + ); + +Map _$TopicOptionsToJson(TopicOptions instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('users', instance.users); + writeNotNull('userCount', instance.userCount); + return val; +} diff --git a/lib/src/binding_forward_export.dart b/lib/src/binding_forward_export.dart new file mode 100644 index 0000000..705a91a --- /dev/null +++ b/lib/src/binding_forward_export.dart @@ -0,0 +1,12 @@ +export 'agora_rtm_base.dart'; +export 'agora_rtm_client.dart'; +export 'agora_rtm_lock.dart'; +export 'agora_rtm_presence.dart'; +export 'agora_rtm_storage.dart'; +export 'agora_stream_channel.dart'; +export 'dart:convert'; +export 'dart:typed_data'; +export 'package:json_annotation/json_annotation.dart'; +export 'package:flutter/foundation.dart'; +export 'package:agora_rtm/src/bindings/json_converters.dart'; +export 'package:agora_rtm/src/agora_rtm_client_ext.dart'; diff --git a/lib/src/bindings/agora_rtm_client_impl_override.dart b/lib/src/bindings/agora_rtm_client_impl_override.dart new file mode 100644 index 0000000..a26a30a --- /dev/null +++ b/lib/src/bindings/agora_rtm_client_impl_override.dart @@ -0,0 +1,237 @@ +import 'dart:convert' show utf8, jsonEncode; +import 'dart:typed_data' show Uint8List; + +import 'package:agora_rtm/src/agora_rtm_client_ext.dart'; +import 'package:agora_rtm/src/agora_rtm_base.dart' show PublishOptions; +import 'package:agora_rtm/src/agora_rtm_client.dart' show RtmConfig; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_client.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_stream_channel.dart'; +import 'package:agora_rtm/src/impl/extensions.dart'; +import 'package:flutter/foundation.dart' + show kIsWeb, defaultTargetPlatform, TargetPlatform, debugPrint; +import 'package:flutter/services.dart' show MethodChannel; + +import 'gen/agora_rtm_client_event_impl.dart'; +import 'gen/agora_rtm_client_impl.dart' as rtmc_binding; +import 'package:iris_method_channel/iris_method_channel.dart'; +import 'agora_stream_channel_impl_override.dart'; +import 'package:async/async.dart' show AsyncMemoizer; +import 'package:meta/meta.dart'; + +class SharedNativeHandleInitilizationArgProvider + implements InitilizationArgProvider { + const SharedNativeHandleInitilizationArgProvider(this.sharedNativeHandle); + + final Object sharedNativeHandle; + @override + IrisHandle provide(IrisApiEngineHandle apiEngineHandle) { + return ObjectIrisHandle(sharedNativeHandle); + } +} + +class _StreamChannelScopedKey implements ScopedKey { + const _StreamChannelScopedKey(this.channelName); + final String channelName; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is _StreamChannelScopedKey && other.channelName == channelName; + } + + @override + int get hashCode => channelName.hashCode; +} + +class RtmClientImplOverride extends rtmc_binding.RtmClientImpl { + RtmClientImplOverride._(IrisMethodChannel irisMethodChannel) + : super(irisMethodChannel); + + static RtmClientImplOverride create( + IrisMethodChannel irisMethodChannel, + ) { + final instance = RtmClientImplOverride._(irisMethodChannel); + return instance; + } + + Future initialize( + String appId, + String userId, + RtmEventHandler rtmEventHandler, { + RtmConfig? config, + List args = const [], + }) async { + int errorCode = 0; + try { + await _initialize( + appId, + userId, + rtmEventHandler, + config: config, + args: args, + ); + } catch (e) { + assert(e is AgoraRtmException); + errorCode = (e as AgoraRtmException).code; + } + + final status = await irisMethodChannel.wrapRtmStatus(errorCode, 'RTM'); + + return status; + } + + final _rtmClientImplScopedKey = const TypedScopedKey(RtmClientImplOverride); + + final ScopedObjects _scopedObjects = ScopedObjects(); + + final DisposableScopedObjects _streamChannelObjects = + DisposableScopedObjects(); + + @internal + late MethodChannel rtmMethodChannel; + + AsyncMemoizer? _initializeCallOnce; + + IrisMethodChannel getIrisMethodChannel() { + return irisMethodChannel; + } + + @override + Future createStreamChannel(String channelName) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_createStreamChannel_ae3d0cf'; + final param = createParams({'channelName': channelName}); + + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final errorCode = rm['errorCode']; + if (errorCode < 0) { + throwExceptionHandler(code: errorCode); + } + + final streamChannel = _streamChannelObjects.putIfAbsent( + _StreamChannelScopedKey(channelName), + () { + StreamChannelImpl streamChannel = + StreamChannelImpl(irisMethodChannel, channelName); + return streamChannel; + }, + ); + _scopedObjects.putIfAbsent( + _rtmClientImplScopedKey, + () { + return _streamChannelObjects; + }, + ); + + return streamChannel; + } + + @pragma('vm:prefer-inline') + void _throwRtmException({required int code, String? message}) { + throw AgoraRtmException(code: code, message: message); + } + + Future _initialize( + String appId, + String userId, + RtmEventHandler rtmEventHandler, { + RtmConfig? config, + List args = const [], + }) async { + _initializeCallOnce ??= AsyncMemoizer(); + await _initializeCallOnce!.runOnce(() async { + rtmMethodChannel = const MethodChannel('agora_rtm'); + + // From `iris_method_channel` + throwExceptionHandler = _throwRtmException; + + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) { + await rtmMethodChannel.invokeMethod('androidInit'); + } + + await irisMethodChannel.initilize(args); + _scopedObjects.putIfAbsent( + const TypedScopedKey(RtmClientImplOverride), + () => _streamChannelObjects, + ); + }); + + late RtmConfig adjustedConfig; + if (config != null) { + adjustedConfig = config; + } else { + adjustedConfig = const RtmConfig(); + } + + final configJsonMap = adjustedConfig.toJson(); + if (adjustedConfig.encryptionConfig?.encryptionSalt != null) { + configJsonMap['encryptionConfig']['encryptionSalt'] = + adjustedConfig.encryptionConfig?.encryptionSalt!.toList(); + } + + configJsonMap['appId'] = appId; + configJsonMap['userId'] = userId; + + final param = {'config': configJsonMap}; + + final eventHandlerWrapper = RtmEventHandlerWrapper(rtmEventHandler); + final callApiResult = await irisMethodChannel.registerEventHandler( + ScopedEvent( + scopedKey: _rtmClientImplScopedKey, + registerName: 'RtmClient_create', + unregisterName: '', + handler: eventHandlerWrapper), + jsonEncode(param)); + + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + + final result = rm['result']; + + if (result < 0) { + throwExceptionHandler(code: result); + } + + await irisMethodChannel.invokeMethod(IrisMethodCall( + 'RtmClient_setAppType', + jsonEncode({'appType': 4}), + )); + } + + @override + Future release() async { + await _scopedObjects.clear(); + + try { + await super.release(); + } catch (e) { + debugPrint('RtmClient release error: ${e.toString()}'); + } + + await irisMethodChannel.dispose(); + // _instance = null; + } + + @override + Future publish( + {required String channelName, + required String message, + required int length, + required PublishOptions option}) async { + return publishBinaryMessage( + channelName: channelName, + message: Uint8List.fromList(utf8.encode(message)), + length: length, + option: option); + } +} diff --git a/lib/src/bindings/agora_stream_channel_impl_override.dart b/lib/src/bindings/agora_stream_channel_impl_override.dart new file mode 100644 index 0000000..81f2a7b --- /dev/null +++ b/lib/src/bindings/agora_stream_channel_impl_override.dart @@ -0,0 +1,66 @@ +import 'dart:convert'; +import 'dart:typed_data' show Uint8List; + +import 'package:agora_rtm/src/agora_rtm_base.dart' show TopicMessageOptions; + +import 'gen/agora_stream_channel_impl.dart' as sci_binding; +import 'package:iris_method_channel/iris_method_channel.dart'; + +class StreamChannelImpl extends sci_binding.StreamChannelImpl + with ScopedDisposableObjectMixin { + // ignore: use_super_parameters + StreamChannelImpl(IrisMethodChannel irisMethodChannel, this._channelName) + : super(irisMethodChannel); + + final String _channelName; + + bool _released = false; + + @override + Map createParams(Map param) { + return {'channelName': _channelName, ...param}; + } + + @override + Future release() async { + if (_released) { + return 0; + } + + _released = true; + markDisposed(); + // return super.release(); + return 0; + } + + @override + Future dispose() async { + await release(); + } + + @override + Future publishTopicMessage( + {required String topic, + required String message, + required int length, + required TopicMessageOptions option}) async { + return publishBinaryMessage( + topic: topic, + message: Uint8List.fromList(utf8.encode(message)), + length: length, + option: option); + } + + @override + Future publishTextMessage( + {required String topic, + required String message, + required int length, + required TopicMessageOptions option}) { + return publishBinaryMessage( + topic: topic, + message: Uint8List.fromList(utf8.encode(message)), + length: length, + option: option); + } +} diff --git a/lib/src/bindings/gen/agora_rtm_client.dart b/lib/src/bindings/gen/agora_rtm_client.dart new file mode 100644 index 0000000..eb3d9b1 --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_client.dart @@ -0,0 +1,262 @@ +import 'binding_forward_export.dart'; + +class RtmEventHandler { + /// Construct the [RtmEventHandler]. + const RtmEventHandler({ + this.onLinkStateEvent, + this.onMessageEvent, + this.onPresenceEvent, + this.onTopicEvent, + this.onLockEvent, + this.onStorageEvent, + this.onJoinResult, + this.onLeaveResult, + this.onPublishTopicMessageResult, + this.onJoinTopicResult, + this.onLeaveTopicResult, + this.onSubscribeTopicResult, + this.onUnsubscribeTopicResult, + this.onGetSubscribedUserListResult, + this.onConnectionStateChanged, + this.onTokenPrivilegeWillExpire, + this.onSubscribeResult, + this.onUnsubscribeResult, + this.onPublishResult, + this.onLoginResult, + this.onLogoutResult, + this.onRenewTokenResult, + this.onSetChannelMetadataResult, + this.onUpdateChannelMetadataResult, + this.onRemoveChannelMetadataResult, + this.onGetChannelMetadataResult, + this.onSetUserMetadataResult, + this.onUpdateUserMetadataResult, + this.onRemoveUserMetadataResult, + this.onGetUserMetadataResult, + this.onSubscribeUserMetadataResult, + this.onUnsubscribeUserMetadataResult, + this.onSetLockResult, + this.onRemoveLockResult, + this.onReleaseLockResult, + this.onAcquireLockResult, + this.onRevokeLockResult, + this.onGetLocksResult, + this.onWhoNowResult, + this.onGetOnlineUsersResult, + this.onWhereNowResult, + this.onGetUserChannelsResult, + this.onPresenceSetStateResult, + this.onPresenceRemoveStateResult, + this.onPresenceGetStateResult, + }); + + final void Function(LinkStateEvent event)? onLinkStateEvent; + + final void Function(MessageEvent event)? onMessageEvent; + + final void Function(PresenceEvent event)? onPresenceEvent; + + final void Function(TopicEvent event)? onTopicEvent; + + final void Function(LockEvent event)? onLockEvent; + + final void Function(StorageEvent event)? onStorageEvent; + + final void Function(int requestId, String channelName, String userId, + RtmErrorCode errorCode)? onJoinResult; + + final void Function(int requestId, String channelName, String userId, + RtmErrorCode errorCode)? onLeaveResult; + + final void Function(int requestId, String channelName, String topic, + RtmErrorCode errorCode)? onPublishTopicMessageResult; + + final void Function(int requestId, String channelName, String userId, + String topic, String meta, RtmErrorCode errorCode)? onJoinTopicResult; + + final void Function(int requestId, String channelName, String userId, + String topic, String meta, RtmErrorCode errorCode)? onLeaveTopicResult; + + final void Function( + int requestId, + String channelName, + String userId, + String topic, + UserList succeedUsers, + UserList failedUsers, + RtmErrorCode errorCode)? onSubscribeTopicResult; + + final void Function(int requestId, String channelName, String topic, + RtmErrorCode errorCode)? onUnsubscribeTopicResult; + + final void Function(int requestId, String channelName, String topic, + UserList users, RtmErrorCode errorCode)? onGetSubscribedUserListResult; + + final void Function(String channelName, RtmConnectionState state, + RtmConnectionChangeReason reason)? onConnectionStateChanged; + + final void Function(String channelName)? onTokenPrivilegeWillExpire; + + final void Function( + int requestId, String channelName, RtmErrorCode errorCode)? + onSubscribeResult; + + final void Function( + int requestId, String channelName, RtmErrorCode errorCode)? + onUnsubscribeResult; + + final void Function(int requestId, RtmErrorCode errorCode)? onPublishResult; + + final void Function(int requestId, RtmErrorCode errorCode)? onLoginResult; + + final void Function(int requestId, RtmErrorCode errorCode)? onLogoutResult; + + final void Function(int requestId, RtmServiceType serverType, + String channelName, RtmErrorCode errorCode)? onRenewTokenResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + RtmErrorCode errorCode)? onSetChannelMetadataResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + RtmErrorCode errorCode)? onUpdateChannelMetadataResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + RtmErrorCode errorCode)? onRemoveChannelMetadataResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + Metadata data, + RtmErrorCode errorCode)? onGetChannelMetadataResult; + + final void Function(int requestId, String userId, RtmErrorCode errorCode)? + onSetUserMetadataResult; + + final void Function(int requestId, String userId, RtmErrorCode errorCode)? + onUpdateUserMetadataResult; + + final void Function(int requestId, String userId, RtmErrorCode errorCode)? + onRemoveUserMetadataResult; + + final void Function( + int requestId, String userId, Metadata data, RtmErrorCode errorCode)? + onGetUserMetadataResult; + + final void Function(int requestId, String userId, RtmErrorCode errorCode)? + onSubscribeUserMetadataResult; + + final void Function(int requestId, String userId, RtmErrorCode errorCode)? + onUnsubscribeUserMetadataResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode)? onSetLockResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode)? onRemoveLockResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode)? onReleaseLockResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode, + String errorDetails)? onAcquireLockResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode)? onRevokeLockResult; + + final void Function( + int requestId, + String channelName, + RtmChannelType channelType, + List lockDetailList, + int count, + RtmErrorCode errorCode)? onGetLocksResult; + + final void Function(int requestId, List userStateList, int count, + String nextPage, RtmErrorCode errorCode)? onWhoNowResult; + + final void Function(int requestId, List userStateList, int count, + String nextPage, RtmErrorCode errorCode)? onGetOnlineUsersResult; + + final void Function(int requestId, List channels, int count, + RtmErrorCode errorCode)? onWhereNowResult; + + final void Function(int requestId, ChannelInfo channels, int count, + RtmErrorCode errorCode)? onGetUserChannelsResult; + + final void Function(int requestId, RtmErrorCode errorCode)? + onPresenceSetStateResult; + + final void Function(int requestId, RtmErrorCode errorCode)? + onPresenceRemoveStateResult; + + final void Function(int requestId, UserState state, RtmErrorCode errorCode)? + onPresenceGetStateResult; +} + +abstract class RtmClient { + Future release(); + + Future login(String token); + + Future logout(); + + Future getStorage(); + + Future getLock(); + + Future getPresence(); + + Future renewToken(String token); + + Future publish( + {required String channelName, + required String message, + required int length, + required PublishOptions option}); + + Future subscribe( + {required String channelName, required SubscribeOptions options}); + + Future unsubscribe(String channelName); + + Future createStreamChannel(String channelName); + + Future setParameters(String parameters); + + Future publishBinaryMessage( + {required String channelName, + required Uint8List message, + required int length, + required PublishOptions option}); +} diff --git a/lib/src/bindings/gen/agora_rtm_client_event_impl.dart b/lib/src/bindings/gen/agora_rtm_client_event_impl.dart new file mode 100644 index 0000000..4cf5910 --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_client_event_impl.dart @@ -0,0 +1,1001 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import + +import 'binding_forward_export.dart'; +// import 'package:agora_rtc_engine/src/binding/impl_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +class RtmEventHandlerWrapper implements EventLoopEventHandler { + const RtmEventHandlerWrapper(this.rtmEventHandler); + + final RtmEventHandler rtmEventHandler; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is RtmEventHandlerWrapper && + other.rtmEventHandler == rtmEventHandler; + } + + @override + int get hashCode => rtmEventHandler.hashCode; + + @override + bool handleEventInternal( + String eventName, String eventData, List buffers) { + switch (eventName) { + case 'onLinkStateEvent_6d5317f': + if (rtmEventHandler.onLinkStateEvent == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnLinkStateEventJson paramJson = + RtmEventHandlerOnLinkStateEventJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + LinkStateEvent? event = paramJson.event; + if (event == null) { + return true; + } + event = event.fillBuffers(buffers); + rtmEventHandler.onLinkStateEvent!(event); + return true; + + case 'onMessageEvent_ce5f8f5': + if (rtmEventHandler.onMessageEvent == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnMessageEventJson paramJson = + RtmEventHandlerOnMessageEventJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + MessageEvent? event = paramJson.event; + if (event == null) { + return true; + } + event = event.fillBuffers(buffers); + rtmEventHandler.onMessageEvent!(event); + return true; + + case 'onPresenceEvent_32b5db1': + if (rtmEventHandler.onPresenceEvent == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnPresenceEventJson paramJson = + RtmEventHandlerOnPresenceEventJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + PresenceEvent? event = paramJson.event; + if (event == null) { + return true; + } + event = event.fillBuffers(buffers); + rtmEventHandler.onPresenceEvent!(event); + return true; + + case 'onTopicEvent_b4e4bd7': + if (rtmEventHandler.onTopicEvent == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnTopicEventJson paramJson = + RtmEventHandlerOnTopicEventJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + TopicEvent? event = paramJson.event; + if (event == null) { + return true; + } + event = event.fillBuffers(buffers); + rtmEventHandler.onTopicEvent!(event); + return true; + + case 'onLockEvent_5e7fbef': + if (rtmEventHandler.onLockEvent == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnLockEventJson paramJson = + RtmEventHandlerOnLockEventJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + LockEvent? event = paramJson.event; + if (event == null) { + return true; + } + event = event.fillBuffers(buffers); + rtmEventHandler.onLockEvent!(event); + return true; + + case 'onStorageEvent_4304228': + if (rtmEventHandler.onStorageEvent == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnStorageEventJson paramJson = + RtmEventHandlerOnStorageEventJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + StorageEvent? event = paramJson.event; + if (event == null) { + return true; + } + event = event.fillBuffers(buffers); + rtmEventHandler.onStorageEvent!(event); + return true; + + case 'onJoinResult_ce14e01': + if (rtmEventHandler.onJoinResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnJoinResultJson paramJson = + RtmEventHandlerOnJoinResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? userId = paramJson.userId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + userId == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onJoinResult!( + requestId, channelName, userId, errorCode); + return true; + + case 'onLeaveResult_ce14e01': + if (rtmEventHandler.onLeaveResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnLeaveResultJson paramJson = + RtmEventHandlerOnLeaveResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? userId = paramJson.userId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + userId == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onLeaveResult!( + requestId, channelName, userId, errorCode); + return true; + + case 'onPublishTopicMessageResult_ce14e01': + if (rtmEventHandler.onPublishTopicMessageResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnPublishTopicMessageResultJson paramJson = + RtmEventHandlerOnPublishTopicMessageResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? topic = paramJson.topic; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + topic == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onPublishTopicMessageResult!( + requestId, channelName, topic, errorCode); + return true; + + case 'onJoinTopicResult_bfd472e': + if (rtmEventHandler.onJoinTopicResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnJoinTopicResultJson paramJson = + RtmEventHandlerOnJoinTopicResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? userId = paramJson.userId; + String? topic = paramJson.topic; + String? meta = paramJson.meta; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + userId == null || + topic == null || + meta == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onJoinTopicResult!( + requestId, channelName, userId, topic, meta, errorCode); + return true; + + case 'onLeaveTopicResult_bfd472e': + if (rtmEventHandler.onLeaveTopicResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnLeaveTopicResultJson paramJson = + RtmEventHandlerOnLeaveTopicResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? userId = paramJson.userId; + String? topic = paramJson.topic; + String? meta = paramJson.meta; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + userId == null || + topic == null || + meta == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onLeaveTopicResult!( + requestId, channelName, userId, topic, meta, errorCode); + return true; + + case 'onSubscribeTopicResult_ae281ad': + if (rtmEventHandler.onSubscribeTopicResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnSubscribeTopicResultJson paramJson = + RtmEventHandlerOnSubscribeTopicResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? userId = paramJson.userId; + String? topic = paramJson.topic; + UserList? succeedUsers = paramJson.succeedUsers; + UserList? failedUsers = paramJson.failedUsers; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + userId == null || + topic == null || + succeedUsers == null || + failedUsers == null || + errorCode == null) { + return true; + } + succeedUsers = succeedUsers.fillBuffers(buffers); + failedUsers = failedUsers.fillBuffers(buffers); + rtmEventHandler.onSubscribeTopicResult!(requestId, channelName, userId, + topic, succeedUsers, failedUsers, errorCode); + return true; + + case 'onUnsubscribeTopicResult_ce14e01': + if (rtmEventHandler.onUnsubscribeTopicResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnUnsubscribeTopicResultJson paramJson = + RtmEventHandlerOnUnsubscribeTopicResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? topic = paramJson.topic; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + topic == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onUnsubscribeTopicResult!( + requestId, channelName, topic, errorCode); + return true; + + case 'onGetSubscribedUserListResult_79f7ec8': + if (rtmEventHandler.onGetSubscribedUserListResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnGetSubscribedUserListResultJson paramJson = + RtmEventHandlerOnGetSubscribedUserListResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + String? topic = paramJson.topic; + UserList? users = paramJson.users; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + topic == null || + users == null || + errorCode == null) { + return true; + } + users = users.fillBuffers(buffers); + rtmEventHandler.onGetSubscribedUserListResult!( + requestId, channelName, topic, users, errorCode); + return true; + + case 'onConnectionStateChanged_ac0fd53': + if (rtmEventHandler.onConnectionStateChanged == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnConnectionStateChangedJson paramJson = + RtmEventHandlerOnConnectionStateChangedJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + String? channelName = paramJson.channelName; + RtmConnectionState? state = paramJson.state; + RtmConnectionChangeReason? reason = paramJson.reason; + if (channelName == null || state == null || reason == null) { + return true; + } + + rtmEventHandler.onConnectionStateChanged!(channelName, state, reason); + return true; + + case 'onTokenPrivilegeWillExpire_3a2037f': + if (rtmEventHandler.onTokenPrivilegeWillExpire == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnTokenPrivilegeWillExpireJson paramJson = + RtmEventHandlerOnTokenPrivilegeWillExpireJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + String? channelName = paramJson.channelName; + if (channelName == null) { + return true; + } + + rtmEventHandler.onTokenPrivilegeWillExpire!(channelName); + return true; + + case 'onSubscribeResult_edf84fc': + if (rtmEventHandler.onSubscribeResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnSubscribeResultJson paramJson = + RtmEventHandlerOnSubscribeResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || channelName == null || errorCode == null) { + return true; + } + + rtmEventHandler.onSubscribeResult!(requestId, channelName, errorCode); + return true; + + case 'onUnsubscribeResult_edf84fc': + if (rtmEventHandler.onUnsubscribeResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnUnsubscribeResultJson paramJson = + RtmEventHandlerOnUnsubscribeResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || channelName == null || errorCode == null) { + return true; + } + + rtmEventHandler.onUnsubscribeResult!(requestId, channelName, errorCode); + return true; + + case 'onPublishResult_4f46899': + if (rtmEventHandler.onPublishResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnPublishResultJson paramJson = + RtmEventHandlerOnPublishResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onPublishResult!(requestId, errorCode); + return true; + + case 'onLoginResult_4f46899': + if (rtmEventHandler.onLoginResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnLoginResultJson paramJson = + RtmEventHandlerOnLoginResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onLoginResult!(requestId, errorCode); + return true; + + case 'onLogoutResult_4f46899': + if (rtmEventHandler.onLogoutResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnLogoutResultJson paramJson = + RtmEventHandlerOnLogoutResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onLogoutResult!(requestId, errorCode); + return true; + + case 'onRenewTokenResult_e9203be': + if (rtmEventHandler.onRenewTokenResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnRenewTokenResultJson paramJson = + RtmEventHandlerOnRenewTokenResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + RtmServiceType? serverType = paramJson.serverType; + String? channelName = paramJson.channelName; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + serverType == null || + channelName == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onRenewTokenResult!( + requestId, serverType, channelName, errorCode); + return true; + + case 'onSetChannelMetadataResult_7cb1a6e': + if (rtmEventHandler.onSetChannelMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnSetChannelMetadataResultJson paramJson = + RtmEventHandlerOnSetChannelMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onSetChannelMetadataResult!( + requestId, channelName, channelType, errorCode); + return true; + + case 'onUpdateChannelMetadataResult_7cb1a6e': + if (rtmEventHandler.onUpdateChannelMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnUpdateChannelMetadataResultJson paramJson = + RtmEventHandlerOnUpdateChannelMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onUpdateChannelMetadataResult!( + requestId, channelName, channelType, errorCode); + return true; + + case 'onRemoveChannelMetadataResult_7cb1a6e': + if (rtmEventHandler.onRemoveChannelMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnRemoveChannelMetadataResultJson paramJson = + RtmEventHandlerOnRemoveChannelMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onRemoveChannelMetadataResult!( + requestId, channelName, channelType, errorCode); + return true; + + case 'onGetChannelMetadataResult_d105bb5': + if (rtmEventHandler.onGetChannelMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnGetChannelMetadataResultJson paramJson = + RtmEventHandlerOnGetChannelMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + Metadata? data = paramJson.data; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + data == null || + errorCode == null) { + return true; + } + data = data.fillBuffers(buffers); + rtmEventHandler.onGetChannelMetadataResult!( + requestId, channelName, channelType, data, errorCode); + return true; + + case 'onSetUserMetadataResult_edf84fc': + if (rtmEventHandler.onSetUserMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnSetUserMetadataResultJson paramJson = + RtmEventHandlerOnSetUserMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? userId = paramJson.userId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || userId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onSetUserMetadataResult!(requestId, userId, errorCode); + return true; + + case 'onUpdateUserMetadataResult_edf84fc': + if (rtmEventHandler.onUpdateUserMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnUpdateUserMetadataResultJson paramJson = + RtmEventHandlerOnUpdateUserMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? userId = paramJson.userId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || userId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onUpdateUserMetadataResult!( + requestId, userId, errorCode); + return true; + + case 'onRemoveUserMetadataResult_edf84fc': + if (rtmEventHandler.onRemoveUserMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnRemoveUserMetadataResultJson paramJson = + RtmEventHandlerOnRemoveUserMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? userId = paramJson.userId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || userId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onRemoveUserMetadataResult!( + requestId, userId, errorCode); + return true; + + case 'onGetUserMetadataResult_7065706': + if (rtmEventHandler.onGetUserMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnGetUserMetadataResultJson paramJson = + RtmEventHandlerOnGetUserMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? userId = paramJson.userId; + Metadata? data = paramJson.data; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + userId == null || + data == null || + errorCode == null) { + return true; + } + data = data.fillBuffers(buffers); + rtmEventHandler.onGetUserMetadataResult!( + requestId, userId, data, errorCode); + return true; + + case 'onSubscribeUserMetadataResult_edf84fc': + if (rtmEventHandler.onSubscribeUserMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnSubscribeUserMetadataResultJson paramJson = + RtmEventHandlerOnSubscribeUserMetadataResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? userId = paramJson.userId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || userId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onSubscribeUserMetadataResult!( + requestId, userId, errorCode); + return true; + + case 'onUnsubscribeUserMetadataResult_edf84fc': + if (rtmEventHandler.onUnsubscribeUserMetadataResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnUnsubscribeUserMetadataResultJson paramJson = + RtmEventHandlerOnUnsubscribeUserMetadataResultJson.fromJson( + jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? userId = paramJson.userId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || userId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onUnsubscribeUserMetadataResult!( + requestId, userId, errorCode); + return true; + + case 'onSetLockResult_01034fa': + if (rtmEventHandler.onSetLockResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnSetLockResultJson paramJson = + RtmEventHandlerOnSetLockResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + String? lockName = paramJson.lockName; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + lockName == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onSetLockResult!( + requestId, channelName, channelType, lockName, errorCode); + return true; + + case 'onRemoveLockResult_01034fa': + if (rtmEventHandler.onRemoveLockResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnRemoveLockResultJson paramJson = + RtmEventHandlerOnRemoveLockResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + String? lockName = paramJson.lockName; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + lockName == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onRemoveLockResult!( + requestId, channelName, channelType, lockName, errorCode); + return true; + + case 'onReleaseLockResult_01034fa': + if (rtmEventHandler.onReleaseLockResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnReleaseLockResultJson paramJson = + RtmEventHandlerOnReleaseLockResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + String? lockName = paramJson.lockName; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + lockName == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onReleaseLockResult!( + requestId, channelName, channelType, lockName, errorCode); + return true; + + case 'onAcquireLockResult_aad7ec0': + if (rtmEventHandler.onAcquireLockResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnAcquireLockResultJson paramJson = + RtmEventHandlerOnAcquireLockResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + String? lockName = paramJson.lockName; + RtmErrorCode? errorCode = paramJson.errorCode; + String? errorDetails = paramJson.errorDetails; + if (requestId == null || + channelName == null || + channelType == null || + lockName == null || + errorCode == null || + errorDetails == null) { + return true; + } + + rtmEventHandler.onAcquireLockResult!(requestId, channelName, + channelType, lockName, errorCode, errorDetails); + return true; + + case 'onRevokeLockResult_01034fa': + if (rtmEventHandler.onRevokeLockResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnRevokeLockResultJson paramJson = + RtmEventHandlerOnRevokeLockResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + String? lockName = paramJson.lockName; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + lockName == null || + errorCode == null) { + return true; + } + + rtmEventHandler.onRevokeLockResult!( + requestId, channelName, channelType, lockName, errorCode); + return true; + + case 'onGetLocksResult_6e98f5f': + if (rtmEventHandler.onGetLocksResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnGetLocksResultJson paramJson = + RtmEventHandlerOnGetLocksResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + String? channelName = paramJson.channelName; + RtmChannelType? channelType = paramJson.channelType; + List? lockDetailList = paramJson.lockDetailList; + int? count = paramJson.count; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channelName == null || + channelType == null || + lockDetailList == null || + count == null || + errorCode == null) { + return true; + } + lockDetailList = + lockDetailList.map((e) => e.fillBuffers(buffers)).toList(); + rtmEventHandler.onGetLocksResult!(requestId, channelName, channelType, + lockDetailList, count, errorCode); + return true; + + case 'onWhoNowResult_b070777': + if (rtmEventHandler.onWhoNowResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnWhoNowResultJson paramJson = + RtmEventHandlerOnWhoNowResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + List? userStateList = paramJson.userStateList; + int? count = paramJson.count; + String? nextPage = paramJson.nextPage; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + userStateList == null || + count == null || + nextPage == null || + errorCode == null) { + return true; + } + userStateList = + userStateList.map((e) => e.fillBuffers(buffers)).toList(); + rtmEventHandler.onWhoNowResult!( + requestId, userStateList, count, nextPage, errorCode); + return true; + + case 'onGetOnlineUsersResult_b070777': + if (rtmEventHandler.onGetOnlineUsersResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnGetOnlineUsersResultJson paramJson = + RtmEventHandlerOnGetOnlineUsersResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + List? userStateList = paramJson.userStateList; + int? count = paramJson.count; + String? nextPage = paramJson.nextPage; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + userStateList == null || + count == null || + nextPage == null || + errorCode == null) { + return true; + } + userStateList = + userStateList.map((e) => e.fillBuffers(buffers)).toList(); + rtmEventHandler.onGetOnlineUsersResult!( + requestId, userStateList, count, nextPage, errorCode); + return true; + + case 'onWhereNowResult_ae64023': + if (rtmEventHandler.onWhereNowResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnWhereNowResultJson paramJson = + RtmEventHandlerOnWhereNowResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + List? channels = paramJson.channels; + int? count = paramJson.count; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channels == null || + count == null || + errorCode == null) { + return true; + } + channels = channels.map((e) => e.fillBuffers(buffers)).toList(); + rtmEventHandler.onWhereNowResult!( + requestId, channels, count, errorCode); + return true; + + case 'onGetUserChannelsResult_ae64023': + if (rtmEventHandler.onGetUserChannelsResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnGetUserChannelsResultJson paramJson = + RtmEventHandlerOnGetUserChannelsResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + ChannelInfo? channels = paramJson.channels; + int? count = paramJson.count; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || + channels == null || + count == null || + errorCode == null) { + return true; + } + channels = channels.fillBuffers(buffers); + rtmEventHandler.onGetUserChannelsResult!( + requestId, channels, count, errorCode); + return true; + + case 'onPresenceSetStateResult_4f46899': + if (rtmEventHandler.onPresenceSetStateResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnPresenceSetStateResultJson paramJson = + RtmEventHandlerOnPresenceSetStateResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onPresenceSetStateResult!(requestId, errorCode); + return true; + + case 'onPresenceRemoveStateResult_4f46899': + if (rtmEventHandler.onPresenceRemoveStateResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnPresenceRemoveStateResultJson paramJson = + RtmEventHandlerOnPresenceRemoveStateResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || errorCode == null) { + return true; + } + + rtmEventHandler.onPresenceRemoveStateResult!(requestId, errorCode); + return true; + + case 'onPresenceGetStateResult_3d764cc': + if (rtmEventHandler.onPresenceGetStateResult == null) { + return true; + } + final jsonMap = jsonDecode(eventData); + RtmEventHandlerOnPresenceGetStateResultJson paramJson = + RtmEventHandlerOnPresenceGetStateResultJson.fromJson(jsonMap); + paramJson = paramJson.fillBuffers(buffers); + int? requestId = paramJson.requestId; + UserState? state = paramJson.state; + RtmErrorCode? errorCode = paramJson.errorCode; + if (requestId == null || state == null || errorCode == null) { + return true; + } + state = state.fillBuffers(buffers); + rtmEventHandler.onPresenceGetStateResult!(requestId, state, errorCode); + return true; + } + return false; + } + + @override + bool handleEvent( + String eventName, String eventData, List buffers) { + if (!eventName.startsWith('RtmEventHandler')) return false; + final newEvent = eventName.replaceFirst('RtmEventHandler_', ''); + if (handleEventInternal(newEvent, eventData, buffers)) { + return true; + } + return false; + } +} diff --git a/lib/src/bindings/gen/agora_rtm_client_impl.dart b/lib/src/bindings/gen/agora_rtm_client_impl.dart new file mode 100644 index 0000000..15172f2 --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_client_impl.dart @@ -0,0 +1,275 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import, annotate_overrides + +import 'binding_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +class RtmClientImpl implements RtmClient { + RtmClientImpl(this.irisMethodChannel); + + final IrisMethodChannel irisMethodChannel; + + @protected + Map createParams(Map param) { + return param; + } + + @protected + bool get isOverrideClassName => false; + + @protected + String get className => 'RtmClient'; + + @override + Future release() async { + final apiType = '${isOverrideClassName ? className : 'RtmClient'}_release'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + } + + @override + Future login(String token) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_login_1fa04dd'; + final param = createParams({'token': token}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final loginJson = RtmClientLoginJson.fromJson(rm); + return loginJson.requestId; + } + + @override + Future logout() async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_logout_90386a9'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final logoutJson = RtmClientLogoutJson.fromJson(rm); + return logoutJson.requestId; + } + + @override + Future getStorage() async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_getStorage'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + return result as RtmStorage; + } + + @override + Future getLock() async { + final apiType = '${isOverrideClassName ? className : 'RtmClient'}_getLock'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + return result as RtmLock; + } + + @override + Future getPresence() async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_getPresence'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + return result as RtmPresence; + } + + @override + Future renewToken(String token) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_renewToken_1fa04dd'; + final param = createParams({'token': token}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final renewTokenJson = RtmClientRenewTokenJson.fromJson(rm); + return renewTokenJson.requestId; + } + + @override + Future publish( + {required String channelName, + required String message, + required int length, + required PublishOptions option}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_publish_2d36e93'; + final param = createParams({ + 'channelName': channelName, + 'message': message, + 'length': length, + 'option': option.toJson() + }); + final List buffers = []; + buffers.addAll(option.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final publishJson = RtmClientPublishJson.fromJson(rm); + return publishJson.requestId; + } + + @override + Future subscribe( + {required String channelName, required SubscribeOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_subscribe_3fae92d'; + final param = + createParams({'channelName': channelName, 'options': options.toJson()}); + final List buffers = []; + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final subscribeJson = RtmClientSubscribeJson.fromJson(rm); + return subscribeJson.requestId; + } + + @override + Future unsubscribe(String channelName) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_unsubscribe_1fa04dd'; + final param = createParams({'channelName': channelName}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final unsubscribeJson = RtmClientUnsubscribeJson.fromJson(rm); + return unsubscribeJson.requestId; + } + + @override + Future createStreamChannel(String channelName) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_createStreamChannel_ae3d0cf'; + final param = createParams({'channelName': channelName}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + return result as StreamChannel; + } + + @override + Future setParameters(String parameters) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_setParameters_3a2037f'; + final param = createParams({'parameters': parameters}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + } + + @override + Future publishBinaryMessage( + {required String channelName, + required Uint8List message, + required int length, + required PublishOptions option}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmClient'}_publish_2d36e93'; + final param = createParams({ + 'channelName': channelName, + 'length': length, + 'option': option.toJson() + }); + final List buffers = []; + buffers.add(message); + buffers.addAll(option.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final publishBinaryMessageJson = + RtmClientPublishBinaryMessageJson.fromJson(rm); + return publishBinaryMessageJson.requestId; + } +} diff --git a/lib/src/bindings/gen/agora_rtm_lock.dart b/lib/src/bindings/gen/agora_rtm_lock.dart new file mode 100644 index 0000000..e6cf6c7 --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_lock.dart @@ -0,0 +1,34 @@ +import 'binding_forward_export.dart'; + +abstract class RtmLock { + Future setLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName, + required int ttl}); + + Future getLocks( + {required String channelName, required RtmChannelType channelType}); + + Future removeLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName}); + + Future acquireLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName, + required bool retry}); + + Future releaseLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName}); + + Future revokeLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName, + required String owner}); +} diff --git a/lib/src/bindings/gen/agora_rtm_lock_impl.dart b/lib/src/bindings/gen/agora_rtm_lock_impl.dart new file mode 100644 index 0000000..561652d --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_lock_impl.dart @@ -0,0 +1,181 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import, annotate_overrides + +import 'binding_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +class RtmLockImpl implements RtmLock { + RtmLockImpl(this.irisMethodChannel); + + final IrisMethodChannel irisMethodChannel; + + @protected + Map createParams(Map param) { + return param; + } + + @protected + bool get isOverrideClassName => false; + + @protected + String get className => 'RtmLock'; + + @override + Future setLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName, + required int ttl}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmLock'}_setLock_89e5672'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'lockName': lockName, + 'ttl': ttl + }); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final setLockJson = RtmLockSetLockJson.fromJson(rm); + return setLockJson.requestId; + } + + @override + Future getLocks( + {required String channelName, + required RtmChannelType channelType}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmLock'}_getLocks_ad8568b'; + final param = createParams( + {'channelName': channelName, 'channelType': channelType.value()}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final getLocksJson = RtmLockGetLocksJson.fromJson(rm); + return getLocksJson.requestId; + } + + @override + Future removeLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmLock'}_removeLock_4ffa44d'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'lockName': lockName + }); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final removeLockJson = RtmLockRemoveLockJson.fromJson(rm); + return removeLockJson.requestId; + } + + @override + Future acquireLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName, + required bool retry}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmLock'}_acquireLock_cd2dbc2'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'lockName': lockName, + 'retry': retry + }); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final acquireLockJson = RtmLockAcquireLockJson.fromJson(rm); + return acquireLockJson.requestId; + } + + @override + Future releaseLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmLock'}_releaseLock_4ffa44d'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'lockName': lockName + }); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final releaseLockJson = RtmLockReleaseLockJson.fromJson(rm); + return releaseLockJson.requestId; + } + + @override + Future revokeLock( + {required String channelName, + required RtmChannelType channelType, + required String lockName, + required String owner}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmLock'}_revokeLock_fc4a9d7'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'lockName': lockName, + 'owner': owner + }); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final revokeLockJson = RtmLockRevokeLockJson.fromJson(rm); + return revokeLockJson.requestId; + } +} diff --git a/lib/src/bindings/gen/agora_rtm_presence.dart b/lib/src/bindings/gen/agora_rtm_presence.dart new file mode 100644 index 0000000..ad21ab7 --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_presence.dart @@ -0,0 +1,34 @@ +import 'binding_forward_export.dart'; + +abstract class RtmPresence { + Future whoNow( + {required String channelName, + required RtmChannelType channelType, + required PresenceOptions options}); + + Future whereNow(String userId); + + Future setState( + {required String channelName, + required RtmChannelType channelType, + required List items, + required int count}); + + Future removeState( + {required String channelName, + required RtmChannelType channelType, + required List keys, + required int count}); + + Future getState( + {required String channelName, + required RtmChannelType channelType, + required String userId}); + + Future getOnlineUsers( + {required String channelName, + required RtmChannelType channelType, + required GetOnlineUsersOptions options}); + + Future getUserChannels(String userId); +} diff --git a/lib/src/bindings/gen/agora_rtm_presence_impl.dart b/lib/src/bindings/gen/agora_rtm_presence_impl.dart new file mode 100644 index 0000000..d436a8e --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_presence_impl.dart @@ -0,0 +1,204 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import, annotate_overrides + +import 'binding_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +class RtmPresenceImpl implements RtmPresence { + RtmPresenceImpl(this.irisMethodChannel); + + final IrisMethodChannel irisMethodChannel; + + @protected + Map createParams(Map param) { + return param; + } + + @protected + bool get isOverrideClassName => false; + + @protected + String get className => 'RtmPresence'; + + @override + Future whoNow( + {required String channelName, + required RtmChannelType channelType, + required PresenceOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmPresence'}_whoNow_f7f61d1'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'options': options.toJson() + }); + final List buffers = []; + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final whoNowJson = RtmPresenceWhoNowJson.fromJson(rm); + return whoNowJson.requestId; + } + + @override + Future whereNow(String userId) async { + final apiType = + '${isOverrideClassName ? className : 'RtmPresence'}_whereNow_1fa04dd'; + final param = createParams({'userId': userId}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final whereNowJson = RtmPresenceWhereNowJson.fromJson(rm); + return whereNowJson.requestId; + } + + @override + Future setState( + {required String channelName, + required RtmChannelType channelType, + required List items, + required int count}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmPresence'}_setState_b73723a'; + final itemsJsonList = items.map((e) => e.toJson()).toList(); + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'items': itemsJsonList, + 'count': count + }); + final List buffers = []; + for (final e in items) { + buffers.addAll(e.collectBufferList()); + } + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final setStateJson = RtmPresenceSetStateJson.fromJson(rm); + return setStateJson.requestId; + } + + @override + Future removeState( + {required String channelName, + required RtmChannelType channelType, + required List keys, + required int count}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmPresence'}_removeState_d7033d8'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'keys': keys, + 'count': count + }); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final removeStateJson = RtmPresenceRemoveStateJson.fromJson(rm); + return removeStateJson.requestId; + } + + @override + Future getState( + {required String channelName, + required RtmChannelType channelType, + required String userId}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmPresence'}_getState_4ffa44d'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'userId': userId + }); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final getStateJson = RtmPresenceGetStateJson.fromJson(rm); + return getStateJson.requestId; + } + + @override + Future getOnlineUsers( + {required String channelName, + required RtmChannelType channelType, + required GetOnlineUsersOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmPresence'}_getOnlineUsers_ce2d8e8'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'options': options.toJson() + }); + final List buffers = []; + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final getOnlineUsersJson = RtmPresenceGetOnlineUsersJson.fromJson(rm); + return getOnlineUsersJson.requestId; + } + + @override + Future getUserChannels(String userId) async { + final apiType = + '${isOverrideClassName ? className : 'RtmPresence'}_getUserChannels_1fa04dd'; + final param = createParams({'userId': userId}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final getUserChannelsJson = RtmPresenceGetUserChannelsJson.fromJson(rm); + return getUserChannelsJson.requestId; + } +} diff --git a/lib/src/bindings/gen/agora_rtm_storage.dart b/lib/src/bindings/gen/agora_rtm_storage.dart new file mode 100644 index 0000000..05cb556 --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_storage.dart @@ -0,0 +1,48 @@ +import 'binding_forward_export.dart'; + +abstract class RtmStorage { + Future setChannelMetadata( + {required String channelName, + required RtmChannelType channelType, + required Metadata data, + required MetadataOptions options, + required String lockName}); + + Future updateChannelMetadata( + {required String channelName, + required RtmChannelType channelType, + required Metadata data, + required MetadataOptions options, + required String lockName}); + + Future removeChannelMetadata( + {required String channelName, + required RtmChannelType channelType, + required Metadata data, + required MetadataOptions options, + required String lockName}); + + Future getChannelMetadata( + {required String channelName, required RtmChannelType channelType}); + + Future setUserMetadata( + {required String userId, + required Metadata data, + required MetadataOptions options}); + + Future updateUserMetadata( + {required String userId, + required Metadata data, + required MetadataOptions options}); + + Future removeUserMetadata( + {required String userId, + required Metadata data, + required MetadataOptions options}); + + Future getUserMetadata(String userId); + + Future subscribeUserMetadata(String userId); + + Future unsubscribeUserMetadata(String userId); +} diff --git a/lib/src/bindings/gen/agora_rtm_storage_impl.dart b/lib/src/bindings/gen/agora_rtm_storage_impl.dart new file mode 100644 index 0000000..53b218c --- /dev/null +++ b/lib/src/bindings/gen/agora_rtm_storage_impl.dart @@ -0,0 +1,287 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import, annotate_overrides + +import 'binding_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +class RtmStorageImpl implements RtmStorage { + RtmStorageImpl(this.irisMethodChannel); + + final IrisMethodChannel irisMethodChannel; + + @protected + Map createParams(Map param) { + return param; + } + + @protected + bool get isOverrideClassName => false; + + @protected + String get className => 'RtmStorage'; + + @override + Future setChannelMetadata( + {required String channelName, + required RtmChannelType channelType, + required Metadata data, + required MetadataOptions options, + required String lockName}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_setChannelMetadata_55e6d00'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'data': data.toJson(), + 'options': options.toJson(), + 'lockName': lockName + }); + final List buffers = []; + buffers.addAll(data.collectBufferList()); + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final setChannelMetadataJson = + RtmStorageSetChannelMetadataJson.fromJson(rm); + return setChannelMetadataJson.requestId; + } + + @override + Future updateChannelMetadata( + {required String channelName, + required RtmChannelType channelType, + required Metadata data, + required MetadataOptions options, + required String lockName}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_updateChannelMetadata_55e6d00'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'data': data.toJson(), + 'options': options.toJson(), + 'lockName': lockName + }); + final List buffers = []; + buffers.addAll(data.collectBufferList()); + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final updateChannelMetadataJson = + RtmStorageUpdateChannelMetadataJson.fromJson(rm); + return updateChannelMetadataJson.requestId; + } + + @override + Future removeChannelMetadata( + {required String channelName, + required RtmChannelType channelType, + required Metadata data, + required MetadataOptions options, + required String lockName}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_removeChannelMetadata_55e6d00'; + final param = createParams({ + 'channelName': channelName, + 'channelType': channelType.value(), + 'data': data.toJson(), + 'options': options.toJson(), + 'lockName': lockName + }); + final List buffers = []; + buffers.addAll(data.collectBufferList()); + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final removeChannelMetadataJson = + RtmStorageRemoveChannelMetadataJson.fromJson(rm); + return removeChannelMetadataJson.requestId; + } + + @override + Future getChannelMetadata( + {required String channelName, + required RtmChannelType channelType}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_getChannelMetadata_ad8568b'; + final param = createParams( + {'channelName': channelName, 'channelType': channelType.value()}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final getChannelMetadataJson = + RtmStorageGetChannelMetadataJson.fromJson(rm); + return getChannelMetadataJson.requestId; + } + + @override + Future setUserMetadata( + {required String userId, + required Metadata data, + required MetadataOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_setUserMetadata_24ae125'; + final param = createParams( + {'userId': userId, 'data': data.toJson(), 'options': options.toJson()}); + final List buffers = []; + buffers.addAll(data.collectBufferList()); + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final setUserMetadataJson = RtmStorageSetUserMetadataJson.fromJson(rm); + return setUserMetadataJson.requestId; + } + + @override + Future updateUserMetadata( + {required String userId, + required Metadata data, + required MetadataOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_updateUserMetadata_24ae125'; + final param = createParams( + {'userId': userId, 'data': data.toJson(), 'options': options.toJson()}); + final List buffers = []; + buffers.addAll(data.collectBufferList()); + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final updateUserMetadataJson = + RtmStorageUpdateUserMetadataJson.fromJson(rm); + return updateUserMetadataJson.requestId; + } + + @override + Future removeUserMetadata( + {required String userId, + required Metadata data, + required MetadataOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_removeUserMetadata_24ae125'; + final param = createParams( + {'userId': userId, 'data': data.toJson(), 'options': options.toJson()}); + final List buffers = []; + buffers.addAll(data.collectBufferList()); + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final removeUserMetadataJson = + RtmStorageRemoveUserMetadataJson.fromJson(rm); + return removeUserMetadataJson.requestId; + } + + @override + Future getUserMetadata(String userId) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_getUserMetadata_1fa04dd'; + final param = createParams({'userId': userId}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final getUserMetadataJson = RtmStorageGetUserMetadataJson.fromJson(rm); + return getUserMetadataJson.requestId; + } + + @override + Future subscribeUserMetadata(String userId) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_subscribeUserMetadata_1fa04dd'; + final param = createParams({'userId': userId}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final subscribeUserMetadataJson = + RtmStorageSubscribeUserMetadataJson.fromJson(rm); + return subscribeUserMetadataJson.requestId; + } + + @override + Future unsubscribeUserMetadata(String userId) async { + final apiType = + '${isOverrideClassName ? className : 'RtmStorage'}_unsubscribeUserMetadata_1fa04dd'; + final param = createParams({'userId': userId}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final unsubscribeUserMetadataJson = + RtmStorageUnsubscribeUserMetadataJson.fromJson(rm); + return unsubscribeUserMetadataJson.requestId; + } +} diff --git a/lib/src/bindings/gen/agora_stream_channel.dart b/lib/src/bindings/gen/agora_stream_channel.dart new file mode 100644 index 0000000..fbe8941 --- /dev/null +++ b/lib/src/bindings/gen/agora_stream_channel.dart @@ -0,0 +1,44 @@ +import 'binding_forward_export.dart'; + +abstract class StreamChannel { + Future join(JoinChannelOptions options); + + Future renewToken(String token); + + Future leave(); + + Future getChannelName(); + + Future joinTopic( + {required String topic, required JoinTopicOptions options}); + + Future publishTopicMessage( + {required String topic, + required String message, + required int length, + required TopicMessageOptions option}); + + Future leaveTopic(String topic); + + Future subscribeTopic( + {required String topic, required TopicOptions options}); + + Future unsubscribeTopic( + {required String topic, required TopicOptions options}); + + Future getSubscribedUserList(String topic); + + Future release(); + + Future publishTextMessage( + {required String topic, + required String message, + required int length, + required TopicMessageOptions option}); + + Future publishBinaryMessage( + {required String topic, + required Uint8List message, + required int length, + required TopicMessageOptions option}); +} diff --git a/lib/src/bindings/gen/agora_stream_channel_impl.dart b/lib/src/bindings/gen/agora_stream_channel_impl.dart new file mode 100644 index 0000000..27a7f0e --- /dev/null +++ b/lib/src/bindings/gen/agora_stream_channel_impl.dart @@ -0,0 +1,309 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import, annotate_overrides + +import 'binding_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +class StreamChannelImpl implements StreamChannel { + StreamChannelImpl(this.irisMethodChannel); + + final IrisMethodChannel irisMethodChannel; + + @protected + Map createParams(Map param) { + return param; + } + + @protected + bool get isOverrideClassName => false; + + @protected + String get className => 'StreamChannel'; + + @override + Future join(JoinChannelOptions options) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_join_2090a6b'; + final param = createParams({'options': options.toJson()}); + final List buffers = []; + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final joinJson = StreamChannelJoinJson.fromJson(rm); + return joinJson.requestId; + } + + @override + Future renewToken(String token) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_renewToken_1fa04dd'; + final param = createParams({'token': token}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final renewTokenJson = StreamChannelRenewTokenJson.fromJson(rm); + return renewTokenJson.requestId; + } + + @override + Future leave() async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_leave_90386a9'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final leaveJson = StreamChannelLeaveJson.fromJson(rm); + return leaveJson.requestId; + } + + @override + Future getChannelName() async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_getChannelName'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + return result as String; + } + + @override + Future joinTopic( + {required String topic, required JoinTopicOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_joinTopic_ff0ec3f'; + final param = createParams({'topic': topic, 'options': options.toJson()}); + final List buffers = []; + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final joinTopicJson = StreamChannelJoinTopicJson.fromJson(rm); + return joinTopicJson.requestId; + } + + @override + Future publishTopicMessage( + {required String topic, + required String message, + required int length, + required TopicMessageOptions option}) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_publishTopicMessage_a31773e'; + final param = createParams({ + 'topic': topic, + 'message': message, + 'length': length, + 'option': option.toJson() + }); + final List buffers = []; + buffers.addAll(option.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final publishTopicMessageJson = + StreamChannelPublishTopicMessageJson.fromJson(rm); + return publishTopicMessageJson.requestId; + } + + @override + Future leaveTopic(String topic) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_leaveTopic_1fa04dd'; + final param = createParams({'topic': topic}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final leaveTopicJson = StreamChannelLeaveTopicJson.fromJson(rm); + return leaveTopicJson.requestId; + } + + @override + Future subscribeTopic( + {required String topic, required TopicOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_subscribeTopic_b801234'; + final param = createParams({'topic': topic, 'options': options.toJson()}); + final List buffers = []; + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final subscribeTopicJson = StreamChannelSubscribeTopicJson.fromJson(rm); + return subscribeTopicJson.requestId; + } + + @override + Future unsubscribeTopic( + {required String topic, required TopicOptions options}) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_unsubscribeTopic_b801234'; + final param = createParams({'topic': topic, 'options': options.toJson()}); + final List buffers = []; + buffers.addAll(options.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final unsubscribeTopicJson = StreamChannelUnsubscribeTopicJson.fromJson(rm); + return unsubscribeTopicJson.requestId; + } + + @override + Future getSubscribedUserList(String topic) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_getSubscribedUserList_1fa04dd'; + final param = createParams({'topic': topic}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final getSubscribedUserListJson = + StreamChannelGetSubscribedUserListJson.fromJson(rm); + return getSubscribedUserListJson.requestId; + } + + @override + Future release() async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_release'; + final param = createParams({}); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: null)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + } + + @override + Future publishTextMessage( + {required String topic, + required String message, + required int length, + required TopicMessageOptions option}) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_publishTopicMessage_a31773e'; + final param = createParams({ + 'topic': topic, + 'message': message, + 'length': length, + 'option': option.toJson() + }); + final List buffers = []; + buffers.addAll(option.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final publishTextMessageJson = + StreamChannelPublishTextMessageJson.fromJson(rm); + return publishTextMessageJson.requestId; + } + + @override + Future publishBinaryMessage( + {required String topic, + required Uint8List message, + required int length, + required TopicMessageOptions option}) async { + final apiType = + '${isOverrideClassName ? className : 'StreamChannel'}_publishTopicMessage_a31773e'; + final param = createParams( + {'topic': topic, 'length': length, 'option': option.toJson()}); + final List buffers = []; + buffers.add(message); + buffers.addAll(option.collectBufferList()); + final callApiResult = await irisMethodChannel.invokeMethod( + IrisMethodCall(apiType, jsonEncode(param), buffers: buffers)); + if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); + } + final rm = callApiResult.data; + final result = rm['result']; + if (result < 0) { + throwExceptionHandler(code: result); + } + final publishBinaryMessageJson = + StreamChannelPublishBinaryMessageJson.fromJson(rm); + return publishBinaryMessageJson.requestId; + } +} diff --git a/lib/src/bindings/gen/binding_forward_export.dart b/lib/src/bindings/gen/binding_forward_export.dart new file mode 100644 index 0000000..8713c21 --- /dev/null +++ b/lib/src/bindings/gen/binding_forward_export.dart @@ -0,0 +1,21 @@ +export 'package:agora_rtm/src/binding_forward_export.dart' + hide RtmClient, RtmLock, RtmPresence, RtmStorage, StreamChannel; +export 'agora_rtm_client.dart'; +export 'agora_rtm_lock.dart'; +export 'agora_rtm_presence.dart'; +export 'agora_rtm_storage.dart'; +export 'agora_stream_channel.dart'; +export 'agora_rtm_client_impl.dart'; +export 'agora_rtm_client_event_impl.dart'; +export 'agora_rtm_lock_impl.dart'; +export 'agora_rtm_presence_impl.dart'; +export 'agora_rtm_storage_impl.dart'; +export 'agora_stream_channel_impl.dart'; +export 'dart:convert'; +export 'dart:typed_data'; +export 'package:json_annotation/json_annotation.dart'; +export 'package:flutter/foundation.dart'; +export 'package:agora_rtm/src/bindings/json_converters.dart'; +export 'call_api_event_handler_buffer_ext.dart'; +export 'event_handler_param_json.dart'; +export 'call_api_impl_params_json.dart'; diff --git a/lib/src/bindings/gen/call_api_event_handler_buffer_ext.dart b/lib/src/bindings/gen/call_api_event_handler_buffer_ext.dart new file mode 100644 index 0000000..e636d90 --- /dev/null +++ b/lib/src/bindings/gen/call_api_event_handler_buffer_ext.dart @@ -0,0 +1,403 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import, prefer_is_empty + +import 'package:agora_rtm/src/binding_forward_export.dart'; + +extension RtmLogConfigBufferExt on RtmLogConfig { + RtmLogConfig fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension UserListBufferExt on UserList { + UserList fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension PublisherInfoBufferExt on PublisherInfo { + PublisherInfo fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension TopicInfoBufferExt on TopicInfo { + TopicInfo fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension StateItemBufferExt on StateItem { + StateItem fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension LockDetailBufferExt on LockDetail { + LockDetail fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension UserStateBufferExt on UserState { + UserState fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension SubscribeOptionsBufferExt on SubscribeOptions { + SubscribeOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension ChannelInfoBufferExt on ChannelInfo { + ChannelInfo fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension PresenceOptionsBufferExt on PresenceOptions { + PresenceOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension PublishOptionsBufferExt on PublishOptions { + PublishOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension TopicMessageOptionsBufferExt on TopicMessageOptions { + TopicMessageOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension GetOnlineUsersOptionsBufferExt on GetOnlineUsersOptions { + GetOnlineUsersOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension RtmProxyConfigBufferExt on RtmProxyConfig { + RtmProxyConfig fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension RtmEncryptionConfigBufferExt on RtmEncryptionConfig { + RtmEncryptionConfig fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + Uint8List? encryptionSalt; + if (bufferList.length > 0) { + encryptionSalt = bufferList[0]; + } + return RtmEncryptionConfig( + encryptionMode: encryptionMode, + encryptionKey: encryptionKey, + encryptionSalt: encryptionSalt); + } + + List collectBufferList() { + final bufferList = []; + if (encryptionSalt != null) { + bufferList.add(encryptionSalt!); + } + return bufferList; + } +} + +extension RtmPrivateConfigBufferExt on RtmPrivateConfig { + RtmPrivateConfig fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension RtmConfigBufferExt on RtmConfig { + RtmConfig fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension LinkStateEventBufferExt on LinkStateEvent { + LinkStateEvent fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension MessageEventBufferExt on MessageEvent { + MessageEvent fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + Uint8List? message; + if (bufferList.length > 0) { + message = bufferList[0]; + } + return MessageEvent( + channelType: channelType, + messageType: messageType, + channelName: channelName, + channelTopic: channelTopic, + message: message, + messageLength: messageLength, + publisher: publisher, + customType: customType, + timestamp: timestamp); + } + + List collectBufferList() { + final bufferList = []; + if (message != null) { + bufferList.add(message!); + } + return bufferList; + } +} + +extension PresenceEventBufferExt on PresenceEvent { + PresenceEvent fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension IntervalInfoBufferExt on IntervalInfo { + IntervalInfo fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension SnapshotInfoBufferExt on SnapshotInfo { + SnapshotInfo fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension TopicEventBufferExt on TopicEvent { + TopicEvent fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension LockEventBufferExt on LockEvent { + LockEvent fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension StorageEventBufferExt on StorageEvent { + StorageEvent fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension MetadataOptionsBufferExt on MetadataOptions { + MetadataOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension MetadataItemBufferExt on MetadataItem { + MetadataItem fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension MetadataBufferExt on Metadata { + Metadata fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension JoinChannelOptionsBufferExt on JoinChannelOptions { + JoinChannelOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension JoinTopicOptionsBufferExt on JoinTopicOptions { + JoinTopicOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +extension TopicOptionsBufferExt on TopicOptions { + TopicOptions fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} diff --git a/lib/src/bindings/gen/call_api_impl_params_json.dart b/lib/src/bindings/gen/call_api_impl_params_json.dart new file mode 100644 index 0000000..7bbe8ff --- /dev/null +++ b/lib/src/bindings/gen/call_api_impl_params_json.dart @@ -0,0 +1,568 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import + +import 'binding_forward_export.dart'; +part 'call_api_impl_params_json.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmClientLoginJson { + const RtmClientLoginJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmClientLoginJson.fromJson(Map json) => + _$RtmClientLoginJsonFromJson(json); + + Map toJson() => _$RtmClientLoginJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmClientLogoutJson { + const RtmClientLogoutJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmClientLogoutJson.fromJson(Map json) => + _$RtmClientLogoutJsonFromJson(json); + + Map toJson() => _$RtmClientLogoutJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmClientRenewTokenJson { + const RtmClientRenewTokenJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmClientRenewTokenJson.fromJson(Map json) => + _$RtmClientRenewTokenJsonFromJson(json); + + Map toJson() => _$RtmClientRenewTokenJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmClientPublishJson { + const RtmClientPublishJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmClientPublishJson.fromJson(Map json) => + _$RtmClientPublishJsonFromJson(json); + + Map toJson() => _$RtmClientPublishJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmClientSubscribeJson { + const RtmClientSubscribeJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmClientSubscribeJson.fromJson(Map json) => + _$RtmClientSubscribeJsonFromJson(json); + + Map toJson() => _$RtmClientSubscribeJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmClientUnsubscribeJson { + const RtmClientUnsubscribeJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmClientUnsubscribeJson.fromJson(Map json) => + _$RtmClientUnsubscribeJsonFromJson(json); + + Map toJson() => _$RtmClientUnsubscribeJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmClientPublishBinaryMessageJson { + const RtmClientPublishBinaryMessageJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmClientPublishBinaryMessageJson.fromJson( + Map json) => + _$RtmClientPublishBinaryMessageJsonFromJson(json); + + Map toJson() => + _$RtmClientPublishBinaryMessageJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmLockSetLockJson { + const RtmLockSetLockJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmLockSetLockJson.fromJson(Map json) => + _$RtmLockSetLockJsonFromJson(json); + + Map toJson() => _$RtmLockSetLockJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmLockGetLocksJson { + const RtmLockGetLocksJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmLockGetLocksJson.fromJson(Map json) => + _$RtmLockGetLocksJsonFromJson(json); + + Map toJson() => _$RtmLockGetLocksJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmLockRemoveLockJson { + const RtmLockRemoveLockJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmLockRemoveLockJson.fromJson(Map json) => + _$RtmLockRemoveLockJsonFromJson(json); + + Map toJson() => _$RtmLockRemoveLockJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmLockAcquireLockJson { + const RtmLockAcquireLockJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmLockAcquireLockJson.fromJson(Map json) => + _$RtmLockAcquireLockJsonFromJson(json); + + Map toJson() => _$RtmLockAcquireLockJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmLockReleaseLockJson { + const RtmLockReleaseLockJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmLockReleaseLockJson.fromJson(Map json) => + _$RtmLockReleaseLockJsonFromJson(json); + + Map toJson() => _$RtmLockReleaseLockJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmLockRevokeLockJson { + const RtmLockRevokeLockJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmLockRevokeLockJson.fromJson(Map json) => + _$RtmLockRevokeLockJsonFromJson(json); + + Map toJson() => _$RtmLockRevokeLockJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPresenceWhoNowJson { + const RtmPresenceWhoNowJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmPresenceWhoNowJson.fromJson(Map json) => + _$RtmPresenceWhoNowJsonFromJson(json); + + Map toJson() => _$RtmPresenceWhoNowJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPresenceWhereNowJson { + const RtmPresenceWhereNowJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmPresenceWhereNowJson.fromJson(Map json) => + _$RtmPresenceWhereNowJsonFromJson(json); + + Map toJson() => _$RtmPresenceWhereNowJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPresenceSetStateJson { + const RtmPresenceSetStateJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmPresenceSetStateJson.fromJson(Map json) => + _$RtmPresenceSetStateJsonFromJson(json); + + Map toJson() => _$RtmPresenceSetStateJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPresenceRemoveStateJson { + const RtmPresenceRemoveStateJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmPresenceRemoveStateJson.fromJson(Map json) => + _$RtmPresenceRemoveStateJsonFromJson(json); + + Map toJson() => _$RtmPresenceRemoveStateJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPresenceGetStateJson { + const RtmPresenceGetStateJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmPresenceGetStateJson.fromJson(Map json) => + _$RtmPresenceGetStateJsonFromJson(json); + + Map toJson() => _$RtmPresenceGetStateJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPresenceGetOnlineUsersJson { + const RtmPresenceGetOnlineUsersJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmPresenceGetOnlineUsersJson.fromJson(Map json) => + _$RtmPresenceGetOnlineUsersJsonFromJson(json); + + Map toJson() => _$RtmPresenceGetOnlineUsersJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmPresenceGetUserChannelsJson { + const RtmPresenceGetUserChannelsJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmPresenceGetUserChannelsJson.fromJson(Map json) => + _$RtmPresenceGetUserChannelsJsonFromJson(json); + + Map toJson() => _$RtmPresenceGetUserChannelsJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageSetChannelMetadataJson { + const RtmStorageSetChannelMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageSetChannelMetadataJson.fromJson( + Map json) => + _$RtmStorageSetChannelMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageSetChannelMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageUpdateChannelMetadataJson { + const RtmStorageUpdateChannelMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageUpdateChannelMetadataJson.fromJson( + Map json) => + _$RtmStorageUpdateChannelMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageUpdateChannelMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageRemoveChannelMetadataJson { + const RtmStorageRemoveChannelMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageRemoveChannelMetadataJson.fromJson( + Map json) => + _$RtmStorageRemoveChannelMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageRemoveChannelMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageGetChannelMetadataJson { + const RtmStorageGetChannelMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageGetChannelMetadataJson.fromJson( + Map json) => + _$RtmStorageGetChannelMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageGetChannelMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageSetUserMetadataJson { + const RtmStorageSetUserMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageSetUserMetadataJson.fromJson(Map json) => + _$RtmStorageSetUserMetadataJsonFromJson(json); + + Map toJson() => _$RtmStorageSetUserMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageUpdateUserMetadataJson { + const RtmStorageUpdateUserMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageUpdateUserMetadataJson.fromJson( + Map json) => + _$RtmStorageUpdateUserMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageUpdateUserMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageRemoveUserMetadataJson { + const RtmStorageRemoveUserMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageRemoveUserMetadataJson.fromJson( + Map json) => + _$RtmStorageRemoveUserMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageRemoveUserMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageGetUserMetadataJson { + const RtmStorageGetUserMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageGetUserMetadataJson.fromJson(Map json) => + _$RtmStorageGetUserMetadataJsonFromJson(json); + + Map toJson() => _$RtmStorageGetUserMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageSubscribeUserMetadataJson { + const RtmStorageSubscribeUserMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageSubscribeUserMetadataJson.fromJson( + Map json) => + _$RtmStorageSubscribeUserMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageSubscribeUserMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmStorageUnsubscribeUserMetadataJson { + const RtmStorageUnsubscribeUserMetadataJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory RtmStorageUnsubscribeUserMetadataJson.fromJson( + Map json) => + _$RtmStorageUnsubscribeUserMetadataJsonFromJson(json); + + Map toJson() => + _$RtmStorageUnsubscribeUserMetadataJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelJoinJson { + const StreamChannelJoinJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelJoinJson.fromJson(Map json) => + _$StreamChannelJoinJsonFromJson(json); + + Map toJson() => _$StreamChannelJoinJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelRenewTokenJson { + const StreamChannelRenewTokenJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelRenewTokenJson.fromJson(Map json) => + _$StreamChannelRenewTokenJsonFromJson(json); + + Map toJson() => _$StreamChannelRenewTokenJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelLeaveJson { + const StreamChannelLeaveJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelLeaveJson.fromJson(Map json) => + _$StreamChannelLeaveJsonFromJson(json); + + Map toJson() => _$StreamChannelLeaveJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelJoinTopicJson { + const StreamChannelJoinTopicJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelJoinTopicJson.fromJson(Map json) => + _$StreamChannelJoinTopicJsonFromJson(json); + + Map toJson() => _$StreamChannelJoinTopicJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelPublishTopicMessageJson { + const StreamChannelPublishTopicMessageJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelPublishTopicMessageJson.fromJson( + Map json) => + _$StreamChannelPublishTopicMessageJsonFromJson(json); + + Map toJson() => + _$StreamChannelPublishTopicMessageJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelLeaveTopicJson { + const StreamChannelLeaveTopicJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelLeaveTopicJson.fromJson(Map json) => + _$StreamChannelLeaveTopicJsonFromJson(json); + + Map toJson() => _$StreamChannelLeaveTopicJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelSubscribeTopicJson { + const StreamChannelSubscribeTopicJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelSubscribeTopicJson.fromJson(Map json) => + _$StreamChannelSubscribeTopicJsonFromJson(json); + + Map toJson() => + _$StreamChannelSubscribeTopicJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelUnsubscribeTopicJson { + const StreamChannelUnsubscribeTopicJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelUnsubscribeTopicJson.fromJson( + Map json) => + _$StreamChannelUnsubscribeTopicJsonFromJson(json); + + Map toJson() => + _$StreamChannelUnsubscribeTopicJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelGetSubscribedUserListJson { + const StreamChannelGetSubscribedUserListJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelGetSubscribedUserListJson.fromJson( + Map json) => + _$StreamChannelGetSubscribedUserListJsonFromJson(json); + + Map toJson() => + _$StreamChannelGetSubscribedUserListJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelPublishTextMessageJson { + const StreamChannelPublishTextMessageJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelPublishTextMessageJson.fromJson( + Map json) => + _$StreamChannelPublishTextMessageJsonFromJson(json); + + Map toJson() => + _$StreamChannelPublishTextMessageJsonToJson(this); +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class StreamChannelPublishBinaryMessageJson { + const StreamChannelPublishBinaryMessageJson(this.requestId); + + @JsonKey(name: 'requestId') + final int requestId; + + factory StreamChannelPublishBinaryMessageJson.fromJson( + Map json) => + _$StreamChannelPublishBinaryMessageJsonFromJson(json); + + Map toJson() => + _$StreamChannelPublishBinaryMessageJsonToJson(this); +} diff --git a/lib/src/bindings/gen/call_api_impl_params_json.g.dart b/lib/src/bindings/gen/call_api_impl_params_json.g.dart new file mode 100644 index 0000000..871c5b8 --- /dev/null +++ b/lib/src/bindings/gen/call_api_impl_params_json.g.dart @@ -0,0 +1,496 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'call_api_impl_params_json.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RtmClientLoginJson _$RtmClientLoginJsonFromJson(Map json) => + RtmClientLoginJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmClientLoginJsonToJson(RtmClientLoginJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmClientLogoutJson _$RtmClientLogoutJsonFromJson(Map json) => + RtmClientLogoutJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmClientLogoutJsonToJson( + RtmClientLogoutJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmClientRenewTokenJson _$RtmClientRenewTokenJsonFromJson( + Map json) => + RtmClientRenewTokenJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmClientRenewTokenJsonToJson( + RtmClientRenewTokenJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmClientPublishJson _$RtmClientPublishJsonFromJson( + Map json) => + RtmClientPublishJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmClientPublishJsonToJson( + RtmClientPublishJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmClientSubscribeJson _$RtmClientSubscribeJsonFromJson( + Map json) => + RtmClientSubscribeJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmClientSubscribeJsonToJson( + RtmClientSubscribeJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmClientUnsubscribeJson _$RtmClientUnsubscribeJsonFromJson( + Map json) => + RtmClientUnsubscribeJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmClientUnsubscribeJsonToJson( + RtmClientUnsubscribeJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmClientPublishBinaryMessageJson _$RtmClientPublishBinaryMessageJsonFromJson( + Map json) => + RtmClientPublishBinaryMessageJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmClientPublishBinaryMessageJsonToJson( + RtmClientPublishBinaryMessageJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmLockSetLockJson _$RtmLockSetLockJsonFromJson(Map json) => + RtmLockSetLockJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmLockSetLockJsonToJson(RtmLockSetLockJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmLockGetLocksJson _$RtmLockGetLocksJsonFromJson(Map json) => + RtmLockGetLocksJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmLockGetLocksJsonToJson( + RtmLockGetLocksJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmLockRemoveLockJson _$RtmLockRemoveLockJsonFromJson( + Map json) => + RtmLockRemoveLockJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmLockRemoveLockJsonToJson( + RtmLockRemoveLockJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmLockAcquireLockJson _$RtmLockAcquireLockJsonFromJson( + Map json) => + RtmLockAcquireLockJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmLockAcquireLockJsonToJson( + RtmLockAcquireLockJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmLockReleaseLockJson _$RtmLockReleaseLockJsonFromJson( + Map json) => + RtmLockReleaseLockJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmLockReleaseLockJsonToJson( + RtmLockReleaseLockJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmLockRevokeLockJson _$RtmLockRevokeLockJsonFromJson( + Map json) => + RtmLockRevokeLockJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmLockRevokeLockJsonToJson( + RtmLockRevokeLockJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmPresenceWhoNowJson _$RtmPresenceWhoNowJsonFromJson( + Map json) => + RtmPresenceWhoNowJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmPresenceWhoNowJsonToJson( + RtmPresenceWhoNowJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmPresenceWhereNowJson _$RtmPresenceWhereNowJsonFromJson( + Map json) => + RtmPresenceWhereNowJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmPresenceWhereNowJsonToJson( + RtmPresenceWhereNowJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmPresenceSetStateJson _$RtmPresenceSetStateJsonFromJson( + Map json) => + RtmPresenceSetStateJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmPresenceSetStateJsonToJson( + RtmPresenceSetStateJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmPresenceRemoveStateJson _$RtmPresenceRemoveStateJsonFromJson( + Map json) => + RtmPresenceRemoveStateJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmPresenceRemoveStateJsonToJson( + RtmPresenceRemoveStateJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmPresenceGetStateJson _$RtmPresenceGetStateJsonFromJson( + Map json) => + RtmPresenceGetStateJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmPresenceGetStateJsonToJson( + RtmPresenceGetStateJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmPresenceGetOnlineUsersJson _$RtmPresenceGetOnlineUsersJsonFromJson( + Map json) => + RtmPresenceGetOnlineUsersJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmPresenceGetOnlineUsersJsonToJson( + RtmPresenceGetOnlineUsersJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmPresenceGetUserChannelsJson _$RtmPresenceGetUserChannelsJsonFromJson( + Map json) => + RtmPresenceGetUserChannelsJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmPresenceGetUserChannelsJsonToJson( + RtmPresenceGetUserChannelsJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageSetChannelMetadataJson _$RtmStorageSetChannelMetadataJsonFromJson( + Map json) => + RtmStorageSetChannelMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageSetChannelMetadataJsonToJson( + RtmStorageSetChannelMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageUpdateChannelMetadataJson + _$RtmStorageUpdateChannelMetadataJsonFromJson(Map json) => + RtmStorageUpdateChannelMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageUpdateChannelMetadataJsonToJson( + RtmStorageUpdateChannelMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageRemoveChannelMetadataJson + _$RtmStorageRemoveChannelMetadataJsonFromJson(Map json) => + RtmStorageRemoveChannelMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageRemoveChannelMetadataJsonToJson( + RtmStorageRemoveChannelMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageGetChannelMetadataJson _$RtmStorageGetChannelMetadataJsonFromJson( + Map json) => + RtmStorageGetChannelMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageGetChannelMetadataJsonToJson( + RtmStorageGetChannelMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageSetUserMetadataJson _$RtmStorageSetUserMetadataJsonFromJson( + Map json) => + RtmStorageSetUserMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageSetUserMetadataJsonToJson( + RtmStorageSetUserMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageUpdateUserMetadataJson _$RtmStorageUpdateUserMetadataJsonFromJson( + Map json) => + RtmStorageUpdateUserMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageUpdateUserMetadataJsonToJson( + RtmStorageUpdateUserMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageRemoveUserMetadataJson _$RtmStorageRemoveUserMetadataJsonFromJson( + Map json) => + RtmStorageRemoveUserMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageRemoveUserMetadataJsonToJson( + RtmStorageRemoveUserMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageGetUserMetadataJson _$RtmStorageGetUserMetadataJsonFromJson( + Map json) => + RtmStorageGetUserMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageGetUserMetadataJsonToJson( + RtmStorageGetUserMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageSubscribeUserMetadataJson + _$RtmStorageSubscribeUserMetadataJsonFromJson(Map json) => + RtmStorageSubscribeUserMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageSubscribeUserMetadataJsonToJson( + RtmStorageSubscribeUserMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +RtmStorageUnsubscribeUserMetadataJson + _$RtmStorageUnsubscribeUserMetadataJsonFromJson( + Map json) => + RtmStorageUnsubscribeUserMetadataJson( + (json['requestId'] as num).toInt(), + ); + +Map _$RtmStorageUnsubscribeUserMetadataJsonToJson( + RtmStorageUnsubscribeUserMetadataJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelJoinJson _$StreamChannelJoinJsonFromJson( + Map json) => + StreamChannelJoinJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelJoinJsonToJson( + StreamChannelJoinJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelRenewTokenJson _$StreamChannelRenewTokenJsonFromJson( + Map json) => + StreamChannelRenewTokenJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelRenewTokenJsonToJson( + StreamChannelRenewTokenJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelLeaveJson _$StreamChannelLeaveJsonFromJson( + Map json) => + StreamChannelLeaveJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelLeaveJsonToJson( + StreamChannelLeaveJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelJoinTopicJson _$StreamChannelJoinTopicJsonFromJson( + Map json) => + StreamChannelJoinTopicJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelJoinTopicJsonToJson( + StreamChannelJoinTopicJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelPublishTopicMessageJson + _$StreamChannelPublishTopicMessageJsonFromJson(Map json) => + StreamChannelPublishTopicMessageJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelPublishTopicMessageJsonToJson( + StreamChannelPublishTopicMessageJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelLeaveTopicJson _$StreamChannelLeaveTopicJsonFromJson( + Map json) => + StreamChannelLeaveTopicJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelLeaveTopicJsonToJson( + StreamChannelLeaveTopicJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelSubscribeTopicJson _$StreamChannelSubscribeTopicJsonFromJson( + Map json) => + StreamChannelSubscribeTopicJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelSubscribeTopicJsonToJson( + StreamChannelSubscribeTopicJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelUnsubscribeTopicJson _$StreamChannelUnsubscribeTopicJsonFromJson( + Map json) => + StreamChannelUnsubscribeTopicJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelUnsubscribeTopicJsonToJson( + StreamChannelUnsubscribeTopicJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelGetSubscribedUserListJson + _$StreamChannelGetSubscribedUserListJsonFromJson( + Map json) => + StreamChannelGetSubscribedUserListJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelGetSubscribedUserListJsonToJson( + StreamChannelGetSubscribedUserListJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelPublishTextMessageJson + _$StreamChannelPublishTextMessageJsonFromJson(Map json) => + StreamChannelPublishTextMessageJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelPublishTextMessageJsonToJson( + StreamChannelPublishTextMessageJson instance) => + { + 'requestId': instance.requestId, + }; + +StreamChannelPublishBinaryMessageJson + _$StreamChannelPublishBinaryMessageJsonFromJson( + Map json) => + StreamChannelPublishBinaryMessageJson( + (json['requestId'] as num).toInt(), + ); + +Map _$StreamChannelPublishBinaryMessageJsonToJson( + StreamChannelPublishBinaryMessageJson instance) => + { + 'requestId': instance.requestId, + }; diff --git a/lib/src/bindings/gen/event_handler_param_json.dart b/lib/src/bindings/gen/event_handler_param_json.dart new file mode 100644 index 0000000..66ed267 --- /dev/null +++ b/lib/src/bindings/gen/event_handler_param_json.dart @@ -0,0 +1,1725 @@ +/// GENERATED BY terra, DO NOT MODIFY BY HAND. + +// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import, prefer_is_empty + +import 'package:agora_rtm/src/binding_forward_export.dart'; +part 'event_handler_param_json.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnLinkStateEventJson { + const RtmEventHandlerOnLinkStateEventJson({this.event}); + + @JsonKey(name: 'event') + final LinkStateEvent? event; + + factory RtmEventHandlerOnLinkStateEventJson.fromJson( + Map json) => + _$RtmEventHandlerOnLinkStateEventJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnLinkStateEventJsonToJson(this); +} + +extension RtmEventHandlerOnLinkStateEventJsonBufferExt + on RtmEventHandlerOnLinkStateEventJson { + RtmEventHandlerOnLinkStateEventJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnMessageEventJson { + const RtmEventHandlerOnMessageEventJson({this.event}); + + @JsonKey(name: 'event') + final MessageEvent? event; + + factory RtmEventHandlerOnMessageEventJson.fromJson( + Map json) => + _$RtmEventHandlerOnMessageEventJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnMessageEventJsonToJson(this); +} + +extension RtmEventHandlerOnMessageEventJsonBufferExt + on RtmEventHandlerOnMessageEventJson { + RtmEventHandlerOnMessageEventJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnPresenceEventJson { + const RtmEventHandlerOnPresenceEventJson({this.event}); + + @JsonKey(name: 'event') + final PresenceEvent? event; + + factory RtmEventHandlerOnPresenceEventJson.fromJson( + Map json) => + _$RtmEventHandlerOnPresenceEventJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnPresenceEventJsonToJson(this); +} + +extension RtmEventHandlerOnPresenceEventJsonBufferExt + on RtmEventHandlerOnPresenceEventJson { + RtmEventHandlerOnPresenceEventJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnTopicEventJson { + const RtmEventHandlerOnTopicEventJson({this.event}); + + @JsonKey(name: 'event') + final TopicEvent? event; + + factory RtmEventHandlerOnTopicEventJson.fromJson(Map json) => + _$RtmEventHandlerOnTopicEventJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnTopicEventJsonToJson(this); +} + +extension RtmEventHandlerOnTopicEventJsonBufferExt + on RtmEventHandlerOnTopicEventJson { + RtmEventHandlerOnTopicEventJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnLockEventJson { + const RtmEventHandlerOnLockEventJson({this.event}); + + @JsonKey(name: 'event') + final LockEvent? event; + + factory RtmEventHandlerOnLockEventJson.fromJson(Map json) => + _$RtmEventHandlerOnLockEventJsonFromJson(json); + + Map toJson() => _$RtmEventHandlerOnLockEventJsonToJson(this); +} + +extension RtmEventHandlerOnLockEventJsonBufferExt + on RtmEventHandlerOnLockEventJson { + RtmEventHandlerOnLockEventJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnStorageEventJson { + const RtmEventHandlerOnStorageEventJson({this.event}); + + @JsonKey(name: 'event') + final StorageEvent? event; + + factory RtmEventHandlerOnStorageEventJson.fromJson( + Map json) => + _$RtmEventHandlerOnStorageEventJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnStorageEventJsonToJson(this); +} + +extension RtmEventHandlerOnStorageEventJsonBufferExt + on RtmEventHandlerOnStorageEventJson { + RtmEventHandlerOnStorageEventJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnJoinResultJson { + const RtmEventHandlerOnJoinResultJson( + {this.requestId, this.channelName, this.userId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnJoinResultJson.fromJson(Map json) => + _$RtmEventHandlerOnJoinResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnJoinResultJsonToJson(this); +} + +extension RtmEventHandlerOnJoinResultJsonBufferExt + on RtmEventHandlerOnJoinResultJson { + RtmEventHandlerOnJoinResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnLeaveResultJson { + const RtmEventHandlerOnLeaveResultJson( + {this.requestId, this.channelName, this.userId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnLeaveResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnLeaveResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnLeaveResultJsonToJson(this); +} + +extension RtmEventHandlerOnLeaveResultJsonBufferExt + on RtmEventHandlerOnLeaveResultJson { + RtmEventHandlerOnLeaveResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnPublishTopicMessageResultJson { + const RtmEventHandlerOnPublishTopicMessageResultJson( + {this.requestId, this.channelName, this.topic, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'topic') + final String? topic; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnPublishTopicMessageResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnPublishTopicMessageResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnPublishTopicMessageResultJsonToJson(this); +} + +extension RtmEventHandlerOnPublishTopicMessageResultJsonBufferExt + on RtmEventHandlerOnPublishTopicMessageResultJson { + RtmEventHandlerOnPublishTopicMessageResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnJoinTopicResultJson { + const RtmEventHandlerOnJoinTopicResultJson( + {this.requestId, + this.channelName, + this.userId, + this.topic, + this.meta, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'topic') + final String? topic; + + @JsonKey(name: 'meta') + final String? meta; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnJoinTopicResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnJoinTopicResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnJoinTopicResultJsonToJson(this); +} + +extension RtmEventHandlerOnJoinTopicResultJsonBufferExt + on RtmEventHandlerOnJoinTopicResultJson { + RtmEventHandlerOnJoinTopicResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnLeaveTopicResultJson { + const RtmEventHandlerOnLeaveTopicResultJson( + {this.requestId, + this.channelName, + this.userId, + this.topic, + this.meta, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'topic') + final String? topic; + + @JsonKey(name: 'meta') + final String? meta; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnLeaveTopicResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnLeaveTopicResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnLeaveTopicResultJsonToJson(this); +} + +extension RtmEventHandlerOnLeaveTopicResultJsonBufferExt + on RtmEventHandlerOnLeaveTopicResultJson { + RtmEventHandlerOnLeaveTopicResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnSubscribeTopicResultJson { + const RtmEventHandlerOnSubscribeTopicResultJson( + {this.requestId, + this.channelName, + this.userId, + this.topic, + this.succeedUsers, + this.failedUsers, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'topic') + final String? topic; + + @JsonKey(name: 'succeedUsers') + final UserList? succeedUsers; + + @JsonKey(name: 'failedUsers') + final UserList? failedUsers; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnSubscribeTopicResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnSubscribeTopicResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnSubscribeTopicResultJsonToJson(this); +} + +extension RtmEventHandlerOnSubscribeTopicResultJsonBufferExt + on RtmEventHandlerOnSubscribeTopicResultJson { + RtmEventHandlerOnSubscribeTopicResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnUnsubscribeTopicResultJson { + const RtmEventHandlerOnUnsubscribeTopicResultJson( + {this.requestId, this.channelName, this.topic, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'topic') + final String? topic; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnUnsubscribeTopicResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnUnsubscribeTopicResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnUnsubscribeTopicResultJsonToJson(this); +} + +extension RtmEventHandlerOnUnsubscribeTopicResultJsonBufferExt + on RtmEventHandlerOnUnsubscribeTopicResultJson { + RtmEventHandlerOnUnsubscribeTopicResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnGetSubscribedUserListResultJson { + const RtmEventHandlerOnGetSubscribedUserListResultJson( + {this.requestId, + this.channelName, + this.topic, + this.users, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'topic') + final String? topic; + + @JsonKey(name: 'users') + final UserList? users; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnGetSubscribedUserListResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnGetSubscribedUserListResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnGetSubscribedUserListResultJsonToJson(this); +} + +extension RtmEventHandlerOnGetSubscribedUserListResultJsonBufferExt + on RtmEventHandlerOnGetSubscribedUserListResultJson { + RtmEventHandlerOnGetSubscribedUserListResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnConnectionStateChangedJson { + const RtmEventHandlerOnConnectionStateChangedJson( + {this.channelName, this.state, this.reason}); + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'state') + final RtmConnectionState? state; + + @JsonKey(name: 'reason') + final RtmConnectionChangeReason? reason; + + factory RtmEventHandlerOnConnectionStateChangedJson.fromJson( + Map json) => + _$RtmEventHandlerOnConnectionStateChangedJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnConnectionStateChangedJsonToJson(this); +} + +extension RtmEventHandlerOnConnectionStateChangedJsonBufferExt + on RtmEventHandlerOnConnectionStateChangedJson { + RtmEventHandlerOnConnectionStateChangedJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnTokenPrivilegeWillExpireJson { + const RtmEventHandlerOnTokenPrivilegeWillExpireJson({this.channelName}); + + @JsonKey(name: 'channelName') + final String? channelName; + + factory RtmEventHandlerOnTokenPrivilegeWillExpireJson.fromJson( + Map json) => + _$RtmEventHandlerOnTokenPrivilegeWillExpireJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnTokenPrivilegeWillExpireJsonToJson(this); +} + +extension RtmEventHandlerOnTokenPrivilegeWillExpireJsonBufferExt + on RtmEventHandlerOnTokenPrivilegeWillExpireJson { + RtmEventHandlerOnTokenPrivilegeWillExpireJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnSubscribeResultJson { + const RtmEventHandlerOnSubscribeResultJson( + {this.requestId, this.channelName, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnSubscribeResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnSubscribeResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnSubscribeResultJsonToJson(this); +} + +extension RtmEventHandlerOnSubscribeResultJsonBufferExt + on RtmEventHandlerOnSubscribeResultJson { + RtmEventHandlerOnSubscribeResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnUnsubscribeResultJson { + const RtmEventHandlerOnUnsubscribeResultJson( + {this.requestId, this.channelName, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnUnsubscribeResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnUnsubscribeResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnUnsubscribeResultJsonToJson(this); +} + +extension RtmEventHandlerOnUnsubscribeResultJsonBufferExt + on RtmEventHandlerOnUnsubscribeResultJson { + RtmEventHandlerOnUnsubscribeResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnPublishResultJson { + const RtmEventHandlerOnPublishResultJson({this.requestId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnPublishResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnPublishResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnPublishResultJsonToJson(this); +} + +extension RtmEventHandlerOnPublishResultJsonBufferExt + on RtmEventHandlerOnPublishResultJson { + RtmEventHandlerOnPublishResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnLoginResultJson { + const RtmEventHandlerOnLoginResultJson({this.requestId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnLoginResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnLoginResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnLoginResultJsonToJson(this); +} + +extension RtmEventHandlerOnLoginResultJsonBufferExt + on RtmEventHandlerOnLoginResultJson { + RtmEventHandlerOnLoginResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnLogoutResultJson { + const RtmEventHandlerOnLogoutResultJson({this.requestId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnLogoutResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnLogoutResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnLogoutResultJsonToJson(this); +} + +extension RtmEventHandlerOnLogoutResultJsonBufferExt + on RtmEventHandlerOnLogoutResultJson { + RtmEventHandlerOnLogoutResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnRenewTokenResultJson { + const RtmEventHandlerOnRenewTokenResultJson( + {this.requestId, this.serverType, this.channelName, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'serverType') + final RtmServiceType? serverType; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnRenewTokenResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnRenewTokenResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnRenewTokenResultJsonToJson(this); +} + +extension RtmEventHandlerOnRenewTokenResultJsonBufferExt + on RtmEventHandlerOnRenewTokenResultJson { + RtmEventHandlerOnRenewTokenResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnSetChannelMetadataResultJson { + const RtmEventHandlerOnSetChannelMetadataResultJson( + {this.requestId, this.channelName, this.channelType, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnSetChannelMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnSetChannelMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnSetChannelMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnSetChannelMetadataResultJsonBufferExt + on RtmEventHandlerOnSetChannelMetadataResultJson { + RtmEventHandlerOnSetChannelMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnUpdateChannelMetadataResultJson { + const RtmEventHandlerOnUpdateChannelMetadataResultJson( + {this.requestId, this.channelName, this.channelType, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnUpdateChannelMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnUpdateChannelMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnUpdateChannelMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnUpdateChannelMetadataResultJsonBufferExt + on RtmEventHandlerOnUpdateChannelMetadataResultJson { + RtmEventHandlerOnUpdateChannelMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnRemoveChannelMetadataResultJson { + const RtmEventHandlerOnRemoveChannelMetadataResultJson( + {this.requestId, this.channelName, this.channelType, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnRemoveChannelMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnRemoveChannelMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnRemoveChannelMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnRemoveChannelMetadataResultJsonBufferExt + on RtmEventHandlerOnRemoveChannelMetadataResultJson { + RtmEventHandlerOnRemoveChannelMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnGetChannelMetadataResultJson { + const RtmEventHandlerOnGetChannelMetadataResultJson( + {this.requestId, + this.channelName, + this.channelType, + this.data, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'data') + final Metadata? data; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnGetChannelMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnGetChannelMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnGetChannelMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnGetChannelMetadataResultJsonBufferExt + on RtmEventHandlerOnGetChannelMetadataResultJson { + RtmEventHandlerOnGetChannelMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnSetUserMetadataResultJson { + const RtmEventHandlerOnSetUserMetadataResultJson( + {this.requestId, this.userId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnSetUserMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnSetUserMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnSetUserMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnSetUserMetadataResultJsonBufferExt + on RtmEventHandlerOnSetUserMetadataResultJson { + RtmEventHandlerOnSetUserMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnUpdateUserMetadataResultJson { + const RtmEventHandlerOnUpdateUserMetadataResultJson( + {this.requestId, this.userId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnUpdateUserMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnUpdateUserMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnUpdateUserMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnUpdateUserMetadataResultJsonBufferExt + on RtmEventHandlerOnUpdateUserMetadataResultJson { + RtmEventHandlerOnUpdateUserMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnRemoveUserMetadataResultJson { + const RtmEventHandlerOnRemoveUserMetadataResultJson( + {this.requestId, this.userId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnRemoveUserMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnRemoveUserMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnRemoveUserMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnRemoveUserMetadataResultJsonBufferExt + on RtmEventHandlerOnRemoveUserMetadataResultJson { + RtmEventHandlerOnRemoveUserMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnGetUserMetadataResultJson { + const RtmEventHandlerOnGetUserMetadataResultJson( + {this.requestId, this.userId, this.data, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'data') + final Metadata? data; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnGetUserMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnGetUserMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnGetUserMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnGetUserMetadataResultJsonBufferExt + on RtmEventHandlerOnGetUserMetadataResultJson { + RtmEventHandlerOnGetUserMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnSubscribeUserMetadataResultJson { + const RtmEventHandlerOnSubscribeUserMetadataResultJson( + {this.requestId, this.userId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnSubscribeUserMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnSubscribeUserMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnSubscribeUserMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnSubscribeUserMetadataResultJsonBufferExt + on RtmEventHandlerOnSubscribeUserMetadataResultJson { + RtmEventHandlerOnSubscribeUserMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnUnsubscribeUserMetadataResultJson { + const RtmEventHandlerOnUnsubscribeUserMetadataResultJson( + {this.requestId, this.userId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userId') + final String? userId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnUnsubscribeUserMetadataResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnUnsubscribeUserMetadataResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnUnsubscribeUserMetadataResultJsonToJson(this); +} + +extension RtmEventHandlerOnUnsubscribeUserMetadataResultJsonBufferExt + on RtmEventHandlerOnUnsubscribeUserMetadataResultJson { + RtmEventHandlerOnUnsubscribeUserMetadataResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnSetLockResultJson { + const RtmEventHandlerOnSetLockResultJson( + {this.requestId, + this.channelName, + this.channelType, + this.lockName, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'lockName') + final String? lockName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnSetLockResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnSetLockResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnSetLockResultJsonToJson(this); +} + +extension RtmEventHandlerOnSetLockResultJsonBufferExt + on RtmEventHandlerOnSetLockResultJson { + RtmEventHandlerOnSetLockResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnRemoveLockResultJson { + const RtmEventHandlerOnRemoveLockResultJson( + {this.requestId, + this.channelName, + this.channelType, + this.lockName, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'lockName') + final String? lockName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnRemoveLockResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnRemoveLockResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnRemoveLockResultJsonToJson(this); +} + +extension RtmEventHandlerOnRemoveLockResultJsonBufferExt + on RtmEventHandlerOnRemoveLockResultJson { + RtmEventHandlerOnRemoveLockResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnReleaseLockResultJson { + const RtmEventHandlerOnReleaseLockResultJson( + {this.requestId, + this.channelName, + this.channelType, + this.lockName, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'lockName') + final String? lockName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnReleaseLockResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnReleaseLockResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnReleaseLockResultJsonToJson(this); +} + +extension RtmEventHandlerOnReleaseLockResultJsonBufferExt + on RtmEventHandlerOnReleaseLockResultJson { + RtmEventHandlerOnReleaseLockResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnAcquireLockResultJson { + const RtmEventHandlerOnAcquireLockResultJson( + {this.requestId, + this.channelName, + this.channelType, + this.lockName, + this.errorCode, + this.errorDetails}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'lockName') + final String? lockName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + @JsonKey(name: 'errorDetails') + final String? errorDetails; + + factory RtmEventHandlerOnAcquireLockResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnAcquireLockResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnAcquireLockResultJsonToJson(this); +} + +extension RtmEventHandlerOnAcquireLockResultJsonBufferExt + on RtmEventHandlerOnAcquireLockResultJson { + RtmEventHandlerOnAcquireLockResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnRevokeLockResultJson { + const RtmEventHandlerOnRevokeLockResultJson( + {this.requestId, + this.channelName, + this.channelType, + this.lockName, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'lockName') + final String? lockName; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnRevokeLockResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnRevokeLockResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnRevokeLockResultJsonToJson(this); +} + +extension RtmEventHandlerOnRevokeLockResultJsonBufferExt + on RtmEventHandlerOnRevokeLockResultJson { + RtmEventHandlerOnRevokeLockResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnGetLocksResultJson { + const RtmEventHandlerOnGetLocksResultJson( + {this.requestId, + this.channelName, + this.channelType, + this.lockDetailList, + this.count, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channelName') + final String? channelName; + + @JsonKey(name: 'channelType') + final RtmChannelType? channelType; + + @JsonKey(name: 'lockDetailList') + final List? lockDetailList; + + @JsonKey(name: 'count') + final int? count; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnGetLocksResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnGetLocksResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnGetLocksResultJsonToJson(this); +} + +extension RtmEventHandlerOnGetLocksResultJsonBufferExt + on RtmEventHandlerOnGetLocksResultJson { + RtmEventHandlerOnGetLocksResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnWhoNowResultJson { + const RtmEventHandlerOnWhoNowResultJson( + {this.requestId, + this.userStateList, + this.count, + this.nextPage, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userStateList') + final List? userStateList; + + @JsonKey(name: 'count') + final int? count; + + @JsonKey(name: 'nextPage') + final String? nextPage; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnWhoNowResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnWhoNowResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnWhoNowResultJsonToJson(this); +} + +extension RtmEventHandlerOnWhoNowResultJsonBufferExt + on RtmEventHandlerOnWhoNowResultJson { + RtmEventHandlerOnWhoNowResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnGetOnlineUsersResultJson { + const RtmEventHandlerOnGetOnlineUsersResultJson( + {this.requestId, + this.userStateList, + this.count, + this.nextPage, + this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'userStateList') + final List? userStateList; + + @JsonKey(name: 'count') + final int? count; + + @JsonKey(name: 'nextPage') + final String? nextPage; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnGetOnlineUsersResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnGetOnlineUsersResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnGetOnlineUsersResultJsonToJson(this); +} + +extension RtmEventHandlerOnGetOnlineUsersResultJsonBufferExt + on RtmEventHandlerOnGetOnlineUsersResultJson { + RtmEventHandlerOnGetOnlineUsersResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnWhereNowResultJson { + const RtmEventHandlerOnWhereNowResultJson( + {this.requestId, this.channels, this.count, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channels') + final List? channels; + + @JsonKey(name: 'count') + final int? count; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnWhereNowResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnWhereNowResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnWhereNowResultJsonToJson(this); +} + +extension RtmEventHandlerOnWhereNowResultJsonBufferExt + on RtmEventHandlerOnWhereNowResultJson { + RtmEventHandlerOnWhereNowResultJson fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnGetUserChannelsResultJson { + const RtmEventHandlerOnGetUserChannelsResultJson( + {this.requestId, this.channels, this.count, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'channels') + final ChannelInfo? channels; + + @JsonKey(name: 'count') + final int? count; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnGetUserChannelsResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnGetUserChannelsResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnGetUserChannelsResultJsonToJson(this); +} + +extension RtmEventHandlerOnGetUserChannelsResultJsonBufferExt + on RtmEventHandlerOnGetUserChannelsResultJson { + RtmEventHandlerOnGetUserChannelsResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnPresenceSetStateResultJson { + const RtmEventHandlerOnPresenceSetStateResultJson( + {this.requestId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnPresenceSetStateResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnPresenceSetStateResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnPresenceSetStateResultJsonToJson(this); +} + +extension RtmEventHandlerOnPresenceSetStateResultJsonBufferExt + on RtmEventHandlerOnPresenceSetStateResultJson { + RtmEventHandlerOnPresenceSetStateResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnPresenceRemoveStateResultJson { + const RtmEventHandlerOnPresenceRemoveStateResultJson( + {this.requestId, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnPresenceRemoveStateResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnPresenceRemoveStateResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnPresenceRemoveStateResultJsonToJson(this); +} + +extension RtmEventHandlerOnPresenceRemoveStateResultJsonBufferExt + on RtmEventHandlerOnPresenceRemoveStateResultJson { + RtmEventHandlerOnPresenceRemoveStateResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class RtmEventHandlerOnPresenceGetStateResultJson { + const RtmEventHandlerOnPresenceGetStateResultJson( + {this.requestId, this.state, this.errorCode}); + + @JsonKey(name: 'requestId') + final int? requestId; + + @JsonKey(name: 'state') + final UserState? state; + + @JsonKey(name: 'errorCode') + final RtmErrorCode? errorCode; + + factory RtmEventHandlerOnPresenceGetStateResultJson.fromJson( + Map json) => + _$RtmEventHandlerOnPresenceGetStateResultJsonFromJson(json); + + Map toJson() => + _$RtmEventHandlerOnPresenceGetStateResultJsonToJson(this); +} + +extension RtmEventHandlerOnPresenceGetStateResultJsonBufferExt + on RtmEventHandlerOnPresenceGetStateResultJson { + RtmEventHandlerOnPresenceGetStateResultJson fillBuffers( + List bufferList) { + if (bufferList.isEmpty) return this; + return this; + } + + List collectBufferList() { + final bufferList = []; + return bufferList; + } +} diff --git a/lib/src/bindings/gen/event_handler_param_json.g.dart b/lib/src/bindings/gen/event_handler_param_json.g.dart new file mode 100644 index 0000000..0c04140 --- /dev/null +++ b/lib/src/bindings/gen/event_handler_param_json.g.dart @@ -0,0 +1,1399 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'event_handler_param_json.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RtmEventHandlerOnLinkStateEventJson + _$RtmEventHandlerOnLinkStateEventJsonFromJson(Map json) => + RtmEventHandlerOnLinkStateEventJson( + event: json['event'] == null + ? null + : LinkStateEvent.fromJson(json['event'] as Map), + ); + +Map _$RtmEventHandlerOnLinkStateEventJsonToJson( + RtmEventHandlerOnLinkStateEventJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('event', instance.event?.toJson()); + return val; +} + +RtmEventHandlerOnMessageEventJson _$RtmEventHandlerOnMessageEventJsonFromJson( + Map json) => + RtmEventHandlerOnMessageEventJson( + event: json['event'] == null + ? null + : MessageEvent.fromJson(json['event'] as Map), + ); + +Map _$RtmEventHandlerOnMessageEventJsonToJson( + RtmEventHandlerOnMessageEventJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('event', instance.event?.toJson()); + return val; +} + +RtmEventHandlerOnPresenceEventJson _$RtmEventHandlerOnPresenceEventJsonFromJson( + Map json) => + RtmEventHandlerOnPresenceEventJson( + event: json['event'] == null + ? null + : PresenceEvent.fromJson(json['event'] as Map), + ); + +Map _$RtmEventHandlerOnPresenceEventJsonToJson( + RtmEventHandlerOnPresenceEventJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('event', instance.event?.toJson()); + return val; +} + +RtmEventHandlerOnTopicEventJson _$RtmEventHandlerOnTopicEventJsonFromJson( + Map json) => + RtmEventHandlerOnTopicEventJson( + event: json['event'] == null + ? null + : TopicEvent.fromJson(json['event'] as Map), + ); + +Map _$RtmEventHandlerOnTopicEventJsonToJson( + RtmEventHandlerOnTopicEventJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('event', instance.event?.toJson()); + return val; +} + +RtmEventHandlerOnLockEventJson _$RtmEventHandlerOnLockEventJsonFromJson( + Map json) => + RtmEventHandlerOnLockEventJson( + event: json['event'] == null + ? null + : LockEvent.fromJson(json['event'] as Map), + ); + +Map _$RtmEventHandlerOnLockEventJsonToJson( + RtmEventHandlerOnLockEventJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('event', instance.event?.toJson()); + return val; +} + +RtmEventHandlerOnStorageEventJson _$RtmEventHandlerOnStorageEventJsonFromJson( + Map json) => + RtmEventHandlerOnStorageEventJson( + event: json['event'] == null + ? null + : StorageEvent.fromJson(json['event'] as Map), + ); + +Map _$RtmEventHandlerOnStorageEventJsonToJson( + RtmEventHandlerOnStorageEventJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('event', instance.event?.toJson()); + return val; +} + +RtmEventHandlerOnJoinResultJson _$RtmEventHandlerOnJoinResultJsonFromJson( + Map json) => + RtmEventHandlerOnJoinResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + userId: json['userId'] as String?, + errorCode: $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnJoinResultJsonToJson( + RtmEventHandlerOnJoinResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('userId', instance.userId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +const _$RtmErrorCodeEnumMap = { + RtmErrorCode.ok: 0, + RtmErrorCode.notInitialized: -10001, + RtmErrorCode.notLogin: -10002, + RtmErrorCode.invalidAppId: -10003, + RtmErrorCode.invalidEventHandler: -10004, + RtmErrorCode.invalidToken: -10005, + RtmErrorCode.invalidUserId: -10006, + RtmErrorCode.initServiceFailed: -10007, + RtmErrorCode.invalidChannelName: -10008, + RtmErrorCode.tokenExpired: -10009, + RtmErrorCode.loginNoServerResources: -10010, + RtmErrorCode.loginTimeout: -10011, + RtmErrorCode.loginRejected: -10012, + RtmErrorCode.loginAborted: -10013, + RtmErrorCode.invalidParameter: -10014, + RtmErrorCode.loginNotAuthorized: -10015, + RtmErrorCode.inconsistentAppid: -10016, + RtmErrorCode.duplicateOperation: -10017, + RtmErrorCode.instanceAlreadyReleased: -10018, + RtmErrorCode.invalidChannelType: -10019, + RtmErrorCode.invalidEncryptionParameter: -10020, + RtmErrorCode.operationRateExceedLimitation: -10021, + RtmErrorCode.serviceNotSupported: -10022, + RtmErrorCode.loginCanceled: -10023, + RtmErrorCode.invalidPrivateConfig: -10024, + RtmErrorCode.notConnected: -10025, + RtmErrorCode.channelNotJoined: -11001, + RtmErrorCode.channelNotSubscribed: -11002, + RtmErrorCode.channelExceedTopicUserLimitation: -11003, + RtmErrorCode.channelInReuse: -11004, + RtmErrorCode.channelInstanceExceedLimitation: -11005, + RtmErrorCode.channelInErrorState: -11006, + RtmErrorCode.channelJoinFailed: -11007, + RtmErrorCode.channelInvalidTopicName: -11008, + RtmErrorCode.channelInvalidMessage: -11009, + RtmErrorCode.channelMessageLengthExceedLimitation: -11010, + RtmErrorCode.channelInvalidUserList: -11011, + RtmErrorCode.channelNotAvailable: -11012, + RtmErrorCode.channelTopicNotSubscribed: -11013, + RtmErrorCode.channelExceedTopicLimitation: -11014, + RtmErrorCode.channelJoinTopicFailed: -11015, + RtmErrorCode.channelTopicNotJoined: -11016, + RtmErrorCode.channelTopicNotExist: -11017, + RtmErrorCode.channelInvalidTopicMeta: -11018, + RtmErrorCode.channelSubscribeTimeout: -11019, + RtmErrorCode.channelSubscribeTooFrequent: -11020, + RtmErrorCode.channelSubscribeFailed: -11021, + RtmErrorCode.channelUnsubscribeFailed: -11022, + RtmErrorCode.channelEncryptMessageFailed: -11023, + RtmErrorCode.channelPublishMessageFailed: -11024, + RtmErrorCode.channelPublishMessageTooFrequent: -11025, + RtmErrorCode.channelPublishMessageTimeout: -11026, + RtmErrorCode.channelNotConnected: -11027, + RtmErrorCode.channelLeaveFailed: -11028, + RtmErrorCode.channelCustomTypeLengthOverflow: -11029, + RtmErrorCode.channelInvalidCustomType: -11030, + RtmErrorCode.channelUnsupportedMessageType: -11031, + RtmErrorCode.channelPresenceNotReady: -11032, + RtmErrorCode.channelReceiverOffline: -11033, + RtmErrorCode.channelJoinCanceled: -11034, + RtmErrorCode.storageOperationFailed: -12001, + RtmErrorCode.storageMetadataItemExceedLimitation: -12002, + RtmErrorCode.storageInvalidMetadataItem: -12003, + RtmErrorCode.storageInvalidArgument: -12004, + RtmErrorCode.storageInvalidRevision: -12005, + RtmErrorCode.storageMetadataLengthOverflow: -12006, + RtmErrorCode.storageInvalidLockName: -12007, + RtmErrorCode.storageLockNotAcquired: -12008, + RtmErrorCode.storageInvalidKey: -12009, + RtmErrorCode.storageInvalidValue: -12010, + RtmErrorCode.storageKeyLengthOverflow: -12011, + RtmErrorCode.storageValueLengthOverflow: -12012, + RtmErrorCode.storageDuplicateKey: -12013, + RtmErrorCode.storageOutdatedRevision: -12014, + RtmErrorCode.storageNotSubscribe: -12015, + RtmErrorCode.storageInvalidMetadataInstance: -12016, + RtmErrorCode.storageSubscribeUserExceedLimitation: -12017, + RtmErrorCode.storageOperationTimeout: -12018, + RtmErrorCode.storageNotAvailable: -12019, + RtmErrorCode.presenceNotConnected: -13001, + RtmErrorCode.presenceNotWritable: -13002, + RtmErrorCode.presenceInvalidArgument: -13003, + RtmErrorCode.presenceCachedTooManyStates: -13004, + RtmErrorCode.presenceStateCountOverflow: -13005, + RtmErrorCode.presenceInvalidStateKey: -13006, + RtmErrorCode.presenceInvalidStateValue: -13007, + RtmErrorCode.presenceStateKeySizeOverflow: -13008, + RtmErrorCode.presenceStateValueSizeOverflow: -13009, + RtmErrorCode.presenceStateDuplicateKey: -13010, + RtmErrorCode.presenceUserNotExist: -13011, + RtmErrorCode.presenceOperationTimeout: -13012, + RtmErrorCode.presenceOperationFailed: -13013, + RtmErrorCode.lockOperationFailed: -14001, + RtmErrorCode.lockOperationTimeout: -14002, + RtmErrorCode.lockOperationPerforming: -14003, + RtmErrorCode.lockAlreadyExist: -14004, + RtmErrorCode.lockInvalidName: -14005, + RtmErrorCode.lockNotAcquired: -14006, + RtmErrorCode.lockAcquireFailed: -14007, + RtmErrorCode.lockNotExist: -14008, + RtmErrorCode.lockNotAvailable: -14009, +}; + +RtmEventHandlerOnLeaveResultJson _$RtmEventHandlerOnLeaveResultJsonFromJson( + Map json) => + RtmEventHandlerOnLeaveResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + userId: json['userId'] as String?, + errorCode: $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnLeaveResultJsonToJson( + RtmEventHandlerOnLeaveResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('userId', instance.userId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnPublishTopicMessageResultJson + _$RtmEventHandlerOnPublishTopicMessageResultJsonFromJson( + Map json) => + RtmEventHandlerOnPublishTopicMessageResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + topic: json['topic'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnPublishTopicMessageResultJsonToJson( + RtmEventHandlerOnPublishTopicMessageResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('topic', instance.topic); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnJoinTopicResultJson + _$RtmEventHandlerOnJoinTopicResultJsonFromJson(Map json) => + RtmEventHandlerOnJoinTopicResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + userId: json['userId'] as String?, + topic: json['topic'] as String?, + meta: json['meta'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnJoinTopicResultJsonToJson( + RtmEventHandlerOnJoinTopicResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('userId', instance.userId); + writeNotNull('topic', instance.topic); + writeNotNull('meta', instance.meta); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnLeaveTopicResultJson + _$RtmEventHandlerOnLeaveTopicResultJsonFromJson( + Map json) => + RtmEventHandlerOnLeaveTopicResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + userId: json['userId'] as String?, + topic: json['topic'] as String?, + meta: json['meta'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnLeaveTopicResultJsonToJson( + RtmEventHandlerOnLeaveTopicResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('userId', instance.userId); + writeNotNull('topic', instance.topic); + writeNotNull('meta', instance.meta); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnSubscribeTopicResultJson + _$RtmEventHandlerOnSubscribeTopicResultJsonFromJson( + Map json) => + RtmEventHandlerOnSubscribeTopicResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + userId: json['userId'] as String?, + topic: json['topic'] as String?, + succeedUsers: json['succeedUsers'] == null + ? null + : UserList.fromJson(json['succeedUsers'] as Map), + failedUsers: json['failedUsers'] == null + ? null + : UserList.fromJson(json['failedUsers'] as Map), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnSubscribeTopicResultJsonToJson( + RtmEventHandlerOnSubscribeTopicResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('userId', instance.userId); + writeNotNull('topic', instance.topic); + writeNotNull('succeedUsers', instance.succeedUsers?.toJson()); + writeNotNull('failedUsers', instance.failedUsers?.toJson()); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnUnsubscribeTopicResultJson + _$RtmEventHandlerOnUnsubscribeTopicResultJsonFromJson( + Map json) => + RtmEventHandlerOnUnsubscribeTopicResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + topic: json['topic'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnUnsubscribeTopicResultJsonToJson( + RtmEventHandlerOnUnsubscribeTopicResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('topic', instance.topic); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnGetSubscribedUserListResultJson + _$RtmEventHandlerOnGetSubscribedUserListResultJsonFromJson( + Map json) => + RtmEventHandlerOnGetSubscribedUserListResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + topic: json['topic'] as String?, + users: json['users'] == null + ? null + : UserList.fromJson(json['users'] as Map), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnGetSubscribedUserListResultJsonToJson( + RtmEventHandlerOnGetSubscribedUserListResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('topic', instance.topic); + writeNotNull('users', instance.users?.toJson()); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnConnectionStateChangedJson + _$RtmEventHandlerOnConnectionStateChangedJsonFromJson( + Map json) => + RtmEventHandlerOnConnectionStateChangedJson( + channelName: json['channelName'] as String?, + state: + $enumDecodeNullable(_$RtmConnectionStateEnumMap, json['state']), + reason: $enumDecodeNullable( + _$RtmConnectionChangeReasonEnumMap, json['reason']), + ); + +Map _$RtmEventHandlerOnConnectionStateChangedJsonToJson( + RtmEventHandlerOnConnectionStateChangedJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('channelName', instance.channelName); + writeNotNull('state', _$RtmConnectionStateEnumMap[instance.state]); + writeNotNull('reason', _$RtmConnectionChangeReasonEnumMap[instance.reason]); + return val; +} + +const _$RtmConnectionStateEnumMap = { + RtmConnectionState.disconnected: 1, + RtmConnectionState.connecting: 2, + RtmConnectionState.connected: 3, + RtmConnectionState.reconnecting: 4, + RtmConnectionState.failed: 5, +}; + +const _$RtmConnectionChangeReasonEnumMap = { + RtmConnectionChangeReason.connecting: 0, + RtmConnectionChangeReason.joinSuccess: 1, + RtmConnectionChangeReason.interrupted: 2, + RtmConnectionChangeReason.bannedByServer: 3, + RtmConnectionChangeReason.joinFailed: 4, + RtmConnectionChangeReason.leaveChannel: 5, + RtmConnectionChangeReason.invalidAppId: 6, + RtmConnectionChangeReason.invalidChannelName: 7, + RtmConnectionChangeReason.invalidToken: 8, + RtmConnectionChangeReason.tokenExpired: 9, + RtmConnectionChangeReason.rejectedByServer: 10, + RtmConnectionChangeReason.settingProxyServer: 11, + RtmConnectionChangeReason.renewToken: 12, + RtmConnectionChangeReason.clientIpAddressChanged: 13, + RtmConnectionChangeReason.keepAliveTimeout: 14, + RtmConnectionChangeReason.rejoinSuccess: 15, + RtmConnectionChangeReason.lost: 16, + RtmConnectionChangeReason.echoTest: 17, + RtmConnectionChangeReason.clientIpAddressChangedByUser: 18, + RtmConnectionChangeReason.sameUidLogin: 19, + RtmConnectionChangeReason.tooManyBroadcasters: 20, + RtmConnectionChangeReason.licenseValidationFailure: 21, + RtmConnectionChangeReason.certificationVerifyFailure: 22, + RtmConnectionChangeReason.streamChannelNotAvailable: 23, + RtmConnectionChangeReason.inconsistentAppid: 24, + RtmConnectionChangeReason.loginSuccess: 10001, + RtmConnectionChangeReason.logout: 10002, + RtmConnectionChangeReason.presenceNotReady: 10003, +}; + +RtmEventHandlerOnTokenPrivilegeWillExpireJson + _$RtmEventHandlerOnTokenPrivilegeWillExpireJsonFromJson( + Map json) => + RtmEventHandlerOnTokenPrivilegeWillExpireJson( + channelName: json['channelName'] as String?, + ); + +Map _$RtmEventHandlerOnTokenPrivilegeWillExpireJsonToJson( + RtmEventHandlerOnTokenPrivilegeWillExpireJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('channelName', instance.channelName); + return val; +} + +RtmEventHandlerOnSubscribeResultJson + _$RtmEventHandlerOnSubscribeResultJsonFromJson(Map json) => + RtmEventHandlerOnSubscribeResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnSubscribeResultJsonToJson( + RtmEventHandlerOnSubscribeResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnUnsubscribeResultJson + _$RtmEventHandlerOnUnsubscribeResultJsonFromJson( + Map json) => + RtmEventHandlerOnUnsubscribeResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnUnsubscribeResultJsonToJson( + RtmEventHandlerOnUnsubscribeResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnPublishResultJson _$RtmEventHandlerOnPublishResultJsonFromJson( + Map json) => + RtmEventHandlerOnPublishResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + errorCode: $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnPublishResultJsonToJson( + RtmEventHandlerOnPublishResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnLoginResultJson _$RtmEventHandlerOnLoginResultJsonFromJson( + Map json) => + RtmEventHandlerOnLoginResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + errorCode: $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnLoginResultJsonToJson( + RtmEventHandlerOnLoginResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnLogoutResultJson _$RtmEventHandlerOnLogoutResultJsonFromJson( + Map json) => + RtmEventHandlerOnLogoutResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + errorCode: $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnLogoutResultJsonToJson( + RtmEventHandlerOnLogoutResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnRenewTokenResultJson + _$RtmEventHandlerOnRenewTokenResultJsonFromJson( + Map json) => + RtmEventHandlerOnRenewTokenResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + serverType: + $enumDecodeNullable(_$RtmServiceTypeEnumMap, json['serverType']), + channelName: json['channelName'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnRenewTokenResultJsonToJson( + RtmEventHandlerOnRenewTokenResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('serverType', _$RtmServiceTypeEnumMap[instance.serverType]); + writeNotNull('channelName', instance.channelName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +const _$RtmServiceTypeEnumMap = { + RtmServiceType.none: 0, + RtmServiceType.message: 1, + RtmServiceType.stream: 2, +}; + +RtmEventHandlerOnSetChannelMetadataResultJson + _$RtmEventHandlerOnSetChannelMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnSetChannelMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnSetChannelMetadataResultJsonToJson( + RtmEventHandlerOnSetChannelMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +const _$RtmChannelTypeEnumMap = { + RtmChannelType.none: 0, + RtmChannelType.message: 1, + RtmChannelType.stream: 2, + RtmChannelType.user: 3, +}; + +RtmEventHandlerOnUpdateChannelMetadataResultJson + _$RtmEventHandlerOnUpdateChannelMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnUpdateChannelMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnUpdateChannelMetadataResultJsonToJson( + RtmEventHandlerOnUpdateChannelMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnRemoveChannelMetadataResultJson + _$RtmEventHandlerOnRemoveChannelMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnRemoveChannelMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnRemoveChannelMetadataResultJsonToJson( + RtmEventHandlerOnRemoveChannelMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnGetChannelMetadataResultJson + _$RtmEventHandlerOnGetChannelMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnGetChannelMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + data: json['data'] == null + ? null + : Metadata.fromJson(json['data'] as Map), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnGetChannelMetadataResultJsonToJson( + RtmEventHandlerOnGetChannelMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('data', instance.data?.toJson()); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnSetUserMetadataResultJson + _$RtmEventHandlerOnSetUserMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnSetUserMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userId: json['userId'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnSetUserMetadataResultJsonToJson( + RtmEventHandlerOnSetUserMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('userId', instance.userId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnUpdateUserMetadataResultJson + _$RtmEventHandlerOnUpdateUserMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnUpdateUserMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userId: json['userId'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnUpdateUserMetadataResultJsonToJson( + RtmEventHandlerOnUpdateUserMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('userId', instance.userId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnRemoveUserMetadataResultJson + _$RtmEventHandlerOnRemoveUserMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnRemoveUserMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userId: json['userId'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnRemoveUserMetadataResultJsonToJson( + RtmEventHandlerOnRemoveUserMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('userId', instance.userId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnGetUserMetadataResultJson + _$RtmEventHandlerOnGetUserMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnGetUserMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userId: json['userId'] as String?, + data: json['data'] == null + ? null + : Metadata.fromJson(json['data'] as Map), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnGetUserMetadataResultJsonToJson( + RtmEventHandlerOnGetUserMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('userId', instance.userId); + writeNotNull('data', instance.data?.toJson()); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnSubscribeUserMetadataResultJson + _$RtmEventHandlerOnSubscribeUserMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnSubscribeUserMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userId: json['userId'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnSubscribeUserMetadataResultJsonToJson( + RtmEventHandlerOnSubscribeUserMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('userId', instance.userId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnUnsubscribeUserMetadataResultJson + _$RtmEventHandlerOnUnsubscribeUserMetadataResultJsonFromJson( + Map json) => + RtmEventHandlerOnUnsubscribeUserMetadataResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userId: json['userId'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnUnsubscribeUserMetadataResultJsonToJson( + RtmEventHandlerOnUnsubscribeUserMetadataResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('userId', instance.userId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnSetLockResultJson _$RtmEventHandlerOnSetLockResultJsonFromJson( + Map json) => + RtmEventHandlerOnSetLockResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + lockName: json['lockName'] as String?, + errorCode: $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnSetLockResultJsonToJson( + RtmEventHandlerOnSetLockResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('lockName', instance.lockName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnRemoveLockResultJson + _$RtmEventHandlerOnRemoveLockResultJsonFromJson( + Map json) => + RtmEventHandlerOnRemoveLockResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + lockName: json['lockName'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnRemoveLockResultJsonToJson( + RtmEventHandlerOnRemoveLockResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('lockName', instance.lockName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnReleaseLockResultJson + _$RtmEventHandlerOnReleaseLockResultJsonFromJson( + Map json) => + RtmEventHandlerOnReleaseLockResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + lockName: json['lockName'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnReleaseLockResultJsonToJson( + RtmEventHandlerOnReleaseLockResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('lockName', instance.lockName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnAcquireLockResultJson + _$RtmEventHandlerOnAcquireLockResultJsonFromJson( + Map json) => + RtmEventHandlerOnAcquireLockResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + lockName: json['lockName'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + errorDetails: json['errorDetails'] as String?, + ); + +Map _$RtmEventHandlerOnAcquireLockResultJsonToJson( + RtmEventHandlerOnAcquireLockResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('lockName', instance.lockName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + writeNotNull('errorDetails', instance.errorDetails); + return val; +} + +RtmEventHandlerOnRevokeLockResultJson + _$RtmEventHandlerOnRevokeLockResultJsonFromJson( + Map json) => + RtmEventHandlerOnRevokeLockResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + lockName: json['lockName'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnRevokeLockResultJsonToJson( + RtmEventHandlerOnRevokeLockResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('lockName', instance.lockName); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnGetLocksResultJson + _$RtmEventHandlerOnGetLocksResultJsonFromJson(Map json) => + RtmEventHandlerOnGetLocksResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channelName: json['channelName'] as String?, + channelType: + $enumDecodeNullable(_$RtmChannelTypeEnumMap, json['channelType']), + lockDetailList: (json['lockDetailList'] as List?) + ?.map((e) => LockDetail.fromJson(e as Map)) + .toList(), + count: (json['count'] as num?)?.toInt(), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnGetLocksResultJsonToJson( + RtmEventHandlerOnGetLocksResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channelName', instance.channelName); + writeNotNull('channelType', _$RtmChannelTypeEnumMap[instance.channelType]); + writeNotNull('lockDetailList', + instance.lockDetailList?.map((e) => e.toJson()).toList()); + writeNotNull('count', instance.count); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnWhoNowResultJson _$RtmEventHandlerOnWhoNowResultJsonFromJson( + Map json) => + RtmEventHandlerOnWhoNowResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userStateList: (json['userStateList'] as List?) + ?.map((e) => UserState.fromJson(e as Map)) + .toList(), + count: (json['count'] as num?)?.toInt(), + nextPage: json['nextPage'] as String?, + errorCode: $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnWhoNowResultJsonToJson( + RtmEventHandlerOnWhoNowResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull( + 'userStateList', instance.userStateList?.map((e) => e.toJson()).toList()); + writeNotNull('count', instance.count); + writeNotNull('nextPage', instance.nextPage); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnGetOnlineUsersResultJson + _$RtmEventHandlerOnGetOnlineUsersResultJsonFromJson( + Map json) => + RtmEventHandlerOnGetOnlineUsersResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + userStateList: (json['userStateList'] as List?) + ?.map((e) => UserState.fromJson(e as Map)) + .toList(), + count: (json['count'] as num?)?.toInt(), + nextPage: json['nextPage'] as String?, + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnGetOnlineUsersResultJsonToJson( + RtmEventHandlerOnGetOnlineUsersResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull( + 'userStateList', instance.userStateList?.map((e) => e.toJson()).toList()); + writeNotNull('count', instance.count); + writeNotNull('nextPage', instance.nextPage); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnWhereNowResultJson + _$RtmEventHandlerOnWhereNowResultJsonFromJson(Map json) => + RtmEventHandlerOnWhereNowResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channels: (json['channels'] as List?) + ?.map((e) => ChannelInfo.fromJson(e as Map)) + .toList(), + count: (json['count'] as num?)?.toInt(), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnWhereNowResultJsonToJson( + RtmEventHandlerOnWhereNowResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channels', instance.channels?.map((e) => e.toJson()).toList()); + writeNotNull('count', instance.count); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnGetUserChannelsResultJson + _$RtmEventHandlerOnGetUserChannelsResultJsonFromJson( + Map json) => + RtmEventHandlerOnGetUserChannelsResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + channels: json['channels'] == null + ? null + : ChannelInfo.fromJson(json['channels'] as Map), + count: (json['count'] as num?)?.toInt(), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnGetUserChannelsResultJsonToJson( + RtmEventHandlerOnGetUserChannelsResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('channels', instance.channels?.toJson()); + writeNotNull('count', instance.count); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnPresenceSetStateResultJson + _$RtmEventHandlerOnPresenceSetStateResultJsonFromJson( + Map json) => + RtmEventHandlerOnPresenceSetStateResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnPresenceSetStateResultJsonToJson( + RtmEventHandlerOnPresenceSetStateResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnPresenceRemoveStateResultJson + _$RtmEventHandlerOnPresenceRemoveStateResultJsonFromJson( + Map json) => + RtmEventHandlerOnPresenceRemoveStateResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnPresenceRemoveStateResultJsonToJson( + RtmEventHandlerOnPresenceRemoveStateResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} + +RtmEventHandlerOnPresenceGetStateResultJson + _$RtmEventHandlerOnPresenceGetStateResultJsonFromJson( + Map json) => + RtmEventHandlerOnPresenceGetStateResultJson( + requestId: (json['requestId'] as num?)?.toInt(), + state: json['state'] == null + ? null + : UserState.fromJson(json['state'] as Map), + errorCode: + $enumDecodeNullable(_$RtmErrorCodeEnumMap, json['errorCode']), + ); + +Map _$RtmEventHandlerOnPresenceGetStateResultJsonToJson( + RtmEventHandlerOnPresenceGetStateResultJson instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('requestId', instance.requestId); + writeNotNull('state', instance.state?.toJson()); + writeNotNull('errorCode', _$RtmErrorCodeEnumMap[instance.errorCode]); + return val; +} diff --git a/lib/src/bindings/gen/native_iris_api_engine_bindings.dart b/lib/src/bindings/gen/native_iris_api_engine_bindings.dart new file mode 100644 index 0000000..342a988 --- /dev/null +++ b/lib/src/bindings/gen/native_iris_api_engine_bindings.dart @@ -0,0 +1,205 @@ +// ignore_for_file: camel_case_types, non_constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; + +/// Bindings to IrisApiEngine +class NativeIrisApiEngineBinding { + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeIrisApiEngineBinding(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeIrisApiEngineBinding.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + /// @brief Create an IrisRtmEngine object and return the handle value of + /// the object. + /// + /// @param client The handle value of an exist RtmClient object. + /// @return + /// - zero value for failed. + /// - nonzero value for success. + IrisHandle CreateIrisRtmEngine( + IrisHandle client, + ) { + return _CreateIrisRtmEngine( + client, + ); + } + + late final _CreateIrisRtmEnginePtr = + _lookup>( + 'CreateIrisRtmEngine'); + late final _CreateIrisRtmEngine = + _CreateIrisRtmEnginePtr.asFunction(); + + IrisHandle CreateIrisRtmEngineWithProcTable( + ffi.Pointer procTable, + ) { + return _CreateIrisRtmEngineWithProcTable( + procTable, + ); + } + + late final _CreateIrisRtmEngineWithProcTablePtr = _lookup< + ffi.NativeFunction)>>( + 'CreateIrisRtmEngineWithProcTable'); + late final _CreateIrisRtmEngineWithProcTable = + _CreateIrisRtmEngineWithProcTablePtr.asFunction< + IrisHandle Function(ffi.Pointer)>(); + + /// @brief Destroy an IrisRtmEngine object. + /// + /// @param engine The handle value of IrisRtmEngine object. + void DestroyIrisRtmEngine( + IrisHandle engine, + ) { + return _DestroyIrisRtmEngine( + engine, + ); + } + + late final _DestroyIrisRtmEnginePtr = + _lookup>( + 'DestroyIrisRtmEngine'); + late final _DestroyIrisRtmEngine = + _DestroyIrisRtmEnginePtr.asFunction(); + + /// @brief Call api function with specified IrisRtmEngine. + /// + /// @param engine The handle value of an IrisRtmEngine + /// @param param The pointer to an ApiParam object. + /// @return + /// - 0: Success. + /// - < 0 : Failure. + int CallIrisRtmApi( + IrisHandle engine, + ffi.Pointer param, + ) { + return _CallIrisRtmApi( + engine, + param, + ); + } + + late final _CallIrisRtmApiPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + IrisHandle, ffi.Pointer)>>('CallIrisRtmApi'); + late final _CallIrisRtmApi = _CallIrisRtmApiPtr.asFunction< + int Function(IrisHandle, ffi.Pointer)>(); + + /// @brief Create an IrisEventHandler object. + /// + /// @param handler + /// @return The handle value of created event handler. + IrisEventHandlerHandle CreateIrisRtmEventHandler( + ffi.Pointer handler, + ) { + return _CreateIrisRtmEventHandler( + handler, + ); + } + + late final _CreateIrisRtmEventHandlerPtr = _lookup< + ffi.NativeFunction< + IrisEventHandlerHandle Function( + ffi.Pointer)>>('CreateIrisRtmEventHandler'); + late final _CreateIrisRtmEventHandler = + _CreateIrisRtmEventHandlerPtr.asFunction< + IrisEventHandlerHandle Function(ffi.Pointer)>(); + + /// @brief Destroy the IrisEventHandler object with specified handle value. + /// + /// @param handler The handle value of an IrisEventHandler. + void DestroyIrisRtmEventHandler( + IrisEventHandlerHandle handler, + ) { + return _DestroyIrisRtmEventHandler( + handler, + ); + } + + late final _DestroyIrisRtmEventHandlerPtr = + _lookup>( + 'DestroyIrisRtmEventHandler'); + late final _DestroyIrisRtmEventHandler = _DestroyIrisRtmEventHandlerPtr + .asFunction(); + + /// @brief Convert error code to error string. + /// + /// @param error_code Received error code. + /// @return The error string. + ffi.Pointer GetIrisRtmErrorReason( + int error_code, + ) { + return _GetIrisRtmErrorReason( + error_code, + ); + } + + late final _GetIrisRtmErrorReasonPtr = + _lookup Function(ffi.Int32)>>( + 'GetIrisRtmErrorReason'); + late final _GetIrisRtmErrorReason = _GetIrisRtmErrorReasonPtr.asFunction< + ffi.Pointer Function(int)>(); + + /// @brief Get the version info of the Agora RTM SDK. + /// + /// @return The version info of the Agora RTM SDK. + ffi.Pointer GetIrisRtmVersion() { + return _GetIrisRtmVersion(); + } + + late final _GetIrisRtmVersionPtr = + _lookup Function()>>( + 'GetIrisRtmVersion'); + late final _GetIrisRtmVersion = + _GetIrisRtmVersionPtr.asFunction Function()>(); +} + +final class ProcTableRtm extends ffi.Struct { + external CreateAgoraRtmClient createAgoraRtmClient; +} + +typedef CreateAgoraRtmClient = ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, ffi.Int32)>>; +typedef IrisHandle = ffi.Pointer; +typedef ApiParam = EventParam; + +final class EventParam extends ffi.Struct { + external ffi.Pointer event; + + external ffi.Pointer data; + + @ffi.Uint32() + external int data_size; + + external ffi.Pointer result; + + external ffi.Pointer> buffer; + + external ffi.Pointer length; + + @ffi.Uint32() + external int buffer_count; +} + +typedef IrisEventHandlerHandle = IrisHandle; + +final class IrisCEventHandler extends ffi.Struct { + external Func_Event OnEvent; +} + +typedef Func_Event = ffi + .Pointer)>>; diff --git a/lib/src/bindings/json_converters.dart b/lib/src/bindings/json_converters.dart new file mode 100644 index 0000000..35124fd --- /dev/null +++ b/lib/src/bindings/json_converters.dart @@ -0,0 +1,107 @@ +import 'package:agora_rtm/src/agora_rtm_base.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class IgnoreableJsonConverter implements JsonConverter { + const IgnoreableJsonConverter(); + @override + T? fromJson(S? json) { + return null; + } + + @override + S? toJson(T? object) { + return null; + } +} + +class RtmAreaCodeListConverter + implements JsonConverter?, int?> { + const RtmAreaCodeListConverter(); + + @override + Set? fromJson(int? json) => null; + + @override + int? toJson(Set? areaCodeSet) { + if (areaCodeSet?.isNotEmpty == true) { + late int value; + int index = 0; + for (final v in areaCodeSet!) { + if (index == 0) { + value = v.value(); + } else { + value = value | v.value(); + } + ++index; + } + return value; + } + + return null; + } +} + +class RtmServiceTypeListConverter + implements JsonConverter?, int?> { + const RtmServiceTypeListConverter(); + + @override + Set? fromJson(int? json) => null; + + @override + int? toJson(Set? rtmServiceTypeSet) { + if (rtmServiceTypeSet?.isNotEmpty == true) { + late int value; + int index = 0; + for (final v in rtmServiceTypeSet!) { + if (index == 0) { + value = v.value(); + } else { + value = value | v.value(); + } + ++index; + } + return value; + } + + return null; + } +} + +int _intPtrStr2Int(String value) { + // In 64-bits system, the c++ int ptr value (unsigned long 64) can be 2^64 - 1, + // which may greater than the dart int max value (2^63 - 1), so we can not decode + // the json with big int c++ int ptr value and parse it directly. + // + // After dart sdk 2.0 support parse hexadecimal in unsigned int64 range. + // https://github.com/dart-lang/language/blob/ee1135e0c22391cee17bf3ee262d6a04582d25de/archive/newsletter/20170929.md#semantics + // + // So we retrive the c++ int ptr value from the json string directly, and + // parse an int from hexadecimal here. + BigInt valueBI = BigInt.parse(value); + return int.tryParse('0x${valueBI.toRadixString(16)}') ?? 0; +} + +/// Parse a c++ int ptr value from the json key `_str` +Object? readIntPtr(Map json, String key) { + final newKey = '${key}_str'; + if (json.containsKey(newKey)) { + final value = json[newKey]; + assert(value is String); + return _intPtrStr2Int(value); + } + + return json[key]; +} + +/// Same as `readIntPtr`, but for list of int ptr. +Object? readIntPtrList(Map json, String key) { + final newKey = '${key}_str'; + if (json.containsKey(newKey)) { + final value = json[newKey]; + assert(value is List); + return List.from(value.map((e) => _intPtrStr2Int(e))); + } + + return json[key]; +} diff --git a/lib/src/bindings/native_iris_api_engine_binding_delegate.dart b/lib/src/bindings/native_iris_api_engine_binding_delegate.dart new file mode 100644 index 0000000..0eae9c7 --- /dev/null +++ b/lib/src/bindings/native_iris_api_engine_binding_delegate.dart @@ -0,0 +1,152 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:ffi' as ffi; + +import 'package:ffi/ffi.dart'; + +import 'gen/native_iris_api_engine_bindings.dart' as bindings; +import 'package:iris_method_channel/iris_method_channel.dart'; +import 'package:iris_method_channel/iris_method_channel_bindings_io.dart' + as iris_bindings; + +// ignore_for_file: public_member_api_docs + +const _libName = 'AgoraRtmWrapper'; + +const _doNotInterceptCall = -1000000; + +ffi.DynamicLibrary _loadLib() { + if (Platform.isWindows) { + return ffi.DynamicLibrary.open('$_libName.dll'); + } + + if (Platform.isAndroid) { + return ffi.DynamicLibrary.open("lib$_libName.so"); + } + + return ffi.DynamicLibrary.process(); +} + +class NativeIrisApiEngineBindingsDelegate + extends PlatformBindingsDelegateInterface { + late final bindings.NativeIrisApiEngineBinding _binding; + bindings.NativeIrisApiEngineBinding get binding => _binding; + + @override + void initialize() { + _binding = bindings.NativeIrisApiEngineBinding(_loadLib()); + } + + @override + CreateApiEngineResult createApiEngine(List args) { + ffi.Pointer enginePtr = ffi.nullptr; + if (args.isNotEmpty) { + assert(args.length == 1); + final engineIntPtr = + args[0].provide(const IrisApiEngineHandle(0))() as int; + if (engineIntPtr != 0) { + enginePtr = + ffi.Pointer.fromAddress(engineIntPtr); + } + } + + final apiEnginePtr = _binding.CreateIrisRtmEngineWithProcTable(enginePtr); + + return CreateApiEngineResult(IrisApiEngineHandle(apiEnginePtr)); + } + + void _response( + ffi.Pointer param, Map result) { + using((Arena arena) { + final ffi.Pointer resultMapPointerUtf8 = + jsonEncode(result).toNativeUtf8(allocator: arena); + final ffi.Pointer resultMapPointerInt8 = + resultMapPointerUtf8.cast(); + + for (int i = 0; i < kBasicResultLength; i++) { + if (i >= resultMapPointerUtf8.length) { + break; + } + + param.ref.result[i] = resultMapPointerInt8[i]; + } + }); + } + + /// The value of `methoCall.funcName` should be same as the C function name + int _interceptCall( + IrisMethodCall methodCall, + ffi.Pointer apiEnginePtr, + ffi.Pointer param, + ) { + switch (methodCall.funcName) { + case 'GetIrisRtmErrorReason': + { + final data = jsonDecode(methodCall.params); + final errorCode = data['error_code']; + final reasonPtr = _binding.GetIrisRtmErrorReason(errorCode); + final reason = reasonPtr.cast().toDartString(); + + final result = {'result': reason}; + _response(param, result); + + return 0; + } + default: + break; + } + return _doNotInterceptCall; + } + + @override + int callApi( + IrisMethodCall methodCall, + IrisApiEngineHandle apiEnginePtr, + IrisApiParamHandle param, + ) { + final nApiEnginePtr = apiEnginePtr() as ffi.Pointer; + final nParam = param() as ffi.Pointer; + final interceptRet = _interceptCall(methodCall, nApiEnginePtr, nParam); + if (interceptRet != _doNotInterceptCall) { + return interceptRet; + } + + return _binding.CallIrisRtmApi(nApiEnginePtr, nParam.cast()); + } + + @override + IrisEventHandlerHandle createIrisEventHandler( + IrisCEventHandlerHandle eventHandler, + ) { + return IrisEventHandlerHandle(_binding.CreateIrisRtmEventHandler( + (eventHandler() as ffi.Pointer) + .cast())); + } + + @override + void destroyIrisEventHandler( + IrisEventHandlerHandle handler, + ) { + _binding.DestroyIrisRtmEventHandler(handler() as ffi.Pointer); + } + + @override + void destroyNativeApiEngine(IrisApiEngineHandle apiEnginePtr) { + _binding.DestroyIrisRtmEngine(apiEnginePtr() as ffi.Pointer); + } + + @override + Future callApiAsync(IrisMethodCall methodCall, + IrisApiEngineHandle apiEnginePtr, IrisApiParamHandle param) { + throw UnsupportedError( + '`callApiAsync` not support on native implementation.'); + } +} + +class IrisApiEngineNativeBindingDelegateProvider + extends PlatformBindingsProvider { + @override + PlatformBindingsDelegateInterface provideNativeBindingDelegate() { + return NativeIrisApiEngineBindingsDelegate(); + } +} diff --git a/lib/src/impl/agora_rtm_client_impl_override.dart b/lib/src/impl/agora_rtm_client_impl_override.dart new file mode 100644 index 0000000..eb9fb46 --- /dev/null +++ b/lib/src/impl/agora_rtm_client_impl_override.dart @@ -0,0 +1,295 @@ +import 'package:agora_rtm/src/agora_rtm_base.dart'; +import 'package:agora_rtm/src/agora_rtm_client.dart'; +import 'package:agora_rtm/src/agora_rtm_client_ext.dart' + show AgoraRtmException, RtmStatus; +import 'package:agora_rtm/src/agora_rtm_lock.dart'; +import 'package:agora_rtm/src/agora_rtm_presence.dart'; +import 'package:agora_rtm/src/agora_rtm_storage.dart'; +import 'package:agora_rtm/src/agora_stream_channel.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/impl/agora_stream_channel_impl_override.dart'; +import 'package:agora_rtm/src/impl/extensions.dart'; +import 'package:agora_rtm/src/impl/gen/agora_rtm_lock_impl.dart' + as rtm_lock_impl; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_lock_impl.dart' + as rtm_lock_impl_binding; +import 'package:agora_rtm/src/impl/gen/agora_rtm_storage_impl.dart' + as rtm_storage_impl; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_storage_impl.dart' + as rtm_storage_impl_binding; +import 'package:agora_rtm/src/impl/gen/agora_rtm_presence_impl.dart' + as rtm_presence_impl; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_presence_impl.dart' + as rtm_presence_impl_binding; +import 'package:agora_rtm/src/bindings/gen/agora_stream_channel_impl.dart' + as stream_channel_impl_binding; + +import 'package:agora_rtm/src/impl/gen/agora_rtm_client_impl.dart' + as rtm_client_impl; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart' + as rtm_client_impl_override; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_client_impl.dart' + as rtm_client_native_binding; +import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:flutter/foundation.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +InitilizationArgProvider? _mockSharedNativeHandleProvider; +@visibleForTesting +void setMockSharedNativeHandleProvider( + InitilizationArgProvider? mockSharedNativeHandleProvider) { + assert(() { + _mockSharedNativeHandleProvider = mockSharedNativeHandleProvider; + return true; + }()); +} + +class RtmClientImplOverride extends rtm_client_impl.RtmClientImpl { + // ignore: use_super_parameters + RtmClientImplOverride( + rtm_client_native_binding.RtmClientImpl nativeBindingRtmClientImpl, + RtmResultHandler rtmResultHandler) + : super(nativeBindingRtmClientImpl, rtmResultHandler); + + static Future<(RtmStatus, RtmClient)> create( + String appId, + String userId, { + RtmConfig? config, + RtmResultHandlerImpl? rtmResultHandlerImpl, + rtm_client_impl_override.RtmClientImplOverride? rtmClientNativeBinding, + }) async { + if (_initializedStatus != null && + !_initializedStatus!.error && + _instance != null) { + return (_initializedStatus!, _instance!); + } + + final rtmResultHandler = rtmResultHandlerImpl ?? RtmResultHandlerImpl(); + + List args = []; + + // We only want to set it in testing. + assert(() { + if (_mockSharedNativeHandleProvider != null) { + args.add(_mockSharedNativeHandleProvider!); + } + return true; + }()); + + final bindingRtmClientImpl = rtmClientNativeBinding ?? + rtm_client_impl_override.RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + + _initializedStatus = await bindingRtmClientImpl.initialize( + appId, + userId, + rtmResultHandler.rtmEventHandler, + config: config, + args: args, + ); + + _instance = RtmClientImplOverride(bindingRtmClientImpl, rtmResultHandler); + + return (_initializedStatus!, _instance!); + } + + static RtmClientImplOverride? _instance; + static RtmStatus? _initializedStatus; + + RtmStorage? _storage; + RtmLock? _lock; + RtmPresence? _presence; + + void _setListenerIfNeeded(String key, Object? listener) { + if (listener != null) { + (rtmResultHandler as RtmResultHandlerImpl).setListener(key, listener); + } + } + + void _removeListenerIfNeeded(String key, Object? listener) { + if (listener != null) { + (rtmResultHandler as RtmResultHandlerImpl).removeListener(key); + } + } + + @override + void addListener( + {void Function(LinkStateEvent event)? linkState, + void Function(MessageEvent event)? message, + void Function(PresenceEvent event)? presence, + void Function(TopicEvent event)? topic, + void Function(LockEvent event)? lock, + void Function(StorageEvent event)? storage, + void Function(TokenEvent event)? token}) { + _setListenerIfNeeded('linkState', linkState); + _setListenerIfNeeded('message', message); + _setListenerIfNeeded('presence', presence); + _setListenerIfNeeded('topic', topic); + _setListenerIfNeeded('lock', lock); + _setListenerIfNeeded('storage', storage); + _setListenerIfNeeded('token', token); + } + + @override + void removeListener( + {void Function(LinkStateEvent event)? linkState, + void Function(MessageEvent event)? message, + void Function(PresenceEvent event)? presence, + void Function(TopicEvent event)? topic, + void Function(LockEvent event)? lock, + void Function(StorageEvent event)? storage, + void Function(TokenEvent event)? token}) { + _removeListenerIfNeeded('linkState', linkState); + _removeListenerIfNeeded('message', message); + _removeListenerIfNeeded('presence', presence); + _removeListenerIfNeeded('topic', topic); + _removeListenerIfNeeded('lock', lock); + _removeListenerIfNeeded('storage', storage); + _removeListenerIfNeeded('token', token); + } + + @override + RtmStorage getStorage() { + final irisMethodChannel = (nativeBindingRtmClientImpl + as rtm_client_impl_override.RtmClientImplOverride) + .getIrisMethodChannel(); + return (_storage ??= rtm_storage_impl.RtmStorageImpl( + rtm_storage_impl_binding.RtmStorageImpl(irisMethodChannel), + rtmResultHandler, + )); + } + + @override + RtmLock getLock() { + final irisMethodChannel = (nativeBindingRtmClientImpl + as rtm_client_impl_override.RtmClientImplOverride) + .getIrisMethodChannel(); + return (_lock ??= rtm_lock_impl.RtmLockImpl( + rtm_lock_impl_binding.RtmLockImpl(irisMethodChannel), + rtmResultHandler, + )); + } + + @override + RtmPresence getPresence() { + final irisMethodChannel = (nativeBindingRtmClientImpl + as rtm_client_impl_override.RtmClientImplOverride) + .getIrisMethodChannel(); + + return (_presence ??= rtm_presence_impl.RtmPresenceImpl( + rtm_presence_impl_binding.RtmPresenceImpl(irisMethodChannel), + rtmResultHandler, + )); + } + + @override + Future<(RtmStatus, StreamChannel?)> createStreamChannel( + String channelName, + ) async { + try { + final streamChannel = await (nativeBindingRtmClientImpl + as rtm_client_impl_override.RtmClientImplOverride) + .createStreamChannel(channelName); + + final streamChannelImpl = StreamChannelImplOverride( + streamChannel as stream_channel_impl_binding.StreamChannelImpl, + rtmResultHandler, + ); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(0, 'createStreamChannel'); + return (status, streamChannelImpl); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'createStreamChannel'); + return (status, null); + } + } + + @override + Future release() async { + try { + return await super.release(); + } finally { + _instance = null; + _initializedStatus = null; + } + } + + @override + Future<(RtmStatus, PublishResult?)> publish( + String channelName, String message, + {RtmChannelType channelType = RtmChannelType.message, + String? customType}) async { + // final option = PublishOptions( + // channelType: channelType, + // messageType: RtmMessageType.string, + // customType: customType); + // final requestId = await nativeBindingRtmClientImpl.publish( + // channelName: channelName, + // message: message, + // length: message.length, + // option: option); + // return rtmResultHandler.request(requestId); + + final option = PublishOptions( + channelType: channelType, + messageType: RtmMessageType.string, + customType: customType); + try { + final requestId = await nativeBindingRtmClientImpl.publish( + channelName: channelName, + message: message, + length: message.length, + option: option); + final (PublishResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'publish'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'publish'); + return (status, null); + } + } + + @override + Future<(RtmStatus, PublishResult?)> publishBinaryMessage( + String channelName, Uint8List message, + {RtmChannelType channelType = RtmChannelType.message, + String? customType}) async { + // final option = PublishOptions( + // channelType: channelType, + // messageType: RtmMessageType.binary, + // customType: customType); + // final requestId = await nativeBindingRtmClientImpl.publishBinaryMessage( + // channelName: channelName, + // message: message, + // length: message.length, + // option: option); + // return rtmResultHandler.request(requestId); + + final option = PublishOptions( + channelType: channelType, + messageType: RtmMessageType.binary, + customType: customType); + try { + final requestId = await nativeBindingRtmClientImpl.publishBinaryMessage( + channelName: channelName, + message: message, + length: message.length, + option: option); + final (PublishResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'publishBinaryMessage'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'publishBinaryMessage'); + return (status, null); + } + } +} diff --git a/lib/src/impl/agora_stream_channel_impl_override.dart b/lib/src/impl/agora_stream_channel_impl_override.dart new file mode 100644 index 0000000..152c1c4 --- /dev/null +++ b/lib/src/impl/agora_stream_channel_impl_override.dart @@ -0,0 +1,40 @@ +import 'dart:typed_data' show Uint8List; + +import 'package:agora_rtm/src/agora_rtm_base.dart' + show TopicMessageOptions, RtmMessageType; +import 'package:agora_rtm/src/agora_rtm_client_ext.dart' show RtmStatus; +import 'package:agora_rtm/src/agora_stream_channel.dart' + show PublishTopicMessageResult; +import 'package:agora_rtm/src/impl/gen/agora_stream_channel_impl.dart' + as stream_channel_impl; + +class StreamChannelImplOverride extends stream_channel_impl.StreamChannelImpl { + StreamChannelImplOverride( + super.nativeBindingStreamChannelImpl, super.rtmResultHandler); + + @override + Future<(RtmStatus, PublishTopicMessageResult?)> publishTextMessage( + String topic, String message, + {int sendTs = 0, String? customType}) async { + final option = TopicMessageOptions( + messageType: RtmMessageType.string, + sendTs: sendTs, + customType: customType); + final requestId = await nativeBindingStreamChannelImpl.publishTextMessage( + topic: topic, message: message, length: message.length, option: option); + return rtmResultHandler.request(requestId); + } + + @override + Future<(RtmStatus, PublishTopicMessageResult?)> publishBinaryMessage( + String topic, Uint8List message, + {int sendTs = 0, String? customType}) async { + final option = TopicMessageOptions( + messageType: RtmMessageType.binary, + sendTs: sendTs, + customType: customType); + final requestId = await nativeBindingStreamChannelImpl.publishBinaryMessage( + topic: topic, message: message, length: message.length, option: option); + return rtmResultHandler.request(requestId); + } +} diff --git a/lib/src/impl/extensions.dart b/lib/src/impl/extensions.dart new file mode 100644 index 0000000..4bfeedb --- /dev/null +++ b/lib/src/impl/extensions.dart @@ -0,0 +1,27 @@ +import 'dart:async'; + +import 'package:agora_rtm/src/agora_rtm_client_ext.dart'; +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +extension IrisMethodChannelExt on IrisMethodChannel { + Future wrapRtmStatus( + int nativeReturnCode, String operation) async { + if (nativeReturnCode == 0) { + return Future.sync(() => RtmStatus.success(operation: operation)); + } + + final param = {'error_code': nativeReturnCode}; + final callApiResult = await invokeMethod(IrisMethodCall( + 'GetIrisRtmErrorReason', jsonEncode(param), + buffers: null)); + + final rm = callApiResult.data; + final result = rm['result']; + + return RtmStatus.error( + errorCode: nativeReturnCode.toString(), + operation: operation, + reason: result); + } +} diff --git a/lib/src/impl/gen/agora_rtm_client_impl.dart b/lib/src/impl/gen/agora_rtm_client_impl.dart new file mode 100644 index 0000000..a8e94d0 --- /dev/null +++ b/lib/src/impl/gen/agora_rtm_client_impl.dart @@ -0,0 +1,234 @@ +import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart'; +import 'package:agora_rtm/src/impl/extensions.dart'; +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_client_impl.dart' + as native_binding; + +class RtmClientImpl implements RtmClient { + RtmClientImpl(this.nativeBindingRtmClientImpl, this.rtmResultHandler); + + final RtmResultHandler rtmResultHandler; + + final native_binding.RtmClientImpl nativeBindingRtmClientImpl; + + @override + void addListener( + {void Function(LinkStateEvent event)? linkState, + void Function(MessageEvent event)? message, + void Function(PresenceEvent event)? presence, + void Function(TopicEvent event)? topic, + void Function(LockEvent event)? lock, + void Function(StorageEvent event)? storage, + void Function(TokenEvent event)? token}) { + throw UnimplementedError('Implement this function in sub-class.'); + } + + @override + void removeListener( + {void Function(LinkStateEvent event)? linkState, + void Function(MessageEvent event)? message, + void Function(PresenceEvent event)? presence, + void Function(TopicEvent event)? topic, + void Function(LockEvent event)? lock, + void Function(StorageEvent event)? storage, + void Function(TokenEvent event)? token}) { + throw UnimplementedError('Implement this function in sub-class.'); + } + + @override + Future release() async { + try { + await nativeBindingRtmClientImpl.release(); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(0, 'release'); + return status; + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'release'); + return status; + } + } + + @override + Future<(RtmStatus, LoginResult?)> login(String token) async { + try { + final requestId = await nativeBindingRtmClientImpl.login(token); + final (LoginResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'login'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'login'); + return (status, null); + } + } + + @override + Future<(RtmStatus, LogoutResult?)> logout() async { + try { + final requestId = await nativeBindingRtmClientImpl.logout(); + final (LogoutResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'logout'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'logout'); + return (status, null); + } + } + + @override + RtmStorage getStorage() { + // This function's implementation can't be generated automatically, please implement it manually. + throw UnimplementedError(); + } + + @override + RtmLock getLock() { + // This function's implementation can't be generated automatically, please implement it manually. + throw UnimplementedError(); + } + + @override + RtmPresence getPresence() { + // This function's implementation can't be generated automatically, please implement it manually. + throw UnimplementedError(); + } + + @override + Future<(RtmStatus, RenewTokenResult?)> renewToken(String token) async { + try { + final requestId = await nativeBindingRtmClientImpl.renewToken(token); + final (RenewTokenResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'renewToken'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'renewToken'); + return (status, null); + } + } + + @override + Future<(RtmStatus, PublishResult?)> publish( + String channelName, String message, + {RtmChannelType channelType = RtmChannelType.message, + String? customType}) async { + final option = + PublishOptions(channelType: channelType, customType: customType); + try { + final requestId = await nativeBindingRtmClientImpl.publish( + channelName: channelName, + message: message, + length: message.length, + option: option); + final (PublishResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'publish'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'publish'); + return (status, null); + } + } + + @override + Future<(RtmStatus, SubscribeResult?)> subscribe(String channelName, + {bool withMessage = true, + bool withMetadata = false, + bool withPresence = true, + bool withLock = false, + bool beQuiet = false}) async { + final options = SubscribeOptions( + withMessage: withMessage, + withMetadata: withMetadata, + withPresence: withPresence, + withLock: withLock, + beQuiet: beQuiet); + try { + final requestId = await nativeBindingRtmClientImpl.subscribe( + channelName: channelName, options: options); + final (SubscribeResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'subscribe'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'subscribe'); + return (status, null); + } + } + + @override + Future<(RtmStatus, UnsubscribeResult?)> unsubscribe( + String channelName) async { + try { + final requestId = + await nativeBindingRtmClientImpl.unsubscribe(channelName); + final (UnsubscribeResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'unsubscribe'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'unsubscribe'); + return (status, null); + } + } + + @override + Future<(RtmStatus, StreamChannel?)> createStreamChannel( + String channelName) async { + // This function's implementation can't be generated automatically, please implement it manually. + throw UnimplementedError(); + } + + @override + Future setParameters(String parameters) async { + try { + await nativeBindingRtmClientImpl.setParameters(parameters); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(0, 'setParameters'); + return status; + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'setParameters'); + return status; + } + } + + @override + Future<(RtmStatus, PublishResult?)> publishBinaryMessage( + String channelName, Uint8List message, + {RtmChannelType channelType = RtmChannelType.message, + String? customType}) async { + final option = + PublishOptions(channelType: channelType, customType: customType); + try { + final requestId = await nativeBindingRtmClientImpl.publishBinaryMessage( + channelName: channelName, + message: message, + length: message.length, + option: option); + final (PublishResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'publishBinaryMessage'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmClientImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'publishBinaryMessage'); + return (status, null); + } + } +} diff --git a/lib/src/impl/gen/agora_rtm_lock_impl.dart b/lib/src/impl/gen/agora_rtm_lock_impl.dart new file mode 100644 index 0000000..ae1c930 --- /dev/null +++ b/lib/src/impl/gen/agora_rtm_lock_impl.dart @@ -0,0 +1,136 @@ +import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart'; +import 'package:agora_rtm/src/impl/extensions.dart'; +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_lock_impl.dart' + as native_binding; + +class RtmLockImpl implements RtmLock { + RtmLockImpl(this.nativeBindingRtmLockImpl, this.rtmResultHandler); + + final RtmResultHandler rtmResultHandler; + + final native_binding.RtmLockImpl nativeBindingRtmLockImpl; + + @override + Future<(RtmStatus, SetLockResult?)> setLock( + String channelName, RtmChannelType channelType, String lockName, + {int ttl = 10}) async { + try { + final requestId = await nativeBindingRtmLockImpl.setLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + ttl: ttl); + final (SetLockResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'setLock'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'setLock'); + return (status, null); + } + } + + @override + Future<(RtmStatus, GetLocksResult?)> getLocks( + String channelName, RtmChannelType channelType) async { + try { + final requestId = await nativeBindingRtmLockImpl.getLocks( + channelName: channelName, channelType: channelType); + final (GetLocksResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'getLocks'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getLocks'); + return (status, null); + } + } + + @override + Future<(RtmStatus, RemoveLockResult?)> removeLock( + String channelName, RtmChannelType channelType, String lockName) async { + try { + final requestId = await nativeBindingRtmLockImpl.removeLock( + channelName: channelName, + channelType: channelType, + lockName: lockName); + final (RemoveLockResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'removeLock'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'removeLock'); + return (status, null); + } + } + + @override + Future<(RtmStatus, AcquireLockResult?)> acquireLock( + String channelName, RtmChannelType channelType, String lockName, + {bool retry = false}) async { + try { + final requestId = await nativeBindingRtmLockImpl.acquireLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + retry: retry); + final (AcquireLockResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'acquireLock'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'acquireLock'); + return (status, null); + } + } + + @override + Future<(RtmStatus, ReleaseLockResult?)> releaseLock( + String channelName, RtmChannelType channelType, String lockName) async { + try { + final requestId = await nativeBindingRtmLockImpl.releaseLock( + channelName: channelName, + channelType: channelType, + lockName: lockName); + final (ReleaseLockResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'releaseLock'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'releaseLock'); + return (status, null); + } + } + + @override + Future<(RtmStatus, RevokeLockResult?)> revokeLock(String channelName, + RtmChannelType channelType, String lockName, String owner) async { + try { + final requestId = await nativeBindingRtmLockImpl.revokeLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + owner: owner); + final (RevokeLockResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'revokeLock'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmLockImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'revokeLock'); + return (status, null); + } + } +} diff --git a/lib/src/impl/gen/agora_rtm_presence_impl.dart b/lib/src/impl/gen/agora_rtm_presence_impl.dart new file mode 100644 index 0000000..bc4b8a7 --- /dev/null +++ b/lib/src/impl/gen/agora_rtm_presence_impl.dart @@ -0,0 +1,157 @@ +import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart'; +import 'package:agora_rtm/src/impl/extensions.dart'; +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_presence_impl.dart' + as native_binding; + +class RtmPresenceImpl implements RtmPresence { + RtmPresenceImpl(this.nativeBindingRtmPresenceImpl, this.rtmResultHandler); + + final RtmResultHandler rtmResultHandler; + + final native_binding.RtmPresenceImpl nativeBindingRtmPresenceImpl; + + @override + Future<(RtmStatus, WhoNowResult?)> whoNow( + String channelName, RtmChannelType channelType, + {bool includeUserId = true, + bool includeState = false, + String page = ''}) async { + final options = PresenceOptions( + includeUserId: includeUserId, includeState: includeState, page: page); + try { + final requestId = await nativeBindingRtmPresenceImpl.whoNow( + channelName: channelName, channelType: channelType, options: options); + final (WhoNowResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'whoNow'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'whoNow'); + return (status, null); + } + } + + @override + Future<(RtmStatus, WhereNowResult?)> whereNow(String userId) async { + try { + final requestId = await nativeBindingRtmPresenceImpl.whereNow(userId); + final (WhereNowResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'whereNow'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'whereNow'); + return (status, null); + } + } + + @override + Future<(RtmStatus, SetStateResult?)> setState(String channelName, + RtmChannelType channelType, Map state) async { + final itemsStateItem = state.entries + .map((entry) => StateItem(key: entry.key, value: entry.value)) + .toList(); + try { + final requestId = await nativeBindingRtmPresenceImpl.setState( + channelName: channelName, + channelType: channelType, + items: itemsStateItem, + count: itemsStateItem.length); + final (SetStateResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'setState'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'setState'); + return (status, null); + } + } + + @override + Future<(RtmStatus, RemoveStateResult?)> removeState( + String channelName, RtmChannelType channelType, + {List states = const []}) async { + try { + final requestId = await nativeBindingRtmPresenceImpl.removeState( + channelName: channelName, + channelType: channelType, + keys: states, + count: states.length); + final (RemoveStateResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'removeState'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'removeState'); + return (status, null); + } + } + + @override + Future<(RtmStatus, GetStateResult?)> getState( + String channelName, RtmChannelType channelType, String userId) async { + try { + final requestId = await nativeBindingRtmPresenceImpl.getState( + channelName: channelName, channelType: channelType, userId: userId); + final (GetStateResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'getState'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getState'); + return (status, null); + } + } + + @override + Future<(RtmStatus, GetOnlineUsersResult?)> getOnlineUsers( + String channelName, RtmChannelType channelType, + {bool includeUserId = true, + bool includeState = false, + String? page}) async { + final options = GetOnlineUsersOptions( + includeUserId: includeUserId, includeState: includeState, page: page); + try { + final requestId = await nativeBindingRtmPresenceImpl.getOnlineUsers( + channelName: channelName, channelType: channelType, options: options); + final (GetOnlineUsersResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'getOnlineUsers'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getOnlineUsers'); + return (status, null); + } + } + + @override + Future<(RtmStatus, GetUserChannelsResult?)> getUserChannels( + String userId) async { + try { + final requestId = + await nativeBindingRtmPresenceImpl.getUserChannels(userId); + final (GetUserChannelsResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'getUserChannels'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmPresenceImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getUserChannels'); + return (status, null); + } + } +} diff --git a/lib/src/impl/gen/agora_rtm_storage_impl.dart b/lib/src/impl/gen/agora_rtm_storage_impl.dart new file mode 100644 index 0000000..9bc77d8 --- /dev/null +++ b/lib/src/impl/gen/agora_rtm_storage_impl.dart @@ -0,0 +1,268 @@ +import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart'; +import 'package:agora_rtm/src/impl/extensions.dart'; +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_storage_impl.dart' + as native_binding; + +class RtmStorageImpl implements RtmStorage { + RtmStorageImpl(this.nativeBindingRtmStorageImpl, this.rtmResultHandler); + + final RtmResultHandler rtmResultHandler; + + final native_binding.RtmStorageImpl nativeBindingRtmStorageImpl; + + @override + Future<(RtmStatus, SetChannelMetadataResult?)> setChannelMetadata( + String channelName, + RtmChannelType channelType, + List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false, + String lockName = ''}) async { + final data = Metadata( + majorRevision: majorRevision, + items: metadata, + itemCount: metadata.length); + final options = + MetadataOptions(recordTs: recordTs, recordUserId: recordUserId); + try { + final requestId = await nativeBindingRtmStorageImpl.setChannelMetadata( + channelName: channelName, + channelType: channelType, + data: data, + options: options, + lockName: lockName); + final (SetChannelMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'setChannelMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'setChannelMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, UpdateChannelMetadataResult?)> updateChannelMetadata( + String channelName, + RtmChannelType channelType, + List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false, + String lockName = ''}) async { + final data = Metadata( + majorRevision: majorRevision, + items: metadata, + itemCount: metadata.length); + final options = + MetadataOptions(recordTs: recordTs, recordUserId: recordUserId); + try { + final requestId = await nativeBindingRtmStorageImpl.updateChannelMetadata( + channelName: channelName, + channelType: channelType, + data: data, + options: options, + lockName: lockName); + final (UpdateChannelMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'updateChannelMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'updateChannelMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, RemoveChannelMetadataResult?)> removeChannelMetadata( + String channelName, RtmChannelType channelType, + {int majorRevision = -1, + List metadata = const [], + bool recordTs = false, + bool recordUserId = false, + String lockName = ''}) async { + final data = Metadata( + majorRevision: majorRevision, + items: metadata, + itemCount: metadata.length); + final options = + MetadataOptions(recordTs: recordTs, recordUserId: recordUserId); + try { + final requestId = await nativeBindingRtmStorageImpl.removeChannelMetadata( + channelName: channelName, + channelType: channelType, + data: data, + options: options, + lockName: lockName); + final (RemoveChannelMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'removeChannelMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'removeChannelMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, GetChannelMetadataResult?)> getChannelMetadata( + String channelName, RtmChannelType channelType) async { + try { + final requestId = await nativeBindingRtmStorageImpl.getChannelMetadata( + channelName: channelName, channelType: channelType); + final (GetChannelMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'getChannelMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getChannelMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, SetUserMetadataResult?)> setUserMetadata( + String userId, List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false}) async { + final data = Metadata( + majorRevision: majorRevision, + items: metadata, + itemCount: metadata.length); + final options = + MetadataOptions(recordTs: recordTs, recordUserId: recordUserId); + try { + final requestId = await nativeBindingRtmStorageImpl.setUserMetadata( + userId: userId, data: data, options: options); + final (SetUserMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'setUserMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'setUserMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, UpdateUserMetadataResult?)> updateUserMetadata( + String userId, List metadata, + {int majorRevision = -1, + bool recordTs = false, + bool recordUserId = false}) async { + final data = Metadata( + majorRevision: majorRevision, + items: metadata, + itemCount: metadata.length); + final options = + MetadataOptions(recordTs: recordTs, recordUserId: recordUserId); + try { + final requestId = await nativeBindingRtmStorageImpl.updateUserMetadata( + userId: userId, data: data, options: options); + final (UpdateUserMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'updateUserMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'updateUserMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, RemoveUserMetadataResult?)> removeUserMetadata( + String userId, + {int majorRevision = -1, + List metadata = const [], + bool recordTs = false, + bool recordUserId = false}) async { + final data = Metadata( + majorRevision: majorRevision, + items: metadata, + itemCount: metadata.length); + final options = + MetadataOptions(recordTs: recordTs, recordUserId: recordUserId); + try { + final requestId = await nativeBindingRtmStorageImpl.removeUserMetadata( + userId: userId, data: data, options: options); + final (RemoveUserMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'removeUserMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'removeUserMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, GetUserMetadataResult?)> getUserMetadata( + String userId) async { + try { + final requestId = + await nativeBindingRtmStorageImpl.getUserMetadata(userId); + final (GetUserMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'getUserMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getUserMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, SubscribeUserMetadataResult?)> subscribeUserMetadata( + String userId) async { + try { + final requestId = + await nativeBindingRtmStorageImpl.subscribeUserMetadata(userId); + final (SubscribeUserMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'subscribeUserMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'subscribeUserMetadata'); + return (status, null); + } + } + + @override + Future<(RtmStatus, UnsubscribeUserMetadataResult?)> unsubscribeUserMetadata( + String userId) async { + try { + final requestId = + await nativeBindingRtmStorageImpl.unsubscribeUserMetadata(userId); + final (UnsubscribeUserMetadataResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'unsubscribeUserMetadata'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingRtmStorageImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'unsubscribeUserMetadata'); + return (status, null); + } + } +} diff --git a/lib/src/impl/gen/agora_stream_channel_impl.dart b/lib/src/impl/gen/agora_stream_channel_impl.dart new file mode 100644 index 0000000..ffe9533 --- /dev/null +++ b/lib/src/impl/gen/agora_stream_channel_impl.dart @@ -0,0 +1,242 @@ +import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart'; +import 'package:agora_rtm/src/impl/extensions.dart'; +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_stream_channel_impl.dart' + as native_binding; + +class StreamChannelImpl implements StreamChannel { + StreamChannelImpl(this.nativeBindingStreamChannelImpl, this.rtmResultHandler); + + final RtmResultHandler rtmResultHandler; + + final native_binding.StreamChannelImpl nativeBindingStreamChannelImpl; + + @override + Future<(RtmStatus, JoinResult?)> join( + {String? token, + bool withMetadata = false, + bool withPresence = true, + bool withLock = false, + bool beQuiet = false}) async { + final options = JoinChannelOptions( + token: token, + withMetadata: withMetadata, + withPresence: withPresence, + withLock: withLock, + beQuiet: beQuiet); + try { + final requestId = await nativeBindingStreamChannelImpl.join(options); + final (JoinResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'join'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'join'); + return (status, null); + } + } + + @override + Future<(RtmStatus, RenewTokenResult?)> renewToken(String token) async { + try { + final requestId = await nativeBindingStreamChannelImpl.renewToken(token); + final (RenewTokenResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'renewToken'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'renewToken'); + return (status, null); + } + } + + @override + Future<(RtmStatus, LeaveResult?)> leave() async { + try { + final requestId = await nativeBindingStreamChannelImpl.leave(); + final (LeaveResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'leave'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'leave'); + return (status, null); + } + } + + @override + Future<(RtmStatus, String?)> getChannelName() async { + try { + final result = await nativeBindingStreamChannelImpl.getChannelName(); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(0, 'getChannelName'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getChannelName'); + return (status, null); + } + } + + @override + Future<(RtmStatus, JoinTopicResult?)> joinTopic(String topic, + {RtmMessageQos qos = RtmMessageQos.unordered, + RtmMessagePriority priority = RtmMessagePriority.normal, + String meta = '', + bool syncWithMedia = false}) async { + final options = JoinTopicOptions( + qos: qos, priority: priority, meta: meta, syncWithMedia: syncWithMedia); + try { + final requestId = await nativeBindingStreamChannelImpl.joinTopic( + topic: topic, options: options); + final (JoinTopicResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'joinTopic'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'joinTopic'); + return (status, null); + } + } + + @override + Future<(RtmStatus, LeaveTopicResult?)> leaveTopic(String topic) async { + try { + final requestId = await nativeBindingStreamChannelImpl.leaveTopic(topic); + final (LeaveTopicResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'leaveTopic'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'leaveTopic'); + return (status, null); + } + } + + @override + Future<(RtmStatus, SubscribeTopicResult?)> subscribeTopic(String topic, + {List users = const []}) async { + final options = TopicOptions(users: users, userCount: users.length); + try { + final requestId = await nativeBindingStreamChannelImpl.subscribeTopic( + topic: topic, options: options); + final (SubscribeTopicResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'subscribeTopic'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'subscribeTopic'); + return (status, null); + } + } + + @override + Future<(RtmStatus, UnsubscribeTopicResult?)> unsubscribeTopic(String topic, + {List users = const []}) async { + final options = TopicOptions(users: users, userCount: users.length); + try { + final requestId = await nativeBindingStreamChannelImpl.unsubscribeTopic( + topic: topic, options: options); + final (UnsubscribeTopicResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'unsubscribeTopic'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'unsubscribeTopic'); + return (status, null); + } + } + + @override + Future<(RtmStatus, GetSubscribedUserListResult?)> getSubscribedUserList( + String topic) async { + try { + final requestId = + await nativeBindingStreamChannelImpl.getSubscribedUserList(topic); + final (GetSubscribedUserListResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'getSubscribedUserList'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'getSubscribedUserList'); + return (status, null); + } + } + + @override + Future release() async { + try { + await nativeBindingStreamChannelImpl.release(); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(0, 'release'); + return status; + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'release'); + return status; + } + } + + @override + Future<(RtmStatus, PublishTopicMessageResult?)> publishTextMessage( + String topic, String message, + {int sendTs = 0, String? customType}) async { + final option = TopicMessageOptions(sendTs: sendTs, customType: customType); + try { + final requestId = await nativeBindingStreamChannelImpl.publishTextMessage( + topic: topic, + message: message, + length: message.length, + option: option); + final (PublishTopicMessageResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'publishTextMessage'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'publishTextMessage'); + return (status, null); + } + } + + @override + Future<(RtmStatus, PublishTopicMessageResult?)> publishBinaryMessage( + String topic, Uint8List message, + {int sendTs = 0, String? customType}) async { + final option = TopicMessageOptions(sendTs: sendTs, customType: customType); + try { + final requestId = + await nativeBindingStreamChannelImpl.publishBinaryMessage( + topic: topic, + message: message, + length: message.length, + option: option); + final (PublishTopicMessageResult result, RtmErrorCode errorCode) = + await rtmResultHandler.request(requestId); + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(errorCode.value(), 'publishBinaryMessage'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await nativeBindingStreamChannelImpl.irisMethodChannel + .wrapRtmStatus(e.code, 'publishBinaryMessage'); + return (status, null); + } + } +} diff --git a/lib/src/impl/gen/rtm_result_handler.dart b/lib/src/impl/gen/rtm_result_handler.dart new file mode 100644 index 0000000..be8a23b --- /dev/null +++ b/lib/src/impl/gen/rtm_result_handler.dart @@ -0,0 +1,432 @@ +import 'dart:async'; + +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_client.dart'; + +abstract class RtmResultHandler { + RtmResultHandler() { + rtmEventHandler = RtmEventHandler( + onLinkStateEvent: onLinkStateEvent, + onMessageEvent: onMessageEvent, + onPresenceEvent: onPresenceEvent, + onTopicEvent: onTopicEvent, + onLockEvent: onLockEvent, + onStorageEvent: onStorageEvent, + onJoinResult: onJoinResult, + onLeaveResult: onLeaveResult, + onPublishTopicMessageResult: onPublishTopicMessageResult, + onJoinTopicResult: onJoinTopicResult, + onLeaveTopicResult: onLeaveTopicResult, + onSubscribeTopicResult: onSubscribeTopicResult, + onUnsubscribeTopicResult: onUnsubscribeTopicResult, + onGetSubscribedUserListResult: onGetSubscribedUserListResult, + onConnectionStateChanged: onConnectionStateChanged, + onTokenPrivilegeWillExpire: onTokenPrivilegeWillExpire, + onSubscribeResult: onSubscribeResult, + onUnsubscribeResult: onUnsubscribeResult, + onPublishResult: onPublishResult, + onLoginResult: onLoginResult, + onLogoutResult: onLogoutResult, + onRenewTokenResult: onRenewTokenResult, + onSetChannelMetadataResult: onSetChannelMetadataResult, + onUpdateChannelMetadataResult: onUpdateChannelMetadataResult, + onRemoveChannelMetadataResult: onRemoveChannelMetadataResult, + onGetChannelMetadataResult: onGetChannelMetadataResult, + onSetUserMetadataResult: onSetUserMetadataResult, + onUpdateUserMetadataResult: onUpdateUserMetadataResult, + onRemoveUserMetadataResult: onRemoveUserMetadataResult, + onGetUserMetadataResult: onGetUserMetadataResult, + onSubscribeUserMetadataResult: onSubscribeUserMetadataResult, + onUnsubscribeUserMetadataResult: onUnsubscribeUserMetadataResult, + onSetLockResult: onSetLockResult, + onRemoveLockResult: onRemoveLockResult, + onReleaseLockResult: onReleaseLockResult, + onAcquireLockResult: onAcquireLockResult, + onRevokeLockResult: onRevokeLockResult, + onGetLocksResult: onGetLocksResult, + onWhoNowResult: onWhoNowResult, + onGetOnlineUsersResult: onGetOnlineUsersResult, + onWhereNowResult: onWhereNowResult, + onGetUserChannelsResult: onGetUserChannelsResult, + onPresenceSetStateResult: onPresenceSetStateResult, + onPresenceRemoveStateResult: onPresenceRemoveStateResult, + onPresenceGetStateResult: onPresenceGetStateResult, + ); + } + late final RtmEventHandler rtmEventHandler; + + Future request(int requestId); + + void response(int requestId, (Object result, RtmErrorCode errorCode) data); + + @visibleForTesting + @protected + void onLinkStateEvent(LinkStateEvent event) {} + + @visibleForTesting + @protected + void onMessageEvent(MessageEvent event) {} + + @visibleForTesting + @protected + void onPresenceEvent(PresenceEvent event) {} + + @visibleForTesting + @protected + void onTopicEvent(TopicEvent event) {} + + @visibleForTesting + @protected + void onLockEvent(LockEvent event) {} + + @visibleForTesting + @protected + void onStorageEvent(StorageEvent event) {} + + @visibleForTesting + @protected + void onJoinResult(int requestId, String channelName, String userId, + RtmErrorCode errorCode) { + final result = JoinResult(channelName: channelName, userId: userId); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onLeaveResult(int requestId, String channelName, String userId, + RtmErrorCode errorCode) { + final result = LeaveResult(channelName: channelName, userId: userId); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onPublishTopicMessageResult( + int requestId, String channelName, String topic, RtmErrorCode errorCode) { + final result = + PublishTopicMessageResult(channelName: channelName, topic: topic); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onJoinTopicResult(int requestId, String channelName, String userId, + String topic, String meta, RtmErrorCode errorCode) { + final result = JoinTopicResult( + channelName: channelName, userId: userId, topic: topic, meta: meta); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onLeaveTopicResult(int requestId, String channelName, String userId, + String topic, String meta, RtmErrorCode errorCode) { + final result = LeaveTopicResult( + channelName: channelName, userId: userId, topic: topic, meta: meta); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onSubscribeTopicResult( + int requestId, + String channelName, + String userId, + String topic, + UserList succeedUsers, + UserList failedUsers, + RtmErrorCode errorCode) { + final succeedUsersUserList = succeedUsers.users ?? []; + final failedUsersUserList = failedUsers.users ?? []; + final result = SubscribeTopicResult( + channelName: channelName, + userId: userId, + topic: topic, + succeedUsers: succeedUsersUserList, + failedUsers: failedUsersUserList); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onUnsubscribeTopicResult( + int requestId, String channelName, String topic, RtmErrorCode errorCode) { + final result = + UnsubscribeTopicResult(channelName: channelName, topic: topic); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onGetSubscribedUserListResult(int requestId, String channelName, + String topic, UserList users, RtmErrorCode errorCode) { + final result = GetSubscribedUserListResult( + channelName: channelName, topic: topic, users: users); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onConnectionStateChanged(String channelName, RtmConnectionState state, + RtmConnectionChangeReason reason) {} + + @visibleForTesting + @protected + void onTokenPrivilegeWillExpire(String channelName) {} + + @visibleForTesting + @protected + void onSubscribeResult( + int requestId, String channelName, RtmErrorCode errorCode) { + final result = SubscribeResult(channelName: channelName); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onUnsubscribeResult( + int requestId, String channelName, RtmErrorCode errorCode) { + final result = UnsubscribeResult(channelName: channelName); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onPublishResult(int requestId, RtmErrorCode errorCode) { + final result = PublishResult(); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onLoginResult(int requestId, RtmErrorCode errorCode) { + final result = LoginResult(); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onLogoutResult(int requestId, RtmErrorCode errorCode) { + final result = LogoutResult(); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onRenewTokenResult(int requestId, RtmServiceType serverType, + String channelName, RtmErrorCode errorCode) { + final result = + RenewTokenResult(serverType: serverType, channelName: channelName); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onSetChannelMetadataResult(int requestId, String channelName, + RtmChannelType channelType, RtmErrorCode errorCode) { + final result = SetChannelMetadataResult( + channelName: channelName, channelType: channelType); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onUpdateChannelMetadataResult(int requestId, String channelName, + RtmChannelType channelType, RtmErrorCode errorCode) { + final result = UpdateChannelMetadataResult( + channelName: channelName, channelType: channelType); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onRemoveChannelMetadataResult(int requestId, String channelName, + RtmChannelType channelType, RtmErrorCode errorCode) { + final result = RemoveChannelMetadataResult( + channelName: channelName, channelType: channelType); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onGetChannelMetadataResult(int requestId, String channelName, + RtmChannelType channelType, Metadata data, RtmErrorCode errorCode) { + final result = GetChannelMetadataResult( + channelName: channelName, channelType: channelType, data: data); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onSetUserMetadataResult( + int requestId, String userId, RtmErrorCode errorCode) { + final result = SetUserMetadataResult(userId: userId); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onUpdateUserMetadataResult( + int requestId, String userId, RtmErrorCode errorCode) { + final result = UpdateUserMetadataResult(userId: userId); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onRemoveUserMetadataResult( + int requestId, String userId, RtmErrorCode errorCode) { + final result = RemoveUserMetadataResult(userId: userId); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onGetUserMetadataResult( + int requestId, String userId, Metadata data, RtmErrorCode errorCode) { + final result = GetUserMetadataResult(userId: userId, data: data); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onSubscribeUserMetadataResult( + int requestId, String userId, RtmErrorCode errorCode) { + final result = SubscribeUserMetadataResult(userId: userId); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onUnsubscribeUserMetadataResult( + int requestId, String userId, RtmErrorCode errorCode) { + final result = UnsubscribeUserMetadataResult(userId: userId); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onSetLockResult(int requestId, String channelName, + RtmChannelType channelType, String lockName, RtmErrorCode errorCode) { + final result = SetLockResult( + channelName: channelName, channelType: channelType, lockName: lockName); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onRemoveLockResult(int requestId, String channelName, + RtmChannelType channelType, String lockName, RtmErrorCode errorCode) { + final result = RemoveLockResult( + channelName: channelName, channelType: channelType, lockName: lockName); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onReleaseLockResult(int requestId, String channelName, + RtmChannelType channelType, String lockName, RtmErrorCode errorCode) { + final result = ReleaseLockResult( + channelName: channelName, channelType: channelType, lockName: lockName); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onAcquireLockResult( + int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode, + String errorDetails) { + final result = AcquireLockResult( + channelName: channelName, + channelType: channelType, + lockName: lockName, + errorDetails: errorDetails); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onRevokeLockResult(int requestId, String channelName, + RtmChannelType channelType, String lockName, RtmErrorCode errorCode) { + final result = RevokeLockResult( + channelName: channelName, channelType: channelType, lockName: lockName); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onGetLocksResult( + int requestId, + String channelName, + RtmChannelType channelType, + List lockDetailList, + int count, + RtmErrorCode errorCode) { + final result = GetLocksResult( + channelName: channelName, + channelType: channelType, + lockDetailList: lockDetailList, + count: count); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onWhoNowResult(int requestId, List userStateList, int count, + String nextPage, RtmErrorCode errorCode) { + final result = WhoNowResult( + userStateList: userStateList, count: count, nextPage: nextPage); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onGetOnlineUsersResult(int requestId, List userStateList, + int count, String nextPage, RtmErrorCode errorCode) { + final result = GetOnlineUsersResult( + userStateList: userStateList, count: count, nextPage: nextPage); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onWhereNowResult(int requestId, List channels, int count, + RtmErrorCode errorCode) { + final result = WhereNowResult(channels: channels, count: count); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onGetUserChannelsResult( + int requestId, ChannelInfo channels, int count, RtmErrorCode errorCode) { + final result = GetUserChannelsResult(channels: channels, count: count); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onPresenceSetStateResult(int requestId, RtmErrorCode errorCode) { + final result = SetStateResult(); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onPresenceRemoveStateResult(int requestId, RtmErrorCode errorCode) { + final result = RemoveStateResult(); + response(requestId, (result, errorCode)); + } + + @visibleForTesting + @protected + void onPresenceGetStateResult( + int requestId, UserState state, RtmErrorCode errorCode) { + final result = GetStateResult(state: state); + response(requestId, (result, errorCode)); + } +} diff --git a/lib/src/impl/rtm_result_handler_impl.dart b/lib/src/impl/rtm_result_handler_impl.dart new file mode 100644 index 0000000..ab91982 --- /dev/null +++ b/lib/src/impl/rtm_result_handler_impl.dart @@ -0,0 +1,88 @@ +import 'dart:async'; + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart' as rtm_result; + +class RtmResultHandlerImpl extends rtm_result.RtmResultHandler { + final Map> + _pendingRequests = {}; + final Map _pendingResponses = + {}; + final Map _listenerMap = {}; + + @override + Future request(int requestId) { + if (_pendingResponses.containsKey(requestId)) { + final response = _pendingResponses.remove(requestId); + return Future.value(response! as T); + } + + final completer = + _pendingRequests.putIfAbsent(requestId, () => Completer()); + return completer.future.then((v) => v as T); + } + + @override + void response(int requestId, (Object result, RtmErrorCode errorCode) data) { + if (_pendingRequests.containsKey(requestId)) { + final completer = _pendingRequests.remove(requestId); + completer?.complete(data); + return; + } + + _pendingResponses.putIfAbsent(requestId, () => data); + } + + Function? _funcOf(String key) { + return _listenerMap[key] as Function?; + } + + @override + void onLinkStateEvent(LinkStateEvent event) { + _funcOf('linkState')?.call(event); + } + + @override + void onMessageEvent(MessageEvent event) { + _funcOf('message')?.call(event); + } + + @override + void onPresenceEvent(PresenceEvent event) { + _funcOf('presence')?.call(event); + } + + @override + void onTopicEvent(TopicEvent event) { + _funcOf('topic')?.call(event); + } + + @override + void onLockEvent(LockEvent event) { + _funcOf('lock')?.call(event); + } + + @override + void onStorageEvent(StorageEvent event) { + _funcOf('storage')?.call(event); + } + + @override + void onConnectionStateChanged(String channelName, RtmConnectionState state, + RtmConnectionChangeReason reason) { + _funcOf('connection')?.call(channelName, state, reason); + } + + @override + void onTokenPrivilegeWillExpire(String channelName) { + _funcOf('token')?.call(TokenEvent(channelName)); + } + + void setListener(String key, Object listener) { + _listenerMap.putIfAbsent(key, () => listener); + } + + void removeListener(String key) { + _listenerMap.remove(key); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..0f696a6 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,80 @@ +name: agora_rtm +description: "Flutter wrapper around the Agora Real Time Message SDKs for Android and iOS." +version: 2.2.1 +homepage: https://www.agora.io +repository: https://github.com/AgoraIO-Extensions/Agora-Flutter-RTM-SDK.git + +environment: + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.10.0' + +dependencies: + flutter: + sdk: flutter + json_annotation: ^4.4.0 + ffi: '>=1.1.2' + async: '>=2.8.2' + meta: '>=1.7.0' + iris_method_channel: 2.2.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: '>=1.0.0' + build_runner: ^2.1.7 + json_serializable: ^6.1.1 + ffigen: ^4.1.2 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: io.agora.agorartm.agora_rtm + pluginClass: AgoraRtmPlugin + ios: + pluginClass: AgoraRtmPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/scripts/artifacts_version.sh b/scripts/artifacts_version.sh new file mode 100644 index 0000000..b291dfd --- /dev/null +++ b/scripts/artifacts_version.sh @@ -0,0 +1,6 @@ +set -e + +export IRIS_CDN_URL_ANDROID="https://download.agora.io/sdk/release/iris_2.2.1-dev.4_RTM_Android_Video_20240808_0551_578.zip" +export IRIS_CDN_URL_IOS="https://download.agora.io/sdk/release/iris_2.2.1-dev.4_RTM_iOS_Video_20240808_0551_479.zip" +export IRIS_CDN_URL_MACOS="https://download.agora.io/sdk/release/iris_4.3.2-build.1_DCG_Mac_Video_20240604_0500_404.zip" +export IRIS_CDN_URL_WINDOWS="https://download.agora.io/sdk/release/iris_4.3.2-build.1_DCG_Windows_Video_20240604_0456_441.zip" diff --git a/scripts/build-iris.sh b/scripts/build-iris.sh new file mode 100644 index 0000000..652752b --- /dev/null +++ b/scripts/build-iris.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -e +set -x + +# Usage: +# bash scripts/build-iris.sh + +IRIS_PROJECT_PATH=$1 +BUILD_TYPE=$2 # Debug|Release +NATIVE_SDK_PATH_NAME=$3 # Agora_Native_SDK_for_Android_MINI_RTM +AGORA_FLUTTER_PROJECT_PATH=$(pwd) +SCRIPTS_PATH=$(dirname "$0") +ABIS="arm64-v8a armeabi-v7a x86_64" +IRIS_TYPE="RTM" # DCG|RTM +PLATFORM=$4 # android/ios/macos/windows/web + +build_android() { + + bash $IRIS_PROJECT_PATH/ci/build-android.sh buildALL $BUILD_TYPE Video $IRIS_TYPE + + IRIS_OUTPUT=${IRIS_PROJECT_PATH}/build/android/$IRIS_TYPE/ALL_ARCHITECTURE/output/$BUILD_TYPE + + for ABI in ${ABIS}; + do + echo "Copying ${IRIS_OUTPUT}/$ABI/libAgoraRtmWrapper.so to $AGORA_FLUTTER_PROJECT_PATH/android/libs/$ABI/libAgoraRtmWrapper.so" + mkdir -p "$AGORA_FLUTTER_PROJECT_PATH/android/libs/$ABI/" + + cp -RP "${IRIS_OUTPUT}/$ABI/libAgoraRtmWrapper.so" \ + "$AGORA_FLUTTER_PROJECT_PATH/android/libs/$ABI/libAgoraRtmWrapper.so" + + cp -RP "${IRIS_OUTPUT}/$ABI/libIrisDebugger.so" "$AGORA_FLUTTER_PROJECT_PATH/test_shard/iris_tester/android/libs/$ABI/libIrisDebugger.so" + + done; + + echo "Copying ${IRIS_OUTPUT}/AgoraRtmWrapper.jar to $AGORA_FLUTTER_PROJECT_PATH/android/libs/AgoraRtmWrapper.jar" + cp -r "${IRIS_OUTPUT}/AgoraRtmWrapper.jar" "$AGORA_FLUTTER_PROJECT_PATH/android/libs/AgoraRtmWrapper.jar" + + for ABI in ${ABIS}; + do + echo "Copying $IRIS_PROJECT_PATH/third_party/agora/rtm/libs/$NATIVE_SDK_PATH_NAME/rtm/sdk/$ABI/ to $AGORA_FLUTTER_PROJECT_PATH/android/libs/$ABI/" + cp -r "$IRIS_PROJECT_PATH/third_party/agora/rtm/libs/$NATIVE_SDK_PATH_NAME/rtm/sdk/$ABI/" \ + "$AGORA_FLUTTER_PROJECT_PATH/android/libs/$ABI/" + done; + + echo "Copying $IRIS_PROJECT_PATH/third_party/agora/rtm/libs/${NATIVE_SDK_PATH_NAME}/rtm/sdk/agora-rtm-sdk.jar to $AGORA_FLUTTER_PROJECT_PATH/android/libs/libs/agora-rtm-sdk.jar" + cp -r "$IRIS_PROJECT_PATH/third_party/agora/rtm/libs/${NATIVE_SDK_PATH_NAME}/rtm/sdk/agora-rtm-sdk.jar" "$AGORA_FLUTTER_PROJECT_PATH/android/libs/agora-rtm-sdk.jar" +} + +build_mac() { + bash $IRIS_PROJECT_PATH/ci/build-mac.sh buildALL $BUILD_TYPE Video $IRIS_TYPE +} + +if [[ $PLATFORM == "android" ]]; then + build_android +elif [[ $PLATFORM == "macos" ]]; then + build_mac +else + echo "Not implemented" +fi diff --git a/scripts/code_gen.sh b/scripts/code_gen.sh new file mode 100644 index 0000000..99b2343 --- /dev/null +++ b/scripts/code_gen.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -e +set -x + +RTC_VERSION=$1 +MY_PATH=$(realpath $(dirname "$0")) +PROJECT_ROOT=$(realpath ${MY_PATH}/..) + +pushd ${PROJECT_ROOT} + +flutter packages get +bash ${PROJECT_ROOT}/scripts/terra/build.sh ${RTC_VERSION} +bash ${MY_PATH}/flutter-build-runner.sh +bash ${PROJECT_ROOT}/scripts/testcase_gen/build.sh + +dart format . + +popd \ No newline at end of file diff --git a/scripts/dart_pub_publish_check.sh b/scripts/dart_pub_publish_check.sh new file mode 100644 index 0000000..e410065 --- /dev/null +++ b/scripts/dart_pub_publish_check.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# The log of `dart pub publish`` will be written into `${PUB_CACHE}/log/pub_log.txt` +dart pub publish --dry-run --verbose + +if [[ ! -f "${PUB_CACHE}/log/pub_log.txt" ]]; then + echo "The ${PUB_CACHE}/log/pub_log.txt is not exist." + exit 1 +fi + +RET=$(grep 'ERR' ${PUB_CACHE}/log/pub_log.txt) + +if [[ ! -z $RET ]]; then + echo "\n" + echo "=================================== ERR =============================================" + echo "There are some ERR when run the \`dart pub publish --dry-run\`, please check the log." + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/scripts/download_unzip_iris_cdn_artifacts.sh b/scripts/download_unzip_iris_cdn_artifacts.sh new file mode 100644 index 0000000..9d1482e --- /dev/null +++ b/scripts/download_unzip_iris_cdn_artifacts.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +set -e +set -x + +CDN_URL=$1 +PLATFORM=$2 + +MY_PATH=$(dirname "$0") + +ARTIFACTS_PATH="${MY_PATH}/../artifacts" +mkdir -p ${ARTIFACTS_PATH} + +IRIS_TESTER_PATH=${MY_PATH}/../test_shard/iris_tester + +DOWNLOAD_NAME=${CDN_URL##*/} + +DOWNLOAD_BASE_NAME=${DOWNLOAD_NAME/.zip/""} + +curl -L "${CDN_URL}" > ${ARTIFACTS_PATH}/${DOWNLOAD_NAME} + +VERSION="$(cut -d'_' -f2 <<<"${DOWNLOAD_NAME}")" + +pushd ${ARTIFACTS_PATH} + unzip ${DOWNLOAD_NAME} -d ${DOWNLOAD_BASE_NAME} +popd + +UNZIP_PATH="${ARTIFACTS_PATH}/${DOWNLOAD_BASE_NAME}/iris_${VERSION}_RTM_${PLATFORM}" + +if [[ ${PLATFORM} == "Android" ]];then + ABIS="arm64-v8a armeabi-v7a x86_64" + for ABI in ${ABIS}; + do + if [[ ! -d "${IRIS_TESTER_PATH}/android/libs/${ABI}" ]]; then + mkdir -p "${IRIS_TESTER_PATH}/android/libs/${ABI}" + fi + + cp -RP "${UNZIP_PATH}/Debugger/ALL_ARCHITECTURE/${ABI}/libIrisDebugger.so" "${IRIS_TESTER_PATH}/android/libs/${ABI}/libIrisDebugger.so" + + ls ${IRIS_TESTER_PATH}/android/libs/${ABI}/ + done; + + +fi + +if [[ ${PLATFORM} == "MAC" ]];then + cp -RP "${UNZIP_PATH}/Debugger/MAC/IrisDebugger.framework" "${IRIS_TESTER_PATH}/macos/" +fi + +if [[ ${PLATFORM} == "iOS" ]];then + cp -RP "${UNZIP_PATH}/Debugger/ALL_ARCHITECTURE/IrisDebugger.xcframework" "${IRIS_TESTER_PATH}/ios/" +fi + +if [[ ${PLATFORM} == "Windows" ]];then + cp -RP "${UNZIP_PATH}/Debugger/x64/IrisDebugger.dll" "${IRIS_TESTER_PATH}/windows/IrisDebugger.dll" + cp -RP "${UNZIP_PATH}/Debugger/x64/IrisDebugger.lib" "${IRIS_TESTER_PATH}/windows/IrisDebugger.lib" +fi + +# pushd ${UNZIP_PATH} + + + + + +# popd \ No newline at end of file diff --git a/scripts/ffi_gen/ffigen_config.yaml b/scripts/ffi_gen/ffigen_config.yaml new file mode 100644 index 0000000..1a1b4cf --- /dev/null +++ b/scripts/ffi_gen/ffigen_config.yaml @@ -0,0 +1,16 @@ +name: 'NativeIrisApiEngineBinding' +description: 'Bindings to IrisApiEngine' + +output: 'lib/src/bindings/gen/native_iris_api_engine_bindings.dart' + +headers: + entry-points: + - 'tmp_ffi_gen_include/iris_rtm_c_api.h' + include-directives: + - 'tmp_ffi_gen_include/iris_rtm_c_api.h' + +preamble: | + // ignore_for_file: camel_case_types, non_constant_identifier_names + +llvm-path: + - '/opt/homebrew/opt/llvm@15' \ No newline at end of file diff --git a/scripts/ffi_gen/run_ffi_gen.sh b/scripts/ffi_gen/run_ffi_gen.sh new file mode 100644 index 0000000..d77a53e --- /dev/null +++ b/scripts/ffi_gen/run_ffi_gen.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -e +set -x + +IRIS_PATH=$1 +MY_PATH=$(realpath $(dirname "$0")) +PROJECT_ROOT=$(realpath ${MY_PATH}/../..) +TMP_FFI_GEN_INCLUDE_DIR_NAME=${PROJECT_ROOT}/tmp_ffi_gen_include + +rm -rf ${TMP_FFI_GEN_INCLUDE_DIR_NAME} +mkdir ${TMP_FFI_GEN_INCLUDE_DIR_NAME} + +cp -RP ${IRIS_PATH}/src/rtm/include/* ${TMP_FFI_GEN_INCLUDE_DIR_NAME} + +cp -RP ${IRIS_PATH}/common/public/* ${TMP_FFI_GEN_INCLUDE_DIR_NAME} +# cp -RP ${MY_PATH}/ffigen_config.yaml ${TMP_FFI_GEN_INCLUDE_DIR_NAME}/ffigen_config.yaml + +pushd ${PROJECT_ROOT} + +flutter packages get +flutter pub run ffigen --config=${MY_PATH}/ffigen_config.yaml + +popd + +rm -rf ${TMP_FFI_GEN_INCLUDE_DIR_NAME} + +bash ${PROJECT_ROOT}/test_shard/iris_tester/ffigen.sh ${IRIS_PATH} \ No newline at end of file diff --git a/scripts/flutter-build-runner.sh b/scripts/flutter-build-runner.sh new file mode 100644 index 0000000..63594aa --- /dev/null +++ b/scripts/flutter-build-runner.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -e +set -x + +MY_PATH=$(realpath $(dirname "$0")) +AGORA_FLUTTER_PROJECT_PATH=$(realpath ${MY_PATH}/..) + +pushd ${AGORA_FLUTTER_PROJECT_PATH} + +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/macos/Flutter/ephemeral +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/windows/Flutter/ephemeral +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/linux/Flutter/ephemeral +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/ios/.symlinks + +flutter packages get +flutter packages pub run build_runner build --delete-conflicting-outputs + +popd + +pushd ${AGORA_FLUTTER_PROJECT_PATH}/test_shard/integration_test_app + +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/macos/Flutter/ephemeral +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/windows/Flutter/ephemeral +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/linux/Flutter/ephemeral +rm -rf $AGORA_FLUTTER_PROJECT_PATH/example/ios/.symlinks + +flutter packages get +flutter packages pub run build_runner build --delete-conflicting-outputs + +popd \ No newline at end of file diff --git a/scripts/run_flutter_integration_test.sh b/scripts/run_flutter_integration_test.sh new file mode 100644 index 0000000..d6833ef --- /dev/null +++ b/scripts/run_flutter_integration_test.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +set -e +set -x + +MY_PATH=$(realpath $(dirname "$0")) +PROJECT_ROOT=$(realpath ${MY_PATH}/..) +PLATFORM=$1 # android/ios/macos/windows/web + +if [[ ${PLATFORM} == "web" ]];then + pushd ${PROJECT_ROOT}/test_shard/fake_test_app + + IRIS_WEB_VERSION_PATH=${PROJECT_ROOT}/scripts/iris_web_version.js + rm -rf web/iris_web_version.js + cp -RP ${IRIS_WEB_VERSION_PATH} web/ + + echo "Run integration test on web" + echo "If you want to run integration test on your local machine, please follow the https://docs.flutter.dev/testing/integration-tests#test-in-a-web-browser to setup first." + + flutter packages get + + for filename in integration_test/*.dart; do + if [[ "$filename" == *.generated.dart ]]; then + continue + fi + + flutter drive \ + --verbose-system-logs \ + -d web-server \ + --driver=test_driver/integration_test.dart \ + --target=${filename} + done + + popd + +elif [[ ${PLATFORM} == "android" || ${PLATFORM} == "ios" ]];then + DOWNLOAD_IRIS_DEBUGGER=${2:-1} + + if [[ ${DOWNLOAD_IRIS_DEBUGGER} == 1 ]];then + source ${MY_PATH}/artifacts_version.sh + + if [[ ${PLATFORM} == "android" ]];then + bash ${MY_PATH}/download_unzip_iris_cdn_artifacts.sh ${IRIS_CDN_URL_ANDROID} "Android" + elif [[ ${PLATFORM} == "ios" ]];then + bash ${MY_PATH}/download_unzip_iris_cdn_artifacts.sh ${IRIS_CDN_URL_IOS} "iOS" + fi + fi + + pushd ${MY_PATH}/../test_shard/integration_test_app + + flutter packages get + + flutter test integration_test/binding_apis_call_fake_test.dart --dart-define=TEST_APP_ID="${TEST_APP_ID}" --verbose + + popd +else + echo "Not implemented" +fi \ No newline at end of file diff --git a/scripts/terra/.gitignore b/scripts/terra/.gitignore new file mode 100644 index 0000000..c0e9d51 --- /dev/null +++ b/scripts/terra/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +package-lock.json +.terra +.yarn +.yarnrc.yml +yarn.lock \ No newline at end of file diff --git a/scripts/terra/.npmrc b/scripts/terra/.npmrc new file mode 100644 index 0000000..19f169a --- /dev/null +++ b/scripts/terra/.npmrc @@ -0,0 +1 @@ +@agoraio-extensions:registry=https://npm.pkg.github.com \ No newline at end of file diff --git a/scripts/terra/bindings_api_config.yaml b/scripts/terra/bindings_api_config.yaml new file mode 100644 index 0000000..669bcc2 --- /dev/null +++ b/scripts/terra/bindings_api_config.yaml @@ -0,0 +1,109 @@ +parsers: + - name: CXXParser + package: '@agoraio-extensions/cxx-parser' + args: + includeHeaderDirs: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include' + parseFiles: + include: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/*.h' + exclude: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/AgoraRefPtr.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/time_utils.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/AgoraOptional.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/AgoraRefPtr.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/IAgoraMediaComponentFactory.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/IAgoraParameter.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/rte*.h' + + - name: IrisApiIdParser + package: '@agoraio-extensions/terra_shared_configs' + + - name: OverrideNodeParser + package: '@agoraio-extensions/terra_shared_configs' + args: + customHeaderFileNamePrefix: 'Custom' + includeHeaderDirs: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include' + parseFiles: + include: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/*.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/custom_headers/*.h' + exclude: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/time_utils.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/IAgoraMediaComponentFactory.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/rte*.h' + + - path: parsers/cud_node_parser.ts + args: + configPath: configs/cud_node_parser.config.ts + + - name: PointerToArrayParser + package: '@agoraio-extensions/terra_shared_configs' + + # - name: RemoveNodeParser + # package: '@agoraio-extensions/terra_shared_configs' + + - name: ReturnTypeParser + package: '@agoraio-extensions/terra_shared_configs' + args: + convertReturnToVoid: true + + - name: FixEnumConstantParser + package: '@agoraio-extensions/terra_shared_configs' + args: + skipCalEnumValue: true + + - path: parsers/default_value_marker_parser.ts + args: + configPath: configs/default_value_marker_parser.config.ts + + - path: parsers/dart_type_remapping_parser.ts + args: + configPath: configs/dart_type_remapping_parser.config.ts + + # Need put it below `PointerToArrayParser`, since the `PointerToArrayParser` will change the pointer type to array type + - path: parsers/dart_syntax_parser.ts + + # This should be ordered in last step + # - name: MergeNodeParser + # package: '@agoraio-extensions/terra_shared_configs' + # args: + # configFilePath: configs/merge_node_list.ts + # ignoreDefaultConfig: true + +renderers: + - path: renderers/event_handlers_impl_renderer.ts + args: + outputDir: 'lib/src/bindings/gen' + - path: renderers/event_ids_mapping_renderer.ts + - path: renderers/forward_export_renderer.ts + args: + outputDir: 'lib/src' + extraBindingExportFiles: + - 'package:agora_rtm/src/bindings/json_converters.dart' + - 'call_api_event_handler_buffer_ext.dart' + - 'event_handler_param_json.dart' + - 'call_api_impl_params_json.dart' + + - path: renderers/buffer_ext_renderer.ts + args: + outputDir: 'lib/src/bindings/gen' + - path: renderers/event_handler_impl_params_json_renderer.ts + args: + outputDir: 'lib/src/bindings/gen' + - path: renderers/callapi_impl_renderer.ts + args: + outputDir: 'lib/src/bindings/gen' + - path: renderers/api_interface_renderer.ts + args: + outputDir: 'lib/src/bindings/gen' + + # Put it to last step + # - name: IrisDocRenderer + # package: '@agoraio-extensions/terra_shared_configs' + # args: + # language: dart + # fmtConfig: fmt_dart.yaml + # exportFilePath: ../../lib/agora_rtc_engine.dart + # templateUrl: https://github.com/AgoraIO/agora_doc_source/releases/download/master-build/flutter_ng_json_template_en.json \ No newline at end of file diff --git a/scripts/terra/build.sh b/scripts/terra/build.sh new file mode 100644 index 0000000..4331c19 --- /dev/null +++ b/scripts/terra/build.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -e +set -x + +NEW_VERSION=$1 +MY_PATH=$(realpath $(dirname "$0")) +PROJECT_ROOT=$(realpath ${MY_PATH}/../..) + +TERRA_MAIN_FILE=${PROJECT_ROOT}/scripts/terra/bindings_api_config.yaml + +if [ -n "$NEW_VERSION" ]; then + # Check the operating system type + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' -E "s/rtc_[0-9]+\.[0-9]+(\.[0-9]+)*/${NEW_VERSION}/g" $TERRA_MAIN_FILE + else + # Linux and other Unix-like systems + sed -i -E "s/rtc_[0-9]+\.[0-9]+(\.[0-9]+)*/${NEW_VERSION}/g" $TERRA_MAIN_FILE + fi + echo "Updated version to ${NEW_VERSION} in $TERRA_MAIN_FILE" +fi + +bash ${MY_PATH}/prepare.sh + +pushd ${MY_PATH} + +npm exec terra -- run \ + --config ${TERRA_MAIN_FILE} \ + --output-dir=${PROJECT_ROOT} + +npm exec terra -- run \ + --config ${PROJECT_ROOT}/scripts/terra/export_api_config.yaml \ + --output-dir=${PROJECT_ROOT} + +popd + +pushd ${PROJECT_ROOT} + +dart format . + +popd \ No newline at end of file diff --git a/scripts/terra/configs/cud_node_parser.config.ts b/scripts/terra/configs/cud_node_parser.config.ts new file mode 100644 index 0000000..ee531c3 --- /dev/null +++ b/scripts/terra/configs/cud_node_parser.config.ts @@ -0,0 +1,125 @@ +import { CXXTYPE, SimpleTypeKind } from "@agoraio-extensions/cxx-parser"; + +const deleteNodes = [ + // agora::rtm::IRtmClient::createStreamChannel + { + __TYPE: CXXTYPE.Variable, + name: "errorCode", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmClient::createStreamChannel", + }, +]; + +const updateNodes = [ + { + node: { + __TYPE: CXXTYPE.Variable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmPresence::setState", + }, + updated: { + __TYPE: CXXTYPE.Variable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmPresence::setState", + type: { + __TYPE: CXXTYPE.SimpleType, + is_builtin_type: false, + is_const: true, + kind: SimpleTypeKind.array_t, + }, + }, + }, + + // agora::rtm::IRtmEventHandler::MessageEvent::message + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "message", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler::MessageEvent", + }, + updated: { + __TYPE: CXXTYPE.MemberVariable, + name: "message", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler::MessageEvent", + type: { + __TYPE: CXXTYPE.SimpleType, + is_builtin_type: false, + is_const: true, + kind: SimpleTypeKind.pointer_t, + name: "uint8_t", + source: "const uint8_t*", + }, + }, + }, + // agora::rtm::RtmConfig::areaCode + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "areaCode", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + }, + updated: { + __TYPE: CXXTYPE.MemberVariable, + name: "areaCode", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + type: { + __TYPE: CXXTYPE.SimpleType, + is_builtin_type: false, + is_const: false, + kind: SimpleTypeKind.array_t, + }, + }, + }, + // agora::rtm::RtmPrivateConfig::serviceType + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "serviceType", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmPrivateConfig", + }, + updated: { + __TYPE: CXXTYPE.MemberVariable, + name: "serviceType", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmPrivateConfig", + type: { + __TYPE: CXXTYPE.SimpleType, + is_builtin_type: false, + is_const: false, + kind: SimpleTypeKind.array_t, + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "channels", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler::onWhereNowResult", + }, + updated: { + __TYPE: CXXTYPE.Variable, + name: "channels", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler::onWhereNowResult", + type: { + __TYPE: CXXTYPE.SimpleType, + is_builtin_type: false, + is_const: true, + kind: SimpleTypeKind.array_t, + }, + }, + }, +]; + +module.exports = { + deleteNodes: deleteNodes, + updateNodes: updateNodes, +}; diff --git a/scripts/terra/configs/dart_project_marker_parser.config.ts b/scripts/terra/configs/dart_project_marker_parser.config.ts new file mode 100644 index 0000000..99f5184 --- /dev/null +++ b/scripts/terra/configs/dart_project_marker_parser.config.ts @@ -0,0 +1,478 @@ +import { CXXTYPE, CXXTerraNode } from "@agoraio-extensions/cxx-parser"; +import { + DartProjectMarkerParserConfig, + DisplayNameConfig, + FlattenParamNodeConfig, +} from "../parsers/dart_project_marker_parser"; +import { ParseResult } from "@agoraio-extensions/terra-core"; +import { _trim } from "../renderers/utils"; + +function _dartSetTypeDefaultValueBuilder( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string +): string { + return `const {${formattedDartName}}`; +} + +function _toStateItem( + parseResult: ParseResult, + config: DisplayNameConfig +): string { + let name = config.displayName; + return _trim(` + ${name}.entries.map((entry) => StateItem(key: entry.key, value: entry.value)).toList() +`); +} + +function _toMetadata( + parseResult: ParseResult, + config: DisplayNameConfig +): string { + let name = config.displayName ?? config.node.name; + return _trim(` + Metadata(majorRevision: majorRevision, items: ${name}); + `); +} + +function _fromUserList( + parseResult: ParseResult, + config: DisplayNameConfig +): string { + let name = config.node.name; + return _trim(`${name}.users ?? []`); +} + +module.exports = { + jsonConverters: [ + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "areaCode", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + }, + jsonConverter: "@RtmAreaCodeListConverter()", + }, + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "serviceType", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmPrivateConfig", + }, + jsonConverter: "@RtmServiceTypeListConverter()", + }, + ], + displayNameConfigs: [ + { + node: { + __TYPE: CXXTYPE.EnumConstant, + name: "RTM_AREA_CODE_IN", + parent_full_scope_name: "agora::rtm::RTM_AREA_CODE", + }, + displayName: "ind", + }, + { + node: { + __TYPE: CXXTYPE.EnumConstant, + name: "RTM_AREA_CODE_AS", + parent_full_scope_name: "agora::rtm::RTM_AREA_CODE", + }, + displayName: "asm", + }, + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "areaCode", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + }, + displayType: "Set", + converter: { defaultValueBuilder: _dartSetTypeDefaultValueBuilder }, + }, + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "serviceType", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmPrivateConfig", + }, + displayType: "Set", + converter: { defaultValueBuilder: _dartSetTypeDefaultValueBuilder }, + }, + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "meta", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::JoinTopicOptions", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "''"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "syncWithMedia", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::JoinTopicOptions", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "false"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "page", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::PresenceOptions", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "''"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmPresence::setState", + }, + displayName: "state", + displayType: "Map", + converter: { + to: _toStateItem, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "keys", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmPresence::removeState", + }, + displayName: "states", + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "const []"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "succeedUsers", + namespaces: ["agora", "rtm"], + parent_full_scope_name: + "agora::rtm::IRtmEventHandler::onSubscribeTopicResult", + }, + displayType: "List", + converter: { from: _fromUserList }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "failedUsers", + namespaces: ["agora", "rtm"], + parent_full_scope_name: + "agora::rtm::IRtmEventHandler::onSubscribeTopicResult", + }, + displayType: "List", + converter: { from: _fromUserList }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "lockName", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmStorage::setChannelMetadata", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "''"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "lockName", + namespaces: ["agora", "rtm"], + parent_full_scope_name: + "agora::rtm::IRtmStorage::updateChannelMetadata", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "''"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "lockName", + namespaces: ["agora", "rtm"], + parent_full_scope_name: + "agora::rtm::IRtmStorage::removeChannelMetadata", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "''"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "ttl", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmLock::setLock", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "10"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.Variable, + name: "retry", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmLock::acquireLock", + }, + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "false"; + }, + }, + }, + { + node: { + __TYPE: CXXTYPE.MemberVariable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::Metadata", + }, + displayName: "metadata", + converter: { + defaultValueBuilder: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => { + return "const []"; + }, + to: _toMetadata, + }, + }, + ], + trimNamePrefixConfigs: [ + { + node: { + __TYPE: CXXTYPE.Enumz, + name: "RTM_CONNECTION_CHANGE_REASON", + namespaces: ["agora", "rtm"], + }, + trimPrefix: "RTM_CONNECTION_CHANGED_", + }, + { + node: { + __TYPE: CXXTYPE.Enumz, + name: "RTM_ERROR_CODE", + namespaces: ["agora", "rtm"], + }, + trimPrefix: "RTM_ERROR_", + }, + ], + hiddenNodes: [ + { + __TYPE: CXXTYPE.MemberVariable, + name: "appId", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + }, + { + __TYPE: CXXTYPE.MemberVariable, + name: "userId", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + }, + { + __TYPE: CXXTYPE.MemberVariable, + name: "context", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + }, + { + __TYPE: CXXTYPE.MemberVariable, + name: "multipath", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::RtmConfig", + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "onConnectionStateChanged", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler", + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "onTokenPrivilegeWillExpire", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler", + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "publishTopicMessage", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IStreamChannel", + }, + ], + + flattenParamNodeConfigs: [ + { + isFlattenParam: ( + parseResult: ParseResult, + config: FlattenParamNodeConfig, + node: CXXTerraNode + ) => { + let regex = new RegExp("(.*)Options$"); + return node.isVariable() && regex.test(node.asVariable().type.name); + }, + hiddenParamsAfterFlatten: [ + { + __TYPE: CXXTYPE.MemberVariable, + name: "messageType", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::PublishOptions", + }, + { + __TYPE: CXXTYPE.MemberVariable, + name: "messageType", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::TopicMessageOptions", + }, + ], + }, + { + isFlattenParam: ( + parseResult: ParseResult, + config: FlattenParamNodeConfig, + node: CXXTerraNode + ) => { + let regex = new RegExp("(.*)Metadata$"); + return node.isVariable() && regex.test(node.asVariable().type.name); + }, + }, + { + flattenFunctionNode: { + __TYPE: CXXTYPE.MemberFunction, + name: "setChannelMetadata", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmStorage", + }, + notANamedParametersAfterFlatten: [ + { + __TYPE: CXXTYPE.MemberVariable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::Metadata", + }, + ], + }, + { + flattenFunctionNode: { + __TYPE: CXXTYPE.MemberFunction, + name: "updateChannelMetadata", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmStorage", + }, + notANamedParametersAfterFlatten: [ + { + __TYPE: CXXTYPE.MemberVariable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::Metadata", + }, + ], + }, + { + flattenFunctionNode: { + __TYPE: CXXTYPE.MemberFunction, + name: "setUserMetadata", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmStorage", + }, + notANamedParametersAfterFlatten: [ + { + __TYPE: CXXTYPE.MemberVariable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::Metadata", + }, + ], + }, + { + flattenFunctionNode: { + __TYPE: CXXTYPE.MemberFunction, + name: "updateUserMetadata", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmStorage", + }, + notANamedParametersAfterFlatten: [ + { + __TYPE: CXXTYPE.MemberVariable, + name: "items", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::Metadata", + }, + ], + }, + ], +} as DartProjectMarkerParserConfig; diff --git a/scripts/terra/configs/dart_type_remapping_parser.config.ts b/scripts/terra/configs/dart_type_remapping_parser.config.ts new file mode 100644 index 0000000..4fde9e1 --- /dev/null +++ b/scripts/terra/configs/dart_type_remapping_parser.config.ts @@ -0,0 +1,62 @@ +import { CXXTYPE } from "@agoraio-extensions/cxx-parser"; +import { + DartTypeRemappingParserConfig, + DartTypeRemappingConfig, +} from "../parsers/dart_type_remapping_parser"; +import { ParseResult } from "@agoraio-extensions/terra-core"; +import { _trim } from "../renderers/utils"; + +function _toStateItem( + parseResult: ParseResult, + config: DartTypeRemappingConfig +): string { + let name = config.node.name; + return _trim(` + state.entries.map((entry) => StateItem(key: entry.key, value: entry.value)).toList() +`); +} + +function _fromUserList( + parseResult: ParseResult, + config: DartTypeRemappingConfig +): string { + let name = config.node.name; + return _trim(`${name}.users ?? []`); +} + +module.exports = { + remappingNodes: [ + // { + // node: { + // __TYPE: CXXTYPE.Variable, + // name: "items", + // namespaces: ["agora", "rtm"], + // parent_full_scope_name: "agora::rtm::IRtmPresence::setState", + // }, + // dartType: "Map", + // converter: { to: _toStateItem }, + // } as DartTypeRemappingConfig, + // { + // node: { + // __TYPE: CXXTYPE.Variable, + // name: "succeedUsers", + // namespaces: ["agora", "rtm"], + // parent_full_scope_name: + // "agora::rtm::IRtmEventHandler::onSubscribeTopicResult", + // }, + // dartType: "List", + // converter: { from: _fromUserList }, + // } as DartTypeRemappingConfig, + // { + // node: { + // __TYPE: CXXTYPE.Variable, + // name: "failedUsers", + // namespaces: ["agora", "rtm"], + // parent_full_scope_name: + // "agora::rtm::IRtmEventHandler::onSubscribeTopicResult", + // }, + // dartType: "List", + // converter: { from: _fromUserList }, + // } as DartTypeRemappingConfig, + ], +} as DartTypeRemappingParserConfig; diff --git a/scripts/terra/configs/default_value_marker_parser.config.ts b/scripts/terra/configs/default_value_marker_parser.config.ts new file mode 100644 index 0000000..8967431 --- /dev/null +++ b/scripts/terra/configs/default_value_marker_parser.config.ts @@ -0,0 +1,29 @@ +import { CXXTYPE, CXXTerraNode } from "@agoraio-extensions/cxx-parser"; +import { + DefaultValuesConfig, + DefaultValuesConfigs, +} from "../parsers/default_value_marker_parser"; + +module.exports = { + defaultValues: [ + // { + // node: { + // __TYPE: CXXTYPE.Variable, + // name: "lockName", + // namespaces: ["agora", "rtm"], + // parent_full_scope_name: "agora::rtm::IRtmStorage::setChannelMetadata", + // } as CXXTerraNode, + // value: '\'\'', + // } as DefaultValuesConfig, + // { + // node: { + // __TYPE: CXXTYPE.Variable, + // name: "lockName", + // namespaces: ["agora", "rtm"], + // parent_full_scope_name: + // "agora::rtm::IRtmStorage::updateChannelMetadata", + // } as CXXTerraNode, + // value: '\'\'', + // } as DefaultValuesConfig, + ], +} as DefaultValuesConfigs; diff --git a/scripts/terra/configs/export_rtm_api_interface_renderer.config.ts b/scripts/terra/configs/export_rtm_api_interface_renderer.config.ts new file mode 100644 index 0000000..c473977 --- /dev/null +++ b/scripts/terra/configs/export_rtm_api_interface_renderer.config.ts @@ -0,0 +1,44 @@ +// import { CXXTYPE } from "@agoraio-extensions/cxx-parser"; + +// module.exports = { +// remappingNodes: [ +// { +// node: { +// __TYPE: CXXTYPE.Variable, +// name: "items", +// namespaces: ["agora", "rtm"], +// parent_full_scope_name: "agora::rtm::IRtmPresence::setState", +// }, +// dartType: "Map", +// } as RemappingNodeDartTypeConfig, +// { +// node: { +// __TYPE: CXXTYPE.Variable, +// name: "succeedUsers", +// namespaces: ["agora", "rtm"], +// parent_full_scope_name: +// "agora::rtm::IRtmEventHandler::onSubscribeTopicResult", +// }, +// dartType: "List", +// } as RemappingNodeDartTypeConfig, +// { +// node: { +// __TYPE: CXXTYPE.Variable, +// name: "failedUsers", +// namespaces: ["agora", "rtm"], +// parent_full_scope_name: +// "agora::rtm::IRtmEventHandler::onSubscribeTopicResult", +// }, +// dartType: "List", +// } as RemappingNodeDartTypeConfig, +// { +// node: { +// __TYPE: CXXTYPE.MemberVariable, +// name: "states", +// namespaces: ["agora", "rtm"], +// parent_full_scope_name: "agora::rtm::UserState", +// }, +// dartType: "Map", +// } as RemappingNodeDartTypeConfig, +// ], +// } as ExportRtmApiInterfaceRendererConfig; diff --git a/scripts/terra/configs/merge_node_list.ts b/scripts/terra/configs/merge_node_list.ts new file mode 100644 index 0000000..11db358 --- /dev/null +++ b/scripts/terra/configs/merge_node_list.ts @@ -0,0 +1,7 @@ +module.exports = [ + { + source: "agora::rtc::IRtcEngineEventHandlerEx", + target: "agora::rtc::IRtcEngineEventHandler", + deleteSource: true, + }, +]; diff --git a/scripts/terra/configs/pointer_marker.config.ts b/scripts/terra/configs/pointer_marker.config.ts new file mode 100644 index 0000000..310012d --- /dev/null +++ b/scripts/terra/configs/pointer_marker.config.ts @@ -0,0 +1,407 @@ +import { CXXTYPE } from "@agoraio-extensions/cxx-parser"; + +module.exports = { + markers: [ + //rtc begin + { + node: { + __TYPE: CXXTYPE.Struct, + name: "LiveTranscoding", + namespaces: ["agora", "rtc"], + }, + pointerArrayNameMappings: [ + { + ptrName: "transcodingUsers", + lengthName: "userCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "LocalTranscoderConfiguration", + namespaces: ["agora", "rtc"], + }, + pointerArrayNameMappings: [ + { + ptrName: "videoInputStreams", + lengthName: "streamCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "DownlinkNetworkInfo", + namespaces: ["agora", "rtc"], + }, + pointerArrayNameMappings: [ + { + ptrName: "peer_downlink_info", + lengthName: "total_received_video_count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "ChannelMediaRelayConfiguration", + namespaces: ["agora", "rtc"], + }, + pointerArrayNameMappings: [ + { + ptrName: "destInfos", + lengthName: "destCount", + }, + ], + pointerNames: ["srcInfo"], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "ExternalVideoFrame", + namespaces: ["agora", "media", "base"], + }, + pointerNames: [ + "buffer", + "eglContext", + "metadata_buffer", + "alphaBuffer", + "d3d11_texture_2d", + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "VideoFrame", + namespaces: ["agora", "media", "base"], + }, + pointerNames: [ + "yBuffer", + "uBuffer", + "vBuffer", + "metadata_buffer", + "sharedContext", + "d3d11Texture2d", + "alphaBuffer", + "pixelBuffer", + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "LocalSpatialAudioConfig", + namespaces: ["agora", "rtc"], + }, + pointerNames: ["rtcEngine"], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "ThumbImageBuffer", + namespaces: ["agora", "rtc"], + }, + pointerNames: ["buffer"], + }, + //rtc end + + //rtm begin + { + node: { + __TYPE: CXXTYPE.Struct, + name: "UserList", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "users", + lengthName: "userCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "TopicOptions", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "users", + lengthName: "userCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "UserState", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "states", + lengthName: "statesCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "IntervalInfo", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "userStateList", + lengthName: "userStateCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "SnapshotInfo", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "userStateList", + lengthName: "userCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "PresenceEvent", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "stateItems", + lengthName: "stateItemCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "TopicInfo", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "publishers", + lengthName: "publisherCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "TopicEvent", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "topicInfos", + lengthName: "topicInfoCount", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "LockEvent", + namespaces: ["agora", "rtm"], + }, + pointerArrayNameMappings: [ + { + ptrName: "lockDetailList", + lengthName: "count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.Struct, + name: "MessageEvent", + namespaces: ["agora", "rtm"], + }, + pointerNames: ["message"], + }, + + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "publish", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmClient", + }, + pointerArrayNameMappings: [ + { + ptrName: "message", + lengthName: "length", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "publishBinaryMessage", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::ext::IRtmClient", + }, + pointerArrayNameMappings: [ + { + ptrName: "message", + lengthName: "length", + }, + ], + }, + + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "publishTopicMessage", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::ext::IStreamChannel", + }, + pointerArrayNameMappings: [ + { + ptrName: "message", + lengthName: "length", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "publishTextMessage", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::ext::IStreamChannel", + }, + pointerArrayNameMappings: [ + { + ptrName: "message", + lengthName: "length", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "publishBinaryMessage", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::ext::IStreamChannel", + }, + pointerArrayNameMappings: [ + { + ptrName: "message", + lengthName: "length", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "setState", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmPresence", + }, + pointerArrayNameMappings: [ + { + ptrName: "items", + lengthName: "count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "removeState", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmPresence", + }, + pointerArrayNameMappings: [ + { + ptrName: "keys", + lengthName: "count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "onGetLocksResult", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler", + }, + pointerArrayNameMappings: [ + { + ptrName: "lockDetailList", + lengthName: "count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "onWhoNowResult", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler", + }, + pointerArrayNameMappings: [ + { + ptrName: "userStateList", + lengthName: "count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "onGetOnlineUsersResult", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler", + }, + pointerArrayNameMappings: [ + { + ptrName: "userStateList", + lengthName: "count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "onWhereNowResult", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler", + }, + pointerArrayNameMappings: [ + { + ptrName: "channels", + lengthName: "count", + }, + ], + }, + { + node: { + __TYPE: CXXTYPE.MemberFunction, + name: "onGetUserChannelsResult", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmEventHandler", + }, + pointerArrayNameMappings: [ + { + ptrName: "channels", + lengthName: "count", + }, + ], + }, + + //rtm end + ], +}; diff --git a/scripts/terra/configs/sync_function_marker_parser.config.ts b/scripts/terra/configs/sync_function_marker_parser.config.ts new file mode 100644 index 0000000..cfcc3f0 --- /dev/null +++ b/scripts/terra/configs/sync_function_marker_parser.config.ts @@ -0,0 +1,25 @@ +import { CXXTYPE } from "@agoraio-extensions/cxx-parser"; +import { SyncFunctionsMarkerParserConfig } from "../parsers/sync_function_marker_parser"; + +module.exports = { + syncFunctionNodes: [ + { + __TYPE: CXXTYPE.MemberFunction, + name: "getStorage", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmClient", + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getLock", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmClient", + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getPresence", + namespaces: ["agora", "rtm"], + parent_full_scope_name: "agora::rtm::IRtmClient", + }, + ], +} as SyncFunctionsMarkerParserConfig; diff --git a/scripts/terra/export_api_config.yaml b/scripts/terra/export_api_config.yaml new file mode 100644 index 0000000..57638e2 --- /dev/null +++ b/scripts/terra/export_api_config.yaml @@ -0,0 +1,85 @@ +parsers: + - name: CXXParser + package: '@agoraio-extensions/cxx-parser' + args: + includeHeaderDirs: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include' + parseFiles: + include: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/*.h' + exclude: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/AgoraRefPtr.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/time_utils.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/AgoraOptional.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/AgoraRefPtr.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/IAgoraMediaComponentFactory.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/IAgoraParameter.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/rte*.h' + + - name: IrisApiIdParser + package: '@agoraio-extensions/terra_shared_configs' + + - name: OverrideNodeParser + package: '@agoraio-extensions/terra_shared_configs' + args: + customHeaderFileNamePrefix: 'Custom' + includeHeaderDirs: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include' + parseFiles: + include: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/*.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/custom_headers/*.h' + exclude: + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/time_utils.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/IAgoraMediaComponentFactory.h' + - '@agoraio-extensions/terra_shared_configs:headers/rtm_2.2.1/include/rte*.h' + + - name: PointerMarkerParser + package: '@agoraio-extensions/terra_shared_configs' + args: + configPath: configs/pointer_marker.config.ts + + - path: parsers/cud_node_parser.ts + args: + configPath: configs/cud_node_parser.config.ts + + - name: PointerToArrayParser + package: '@agoraio-extensions/terra_shared_configs' + + - name: FixEnumConstantParser + package: '@agoraio-extensions/terra_shared_configs' + args: + skipCalEnumValue: true + + - path: parsers/default_value_marker_parser.ts + args: + configPath: configs/default_value_marker_parser.config.ts + + - path: parsers/dart_type_remapping_parser.ts + args: + configPath: configs/dart_type_remapping_parser.config.ts + + - path: parsers/sync_function_marker_parser.ts + args: + configPath: configs/sync_function_marker_parser.config.ts + + - path: parsers/dart_project_marker_parser.ts + args: + configPath: configs/dart_project_marker_parser.config.ts + + # Need put it below `PointerToArrayParser`, since the `PointerToArrayParser` will change the pointer type to array type + - path: parsers/dart_syntax_parser.ts + +renderers: + - path: renderers/export_api_renderer.ts + args: + outputDir: 'lib/src' + + # Put it to last step + # - name: IrisDocRenderer + # package: '@agoraio-extensions/terra_shared_configs' + # args: + # language: dart + # fmtConfig: fmt_dart.yaml + # exportFilePath: ../../lib/agora_rtc_engine.dart + # templateUrl: https://github.com/AgoraIO/agora_doc_source/releases/download/master-build/flutter_ng_json_template_en.json \ No newline at end of file diff --git a/scripts/terra/package.json b/scripts/terra/package.json new file mode 100644 index 0000000..ec0642d --- /dev/null +++ b/scripts/terra/package.json @@ -0,0 +1,25 @@ +{ + "name": "terra-script", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "@types/lodash": "^4", + "@types/node": "^20.6.0" + }, + "dependencies": { + "@agoraio-extensions/cxx-parser": "git@github.com:AgoraIO-Extensions/terra.git#head=main&workspace=cxx-parser", + "@agoraio-extensions/terra": "git@github.com:AgoraIO-Extensions/terra.git#head=main&workspace=terra", + "@agoraio-extensions/terra-core": "git@github.com:AgoraIO-Extensions/terra.git#head=main&workspace=terra-core", + "@agoraio-extensions/terra_shared_configs": "git@github.com:AgoraIO-Extensions/terra_shared_configs.git#head=main", + "lodash": "^4.17.21", + "ts-node": "^10.9.1", + "typescript": "^5.1.6" + }, + "packageManager": "yarn@4.4.1" +} diff --git a/scripts/terra/parsers/cud_node_parser.ts b/scripts/terra/parsers/cud_node_parser.ts new file mode 100644 index 0000000..e9b153f --- /dev/null +++ b/scripts/terra/parsers/cud_node_parser.ts @@ -0,0 +1,203 @@ +import { + CXXFile, + CXXTYPE, + CXXTerraNode, + Clazz, + Constructor, + EnumConstant, + Enumz, + MemberFunction, + MemberVariable, + Variable, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + TerraContext, + resolvePath, +} from "@agoraio-extensions/terra-core"; +import { irisApiId } from "@agoraio-extensions/terra_shared_configs"; +import _ from "lodash"; + +const funcNeedCheckWithBaseClasses = [ + "agora::media::IAudioFrameObserver", + "agora::rtc::IRtcEngineEventHandlerEx", +]; +function isNeedCheckWithBaseClasses(clazz: Clazz): boolean { + return funcNeedCheckWithBaseClasses.includes(clazz.fullName); +} + +export interface CUDNodeOp { + node: CXXTerraNode; +} + +export interface CUDNodeDeleteConfig extends CUDNodeOp {} + +function deleteOp(sourceNode: CXXTerraNode, opConfig: CUDNodeOp): boolean { + return _.isMatch(sourceNode, opConfig.node); +} + +export interface CUDNodeUpdateConfig extends CUDNodeOp { + updated: CXXTerraNode; +} +function updateOp(sourceNode: CXXTerraNode, opConfig: CUDNodeOp): boolean { + // https://lodash.com/docs/4.17.15#mergeWith + function _customizer(objValue: any, srcValue: any) { + if (_.isArray(objValue)) { + for (let i = 0; i < srcValue.length; i++) { + let srcValueItem = srcValue[i]; + + for (let j = 0; j < objValue.length; j++) { + let objValueItem = objValue[j]; + if (_.isMatch(objValueItem, srcValueItem)) { + objValue[j] = _.merge(objValueItem, srcValueItem); + break; + } + } + } + + return objValue; + } + } + + let updateOpConfig = opConfig as CUDNodeUpdateConfig; + if (_.isMatch(sourceNode, updateOpConfig.node)) { + // _.merge(sourceNode, updateOpConfig.updated); + _.mergeWith(sourceNode, updateOpConfig.updated, _customizer); + } + + // We don't want to filter this node, but just update it. + return false; +} + +export interface CUDNodeConfig { + deleteNodes?: CXXTerraNode[]; + updateNodes?: CUDNodeUpdateConfig[]; +} + +export interface CUDNodeParserArgs { + configPath: string; +} + +export default function CUDNodeParser( + terraContext: TerraContext, + args: CUDNodeParserArgs, + preParseResult?: ParseResult +): ParseResult | undefined { + let parseResult = preParseResult!; + let configPath = resolvePath(args.configPath, terraContext.configDir); + let config = require(configPath) as CUDNodeConfig; + + if (config.deleteNodes && config.deleteNodes.length) { + let ops = config.deleteNodes.map((it) => { + return { + node: it, + } as CUDNodeDeleteConfig; + }); + deleteNodeInParseResult(parseResult, ops, deleteOp); + } + + if (config.updateNodes && config.updateNodes.length) { + deleteNodeInParseResult(parseResult, config.updateNodes, updateOp); + } + + return parseResult; +} + +function filterNodes( + sourceNodes: any[], + nodesToFilter: any[], + op: (sourceNode: CXXTerraNode, opConfig: CUDNodeOp) => boolean +): CXXTerraNode[] { + return sourceNodes.filter((it) => { + for (let n of nodesToFilter) { + if (op(it, n)) { + // if the op function return true, then filter the node + return false; + } + } + + return true; + }); +} + +function deleteNodeInParseResult( + originalParseResult: ParseResult, + deleteNodes: CUDNodeOp[], + op: (sourceNode: CXXTerraNode, opConfig: CUDNodeOp) => boolean +): ParseResult { + // Remove nodes + // 1. Remove CXXFile + // 2. Remove Clazz + + let parseResult = originalParseResult; + + parseResult.nodes = filterNodes( + parseResult.nodes, + deleteNodes.filter((node) => node.node.__TYPE == CXXTYPE.CXXFile), + op + ); + + parseResult.nodes.forEach((it) => { + // Handle the parent nodes + (it as CXXFile).nodes = filterNodes( + (it as CXXFile).nodes, + deleteNodes.filter((node) => node.node.__TYPE != CXXTYPE.CXXFile), + op + ); + + // Handle the children nodes + (it as CXXFile).nodes.forEach((it) => { + // filter Clazz + // filter Struct + if (it.__TYPE == CXXTYPE.Struct || it.__TYPE == CXXTYPE.Clazz) { + // The Struct is the sub class of Clazz + let node = it as Clazz; + + node.constructors = filterNodes( + node.constructors, + deleteNodes.filter((node) => node.node.__TYPE == CXXTYPE.Constructor), + op + ) as Constructor[]; + + node.methods = filterNodes( + node.methods, + deleteNodes.filter( + (node) => node.node.__TYPE == CXXTYPE.MemberFunction + ), + op + ) as MemberFunction[]; + node.methods.forEach((method) => { + method.parameters = filterNodes( + method.parameters, + deleteNodes.filter((node) => node.node.__TYPE == CXXTYPE.Variable), + op + ) as Variable[]; + }); + + node.member_variables = filterNodes( + node.member_variables, + deleteNodes.filter( + (node) => node.node.__TYPE == CXXTYPE.MemberVariable + ), + op + ) as MemberVariable[]; + } else if (it.__TYPE == CXXTYPE.Enumz) { + // filter Enumz + let node = it as Enumz; + node.enum_constants = filterNodes( + node.enum_constants, + deleteNodes.filter( + (node) => node.node.__TYPE == CXXTYPE.EnumConstant + ), + op + ) as EnumConstant[]; + } + }); + }); + + return parseResult; +} + +export function isNodeMatched(sourceNode: CXXTerraNode, nodeToMatch: any): boolean { + return _.isMatch(sourceNode, nodeToMatch); +} diff --git a/scripts/terra/parsers/dart_project_marker_parser.ts b/scripts/terra/parsers/dart_project_marker_parser.ts new file mode 100644 index 0000000..e97179f --- /dev/null +++ b/scripts/terra/parsers/dart_project_marker_parser.ts @@ -0,0 +1,330 @@ +import { + CXXFile, + CXXTerraNode, + MemberFunction, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + TerraContext, + resolvePath, +} from "@agoraio-extensions/terra-core"; +import { checkObjInclude } from "@agoraio-extensions/terra_shared_configs"; + +export interface DartProjectMarkerParserUserData { + jsonConverter?: JsonConverterConfig; + displayNameConfig?: DisplayNameConfig; + trimNamePrefixConfig?: TrimNamePrefixConfig; + isHiddenNode?: boolean; + flattenParamNodeConfig?: FlattenParamNodeConfig; + isHiddenNodesAfterFlatten?: boolean; +} + +export interface JsonConverterConfig { + node: CXXTerraNode; + jsonConverter: string; +} + +export interface DisplayAndActualTypeConverter { + from?: (parseResult: ParseResult, config: DisplayNameConfig) => string; + to?: (parseResult: ParseResult, config: DisplayNameConfig) => string; + defaultValueBuilder?: ( + parseResult: ParseResult, + config: DisplayNameConfig, + formattedDartName: string + ) => string; +} + +export interface DisplayNameConfig { + node: CXXTerraNode; + displayName?: string; + displayType?: string; + converter?: DisplayAndActualTypeConverter; +} + +export interface TrimNamePrefixConfig { + node: CXXTerraNode; + trimPrefix: string; +} + +export interface FlattenParamNodeConfig { + flattenFunctionNode?: CXXTerraNode; + // isFlattenFunctionNode?: ( + // parseResult: ParseResult, + // config: FlattenNodeConfig, + // node: CXXTerraNode + // ) => boolean; + flattenParams?: CXXTerraNode[]; + isFlattenParam?: ( + parseResult: ParseResult, + config: FlattenParamNodeConfig, + node: CXXTerraNode + ) => boolean; + hiddenParamsAfterFlatten?: CXXTerraNode[]; + notANamedParametersAfterFlatten?: CXXTerraNode[]; +} + +export interface DartProjectMarkerParserConfig { + jsonConverters?: JsonConverterConfig[]; + displayNameConfigs?: DisplayNameConfig[]; + trimNamePrefixConfigs?: TrimNamePrefixConfig[]; + hiddenNodes?: CXXTerraNode[]; // hide node in global scope + flattenParamNodeConfigs?: FlattenParamNodeConfig[]; +} + +export interface DartProjectMarkerArgs { + configPath: string; +} + +export default function DartProjectMarkerParser( + terraContext: TerraContext, + args: DartProjectMarkerArgs, + preParseResult?: ParseResult +): ParseResult | undefined { + let parseResult = preParseResult!; + let configPath = resolvePath(args.configPath, terraContext.configDir); + let config = require(configPath) as DartProjectMarkerParserConfig; + let jsonConverters = config.jsonConverters ?? []; + let displayNameConfigs = config.displayNameConfigs ?? []; + let trimNamePrefixConfigs = config.trimNamePrefixConfigs ?? []; + let hiddenNodes = config.hiddenNodes ?? []; + let flattenParamNodeConfigs = config.flattenParamNodeConfigs ?? []; + + (parseResult.nodes as CXXFile[]) + .flatMap((node) => node.nodes) + .forEach((node) => { + if (node.isClazz()) { + node.asClazz().methods.forEach((method) => { + for (let hiddenNode of hiddenNodes) { + if (checkObjInclude(method, hiddenNode)) { + applyHiddenNodeConfig(method, true); + } + } + + for (let flattenNodeConfig of flattenParamNodeConfigs) { + if (flattenNodeConfig.flattenFunctionNode) { + if ( + checkObjInclude(method, flattenNodeConfig.flattenFunctionNode) + ) { + applyFlattenParamNodeConfig(method, flattenNodeConfig); + } + } + } + + method.parameters.forEach((variable) => { + // let isFlattenParams = false; + for (let flattenNodeConfig of flattenParamNodeConfigs) { + if (flattenNodeConfig.flattenParams) { + for (let flattenParam of flattenNodeConfig.flattenParams) { + if (checkObjInclude(variable, flattenParam)) { + applyFlattenParamNodeConfig(variable, flattenNodeConfig); + } + } + } + + if (flattenNodeConfig.isFlattenParam) { + if ( + flattenNodeConfig.isFlattenParam( + parseResult, + flattenNodeConfig, + variable + ) + ) { + applyFlattenParamNodeConfig(variable, flattenNodeConfig); + } + } + } + }); + + method.parameters.forEach((variable) => { + for (let displayNameConfig of displayNameConfigs) { + if (checkObjInclude(variable, displayNameConfig.node)) { + applyDisplayNameConfig(variable, displayNameConfig); + } + } + }); + }); + } + + if (node.isStruct()) { + let structt = node.asStruct(); + + structt.member_variables.forEach((variable) => { + for (let jsonConverter of jsonConverters) { + if (checkObjInclude(variable, jsonConverter.node)) { + applyJsonConverterConfig(variable, jsonConverter); + } + } + + for (let displayNameConfig of displayNameConfigs) { + if (checkObjInclude(variable, displayNameConfig.node)) { + applyDisplayNameConfig(variable, displayNameConfig); + } + } + + for (let hiddenNode of hiddenNodes) { + if (checkObjInclude(variable, hiddenNode)) { + applyHiddenNodeConfig(variable, true); + } + } + }); + } + + if (node.isEnumz()) { + let enumz = node.asEnumz(); + for (let trimNamePrefixConfig of trimNamePrefixConfigs) { + if (checkObjInclude(enumz, trimNamePrefixConfig.node)) { + applyTrimNamePrefixConfig(enumz, trimNamePrefixConfig); + } + } + + enumz.enum_constants.forEach((enumConstant) => { + for (let jsonConverter of jsonConverters) { + if (checkObjInclude(enumConstant, jsonConverter.node)) { + applyJsonConverterConfig(enumConstant, jsonConverter); + } + } + + for (let displayNameConfig of displayNameConfigs) { + if (checkObjInclude(enumConstant, displayNameConfig.node)) { + applyDisplayNameConfig(enumConstant, displayNameConfig); + } + } + }); + } + }); + + return parseResult; +} + +function applyJsonConverterConfig( + node: CXXTerraNode, + jsonConverterConfig: JsonConverterConfig +) { + node.user_data ??= {}; + if (!node.user_data["DartProjectMarkerParser"]) { + node.user_data["DartProjectMarkerParser"] = + {} as DartProjectMarkerParserUserData; + } + node.user_data["DartProjectMarkerParser"].jsonConverter = jsonConverterConfig; +} + +function applyDisplayNameConfig( + node: CXXTerraNode, + displayNameConfig: DisplayNameConfig +) { + node.user_data ??= {}; + if (!node.user_data["DartProjectMarkerParser"]) { + node.user_data["DartProjectMarkerParser"] = + {} as DartProjectMarkerParserUserData; + } + node.user_data["DartProjectMarkerParser"].displayNameConfig = + displayNameConfig; +} + +function applyTrimNamePrefixConfig( + node: CXXTerraNode, + trimNamePrefixConfig: TrimNamePrefixConfig +) { + node.user_data ??= {}; + if (!node.user_data["DartProjectMarkerParser"]) { + node.user_data["DartProjectMarkerParser"] = + {} as DartProjectMarkerParserUserData; + } + node.user_data["DartProjectMarkerParser"].trimNamePrefixConfig = + trimNamePrefixConfig; +} + +function applyHiddenNodeConfig(node: CXXTerraNode, isHiddenNode: boolean) { + node.user_data ??= {}; + if (!node.user_data["DartProjectMarkerParser"]) { + node.user_data["DartProjectMarkerParser"] = + {} as DartProjectMarkerParserUserData; + } + node.user_data["DartProjectMarkerParser"].isHiddenNode = isHiddenNode; +} + +function applyIsHiddenNodesAfterFlatten( + node: CXXTerraNode, + isHiddenNodesAfterFlatten: boolean +) { + node.user_data ??= {}; + if (!node.user_data["DartProjectMarkerParser"]) { + node.user_data["DartProjectMarkerParser"] = + {} as DartProjectMarkerParserUserData; + } + node.user_data["DartProjectMarkerParser"].isHiddenNodesAfterFlatten = + isHiddenNodesAfterFlatten; +} + +export function isHiddenNodesAfterFlatten( + node: CXXTerraNode, + target: CXXTerraNode +): boolean { + let hiddenParamsAfterFlatten = + getDartProjectMarkerParserUserData(node)?.flattenParamNodeConfig + ?.hiddenParamsAfterFlatten ?? []; + for (let hiddenNode of hiddenParamsAfterFlatten) { + if (checkObjInclude(target, hiddenNode)) { + return true; + } + } + + return false; +} + +export function isNotANamedParametersAfterFlattenOfMethod( + node: MemberFunction, + target: CXXTerraNode +): boolean { + let notANamedParametersAfterFlatten = + getDartProjectMarkerParserUserData(node)?.flattenParamNodeConfig + ?.notANamedParametersAfterFlatten ?? []; + for (let param of notANamedParametersAfterFlatten) { + if (checkObjInclude(target, param)) { + return true; + } + } + + return false; +} + +function applyFlattenParamNodeConfig( + node: CXXTerraNode, + flattenParamNodeConfig: FlattenParamNodeConfig +) { + node.user_data ??= {}; + if (!node.user_data["DartProjectMarkerParser"]) { + node.user_data["DartProjectMarkerParser"] = + {} as DartProjectMarkerParserUserData; + } + node.user_data["DartProjectMarkerParser"].flattenParamNodeConfig = + flattenParamNodeConfig; +} + +export function isFlattenParamNodeTypeStruct( + parseResult: ParseResult, + node: CXXTerraNode +): boolean { + let allParams = (parseResult.nodes as CXXFile[]) + .flatMap((node) => node.nodes) + .filter((e) => e.isClazz()) + .flatMap((clazz) => clazz.asClazz().methods) + .flatMap((method) => method.parameters) + .filter( + (param) => + getDartProjectMarkerParserUserData(param)?.flattenParamNodeConfig + ); + let allStructs = allParams.map((param) => { + return parseResult.resolveNodeByType(param.type); + }); + + return allStructs.find((struct) => struct == node) !== undefined; +} + +export function getDartProjectMarkerParserUserData( + node: CXXTerraNode +): DartProjectMarkerParserUserData | undefined { + return node.user_data?.[ + "DartProjectMarkerParser" + ] as DartProjectMarkerParserUserData; +} diff --git a/scripts/terra/parsers/dart_syntax_parser.ts b/scripts/terra/parsers/dart_syntax_parser.ts new file mode 100644 index 0000000..84c6d62 --- /dev/null +++ b/scripts/terra/parsers/dart_syntax_parser.ts @@ -0,0 +1,363 @@ +import { + CXXFile, + CXXTYPE, + CXXTerraNode, + Clazz, + EnumConstant, + Enumz, + SimpleType, + SimpleTypeKind, + TypeAlias, +} from "@agoraio-extensions/cxx-parser"; +import { ParseResult, TerraContext } from "@agoraio-extensions/terra-core"; +import { isUIntPtr, setUserdata } from "../renderers/utils"; + +import path from "path"; +import { getOutVariable } from "@agoraio-extensions/terra_shared_configs"; +import { getDartProjectMarkerParserUserData } from "./dart_project_marker_parser"; + +const userDataKey = "DartSyntaxParser"; + +export interface DartSyntaxParserValue { + dartFileName?: string; + dartName?: string; +} + +function _dartClassName(name: string): string { + // `IRtcEngine` -> `RtcEngine` + // `Input` -> `Input` + if ( + name !== name.toUpperCase() && // not all uppercase + name.startsWith("I") && + name.length > 1 && + name[1] === name[1].toUpperCase() + ) { + name = name.replace("I", ""); + } + + return nameWithUnderscoresToCamelCase(name, true); +} + +export function toDartMemberName(memberName: string): string { + return nameWithUnderscoresToCamelCase(memberName.trimNamespace()); +} + +export function enumConstantNaming( + enumz: Enumz, + enumConstant: EnumConstant +): string { + let enumValueName: string = enumConstant.name; + let trimPrefix = enumz.name; + if (getDartProjectMarkerParserUserData(enumz)?.trimNamePrefixConfig) { + trimPrefix = + getDartProjectMarkerParserUserData(enumz)!.trimNamePrefixConfig! + .trimPrefix; + } + + let name = nameWithUnderscoresToCamelCase( + enumValueName.replace(trimPrefix, ""), + false + ); + if ( + getDartProjectMarkerParserUserData(enumConstant)?.displayNameConfig + ?.displayName + ) { + let displayNameConfig = + getDartProjectMarkerParserUserData(enumConstant)?.displayNameConfig!; + name = displayNameConfig.displayName!; + } + + name = name[0].toLowerCase() + name.slice(1); + + return name; +} + +export function toDartStyleNaming( + memberName: string, + upperCamelCase: boolean = false +): string { + let name = nameWithUnderscoresToCamelCase( + memberName.trimNamespace(), + upperCamelCase + ); + + if (upperCamelCase && name[0] == name[0].toLowerCase()) { + name = name[0].toUpperCase() + name.slice(1); + } else if (!upperCamelCase && name[0] == name[0].toUpperCase()) { + name = name[0].toLowerCase() + name.slice(1); + } + + return name; +} + +function nameWithUnderscoresToCamelCase( + nameWithUnderscores: string, + upperCamelCase: boolean = false +): string { + if (!nameWithUnderscores.includes("_")) { + if ( + !upperCamelCase && + nameWithUnderscores === nameWithUnderscores.toUpperCase() + ) { + nameWithUnderscores = nameWithUnderscores.toLowerCase(); + } + + return nameWithUnderscores; + } + + const nameWithUnderscoresLower = nameWithUnderscores.toLowerCase(); + const words = nameWithUnderscoresLower.split("_"); + for (let i = 0; i < words.length; i++) { + let word = words[i]; + + if (word.length > 0 && ((i === 0 && upperCamelCase) || i !== 0)) { + word = word[0].toUpperCase() + word.slice(1); + } + + words[i] = word; + } + + return words.join(""); +} + +function _dartFileName(filePath: string): string { + let fileName = path.basename(filePath); + fileName = upperCamelCaseToLowercaseWithUnderscores(fileName); + if (fileName.startsWith("i")) { + fileName = fileName.replace("i", ""); + } + return fileName; +} + +function upperCamelCaseToLowercaseWithUnderscores( + upperCamelCaseName: string +): string { + const result: string[] = []; + + let toSearch = upperCamelCaseName; + + let baseRegex = new RegExp("((I[A-Z]|[A-Z])?[a-z0-9]*)"); + + let baseMatch; + + while ((baseMatch = baseRegex.exec(toSearch)) !== null) { + let tmp = baseMatch[0]; + if (tmp.length === 0) { + break; + } + result.push(tmp.toLowerCase()); + toSearch = toSearch.replace(tmp, ""); + } + + return result.join("_"); +} + +const _cppTypedefToDartTypeMappping: Map = new Map([ + ["uid_t", "int"], + ["track_id_t", "int"], + ["video_track_id_t", "int"], + ["conn_id_t", "int"], + ["view_t", "int"], + ["AString", "String"], + ["user_id_t", "String"], +]); + +const _cppStdTypeToDartTypeMappping: Map = new Map([ + ["char", "String"], + ["char *", "String"], + ["const char *", "String"], + ["unsigned int", "int"], + ["size_t", "int"], + ["unsigned short", "int"], + ["float", "double"], + ["int64_t", "int"], + ["int32_t", "int"], + ["long", "int"], + ["int16_t", "int"], + ["unsigned char", "Uint8List"], + ["unsigned char *", "Uint8List"], + ["uint8_t", "int"], + ["uint32_t", "int"], + ["uint64_t", "int"], + ["uint16_t", "int"], + ["long long", "int"], + ["intptr_t", "int"], +]); + +function _dartTypeName(parseResult: ParseResult, type: SimpleType): string { + let typeNode = parseResult.resolveNodeByType(type); + let dartType = typeNode.name.trimNamespace(); + if (typeNode.__TYPE == CXXTYPE.Clazz || typeNode.__TYPE == CXXTYPE.Struct) { + dartType = _dartClassName(dartType); + } else if (typeNode.__TYPE == CXXTYPE.Enumz) { + if (dartType.length == 0) { + dartType = (typeNode.parent?.name.trimNamespace() ?? "") + "Enum"; + } else { + dartType = _dartClassName(dartType); + } + } else if ( + typeNode.isSimpleType() && + typeNode.asSimpleType().kind == SimpleTypeKind.template_t && + typeNode.asSimpleType().template_arguments.length > 0 + ) { + dartType = typeNode.asSimpleType().template_arguments[0].trimNamespace(); + } + + if ( + (dartType == "unsigned char" || dartType == "uint8_t") && + (type.kind == SimpleTypeKind.pointer_t || + type.kind == SimpleTypeKind.array_t) + ) { + dartType = "Uint8List"; + } else { + if (_cppStdTypeToDartTypeMappping.has(dartType)) { + dartType = _cppStdTypeToDartTypeMappping.get(dartType)!; + } + if (_cppTypedefToDartTypeMappping.has(dartType)) { + dartType = _cppTypedefToDartTypeMappping.get(dartType)!; + } + if (dartType.includes("_")) { + dartType = nameWithUnderscoresToCamelCase(dartType, true); + } + + // String type + if ( + type.name == "char" && + (type.kind == SimpleTypeKind.array_t || + type.kind == SimpleTypeKind.pointer_t) + ) { + if (type.source.endsWith("**")) { + dartType = "List<" + dartType + ">"; + } + } else { + if (type.kind == SimpleTypeKind.array_t) { + dartType = "List<" + dartType + ">"; + } + } + + if (isUIntPtr(parseResult, type)) { + dartType = "int"; + } + } + + return dartType; +} + +export default function DartSyntaxParser( + terraContext: TerraContext, + args: any, + preParseResult?: ParseResult +): ParseResult | undefined { + let cxxFiles = preParseResult!.nodes as CXXFile[]; + + cxxFiles.forEach((cxxFile: CXXFile) => { + setUserdata(cxxFile, userDataKey, { + dartFileName: _dartFileName(cxxFile.fileName), + }); + + cxxFile.nodes.forEach((node) => { + if (node.__TYPE == CXXTYPE.Clazz || node.__TYPE == CXXTYPE.Struct) { + let clazz = node as Clazz; + + setUserdata(clazz, userDataKey, { + dartName: _dartClassName(clazz.name), + }); + + clazz.methods.forEach((method) => { + let name = method.name; + if ( + getDartProjectMarkerParserUserData(method)?.displayNameConfig + ?.displayName + ) { + let displayNameConfig = + getDartProjectMarkerParserUserData(method)?.displayNameConfig!; + name = displayNameConfig.displayName!; + } + setUserdata(method, userDataKey, { + dartName: toDartStyleNaming(name), + }); + + setUserdata(method.return_type, userDataKey, { + dartName: _dartTypeName(preParseResult!, method.return_type), + }); + + // If the `ReturnTypeParser` is applied, the `ReturnTypeParser` phase is executed before the `DartSyntaxParser`. + // Therefore, the parameter associated with the out variable's naming may not be set. + // In such cases, we need to reapply the naming here to ensure consistency. + let outVariable = getOutVariable(method); + if (outVariable) { + setUserdata(outVariable, userDataKey, { + dartName: toDartStyleNaming(outVariable.name), + }); + } + + method.parameters.forEach((param) => { + setUserdata(param.type, userDataKey, { + dartName: _dartTypeName(preParseResult!, param.type), + }); + + // let paramName = param.name; + // if ( + // getDartProjectMarkerParserUserData(param)?.displayNameConfig + // ?.displayName + // ) { + // let displayNameConfig = + // getDartProjectMarkerParserUserData(param)?.displayNameConfig!; + // paramName = displayNameConfig.displayName!; + // } + setUserdata(param, userDataKey, { + dartName: toDartStyleNaming(param.name), + }); + }); + }); + + clazz.member_variables.forEach((member) => { + setUserdata(member, userDataKey, { + dartName: toDartStyleNaming(member.name), + }); + + setUserdata(member.type, userDataKey, { + dartName: _dartTypeName(preParseResult!, member.type), + }); + }); + } else if (node.__TYPE == CXXTYPE.TypeAlias) { + let typeAlias = node as TypeAlias; + setUserdata(typeAlias, userDataKey, { + dartName: toDartStyleNaming(typeAlias.name), + }); + setUserdata(typeAlias.underlyingType, userDataKey, { + dartName: _dartTypeName(preParseResult!, typeAlias.underlyingType), + }); + } else if (node.__TYPE == CXXTYPE.Enumz) { + let enumz = node as Enumz; + setUserdata(enumz, userDataKey, { + dartName: _dartClassName(enumz.name), + }); + enumz.enum_constants.forEach((enumConstant) => { + setUserdata(enumConstant, userDataKey, { + dartName: enumConstantNaming(enumz, enumConstant), + }); + }); + } else if (node.isVariable()) { + let v = node.asVariable(); + setUserdata(v, userDataKey, { + dartName: toDartStyleNaming(v.name), + }); + + setUserdata(v.type, userDataKey, { + dartName: _dartTypeName(preParseResult!, v.type), + }); + } + }); + }); + + return preParseResult; +} + +export function dartFileName(node: CXXTerraNode): string { + return node.user_data?.[userDataKey]?.dartFileName ?? ""; +} + +export function dartName(node: CXXTerraNode): string { + return node.user_data?.[userDataKey]?.dartName ?? ""; +} diff --git a/scripts/terra/parsers/dart_type_remapping_parser.ts b/scripts/terra/parsers/dart_type_remapping_parser.ts new file mode 100644 index 0000000..3b2ac25 --- /dev/null +++ b/scripts/terra/parsers/dart_type_remapping_parser.ts @@ -0,0 +1,109 @@ +import { + CXXFile, + CXXTerraNode, + MemberFunction, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + TerraContext, + resolvePath, +} from "@agoraio-extensions/terra-core"; +import { checkObjInclude } from "@agoraio-extensions/terra_shared_configs"; + +export interface DartTypeRemappingInitializer { + from?: (parseResult: ParseResult, config: DartTypeRemappingConfig) => string; + to?: (parseResult: ParseResult, config: DartTypeRemappingConfig) => string; + defaultValueBuilder?: ( + parseResult: ParseResult, + config: DartTypeRemappingConfig, + formattedDartName: string + ) => string; +} + +export interface DartTypeRemappingParserUserData { + config?: DartTypeRemappingConfig; +} + +export interface DartTypeRemappingConfig { + node: CXXTerraNode; + dartType: string; + converter?: DartTypeRemappingInitializer; +} + +export interface DartTypeRemappingParserConfig { + remappingNodes?: DartTypeRemappingConfig[]; +} + +export interface DartTypeRemappingArgs { + configPath: string; +} + +export default function DartTypeRemappingParser( + terraContext: TerraContext, + args: DartTypeRemappingArgs, + preParseResult?: ParseResult +): ParseResult | undefined { + let parseResult = preParseResult!; + let configPath = resolvePath(args.configPath, terraContext.configDir); + let config = require(configPath) as DartTypeRemappingParserConfig; + let remappingNodes = config.remappingNodes ?? []; + + function applyUserDataToMethods(methods: MemberFunction[]) { + methods.forEach((method) => { + method.parameters.forEach((variable) => { + for (let remappingNode of remappingNodes) { + if (checkObjInclude(variable, remappingNode.node)) { + applyUserData(variable, remappingNode); + } + } + }); + }); + } + + function appUserDataToMemberVariables(memberVariables: CXXTerraNode[]) { + memberVariables.forEach((variable) => { + for (let remappingNode of remappingNodes) { + if (checkObjInclude(variable, remappingNode.node)) { + applyUserData(variable, remappingNode); + break; + } + } + }); + } + + (parseResult.nodes as CXXFile[]) + .flatMap((node) => node.nodes) + .filter((e) => e.isClazz() || e.isStruct()) + // .flatMap((clazz) => clazz.asClazz().methods) + // .flatMap((method) => method.parameters) + .forEach((node) => { + if (node.isClazz()) { + let clazz = node.asClazz(); + + applyUserDataToMethods(clazz.methods); + appUserDataToMemberVariables(clazz.member_variables); + } else if (node.isStruct()) { + let struct = node.asStruct(); + + applyUserDataToMethods(struct.methods); + appUserDataToMemberVariables(struct.member_variables); + } + }); + + return parseResult; +} + +function applyUserData(node: CXXTerraNode, config?: DartTypeRemappingConfig) { + node.user_data ??= {}; + node.user_data["DartTypeRemappingParser"] = { + config: config, + } as DartTypeRemappingParserUserData; +} + +export function getDartTypeRemapping( + node: CXXTerraNode +): DartTypeRemappingParserUserData | undefined { + return node.user_data?.[ + "DartTypeRemappingParser" + ] as DartTypeRemappingParserUserData; +} diff --git a/scripts/terra/parsers/default_value_marker_parser.ts b/scripts/terra/parsers/default_value_marker_parser.ts new file mode 100644 index 0000000..8c919c1 --- /dev/null +++ b/scripts/terra/parsers/default_value_marker_parser.ts @@ -0,0 +1,47 @@ +import { CXXFile, CXXTerraNode } from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + TerraContext, + resolvePath, +} from "@agoraio-extensions/terra-core"; +import { checkObjInclude } from "@agoraio-extensions/terra_shared_configs"; + +export interface DefaultValuesConfig { + node: CXXTerraNode; + value: string; +} + +export interface DefaultValuesConfigs { + defaultValues?: DefaultValuesConfig[]; +} + +export interface DefaultValueMarkerParserArgs { + configPath: string; +} + +export default function DefaultValueMarkerParser( + terraContext: TerraContext, + args: DefaultValueMarkerParserArgs, + preParseResult?: ParseResult +): ParseResult | undefined { + let parseResult = preParseResult!; + let configPath = resolvePath(args.configPath, terraContext.configDir); + let config = require(configPath) as DefaultValuesConfigs; + let defaultValues = config.defaultValues ?? []; + + (parseResult.nodes as CXXFile[]) + .flatMap((node) => node.nodes) + .filter((e) => e.isClazz()) + .flatMap((clazz) => clazz.asClazz().methods) + .flatMap((method) => method.parameters) + .forEach((variable) => { + for (let defaultValue of defaultValues) { + if (checkObjInclude(variable, defaultValue.node)) { + variable.default_value = defaultValue.value; + break; + } + } + }); + + return parseResult; +} diff --git a/scripts/terra/parsers/sync_function_marker_parser.ts b/scripts/terra/parsers/sync_function_marker_parser.ts new file mode 100644 index 0000000..69d152a --- /dev/null +++ b/scripts/terra/parsers/sync_function_marker_parser.ts @@ -0,0 +1,67 @@ +import { + CXXFile, + CXXTerraNode, + MemberFunction, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + TerraContext, + resolvePath, +} from "@agoraio-extensions/terra-core"; +import { checkObjInclude } from "@agoraio-extensions/terra_shared_configs"; + +export interface SyncFunctionsMarkerParserUserData { + isMarkedAsSyncFunction?: boolean; +} + +export interface SyncFunctionsMarkerParserConfig { + syncFunctionNodes?: CXXTerraNode[]; +} + +export interface SyncFunctionsMarkerArgs { + configPath: string; +} + +export default function SyncFunctionsMarkerParser( + terraContext: TerraContext, + args: SyncFunctionsMarkerArgs, + preParseResult?: ParseResult +): ParseResult | undefined { + let parseResult = preParseResult!; + let configPath = resolvePath(args.configPath, terraContext.configDir); + let config = require(configPath) as SyncFunctionsMarkerParserConfig; + let syncFunctionNodes = config.syncFunctionNodes ?? []; + + + (parseResult.nodes as CXXFile[]) + .flatMap((node) => node.nodes) + .filter((e) => e.isClazz() ) + .flatMap((clazz) => clazz.asClazz().methods) + // .flatMap((method) => method.parameters) + .forEach((node) => { + for (let syncFunctionNode of syncFunctionNodes) { + if (checkObjInclude(node, syncFunctionNode)) { + // variable.dartType = remappingNode.dartType; + applyUserData(node); + break; + } + } + }); + + return parseResult; +} + +function applyUserData(node: CXXTerraNode) { + node.user_data ??= {}; + node.user_data["SyncFunctionsMarkerParser"] = { + isMarkedAsSyncFunction: true, + } as SyncFunctionsMarkerParserUserData; +} + +export function getSyncFunctionsMarkerParserUserData( + node: CXXTerraNode +): SyncFunctionsMarkerParserUserData | undefined { + return node.user_data?.[ + "SyncFunctionsMarkerParser" + ] as SyncFunctionsMarkerParserUserData; +} diff --git a/scripts/terra/prepare.sh b/scripts/terra/prepare.sh new file mode 100644 index 0000000..3784a19 --- /dev/null +++ b/scripts/terra/prepare.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e +set -x + +MY_PATH=$(realpath $(dirname "$0")) + +pushd ${MY_PATH} + +rm -rf .yarnrc.yml +rm -rf yarn.lock + +echo "nodeLinker: node-modules" >> .yarnrc.yml +echo "enableImmutableInstalls: false" >> .yarnrc.yml +yarn set version berry +yarn + +popd \ No newline at end of file diff --git a/scripts/terra/renderers/api_interface_renderer.ts b/scripts/terra/renderers/api_interface_renderer.ts new file mode 100644 index 0000000..eead80f --- /dev/null +++ b/scripts/terra/renderers/api_interface_renderer.ts @@ -0,0 +1,520 @@ +import { + CXXFile, + CXXTYPE, + Clazz, + MemberFunction, + SimpleTypeKind, + Variable, +} from "@agoraio-extensions/cxx-parser"; +import { + _trim, + defaultDartHeader, + defaultIgnoreForFile, + getBaseClassMethods, + getBaseClasses, + isCallbackClass, + isNullableType, + isNullableVariable, + isRegisterCallbackFunction, + isUnregisterCallbackFunction, + renderEnumJsonSerializable, + renderJsonSerializable, + renderTopLevelVariable, +} from "./utils"; +import { isNodeMatched } from "../parsers/cud_node_parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { + dartFileName, + dartName, + toDartMemberName, +} from "../parsers/dart_syntax_parser"; + +import path from "path"; +import { getSyncFunctionsMarkerParserUserData } from "../parsers/sync_function_marker_parser"; + +export interface ApiInterfaceRendererArgs { + outputDir?: string; +} + +export default function ApiInterfaceRenderer( + terraContext: TerraContext, + args: ApiInterfaceRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let renderResults = (parseResult!.nodes as CXXFile[]) + .filter((cxxFile) => { + return ( + cxxFile.nodes.find((node) => { + return node.isClazz(); + }) != undefined + ); + }) + .map((cxxFile) => { + let subContents = cxxFile.nodes + .filter((e) => e.isClazz()) + .map((node) => { + switch (node.__TYPE) { + case CXXTYPE.Clazz: { + let clazz = node as Clazz; + if (isCallbackClass(clazz.name)) { + return renderCallbackClass(parseResult, clazz); + } else { + return renderClass(parseResult, clazz); + } + } + // case CXXTYPE.Struct: { + // return renderJsonSerializable( + // parseResult, + // dartName(node.asStruct()), + // node.asStruct()!.member_variables + // ); + // } + // case CXXTYPE.Enumz: { + // return renderEnumJsonSerializable(node.asEnumz()); + // } + // case CXXTYPE.Variable: { + // return renderTopLevelVariable(node.asVariable()); + // } + default: { + return ""; + } + } + }) + .join("\n\n"); + + // let isNeedImportGDartFile = + // cxxFile.nodes.find((node) => { + // return node.isStruct() || node.isEnumz(); + // }) != undefined; + + let isNeedImportGDartFile = false; + + let content = _trim(` + import 'binding_forward_export.dart'; + ${ + isNeedImportGDartFile ? `part '${dartFileName(cxxFile)}.g.dart';` : "" + } + + ${subContents} + `); + + return { + file_name: path.join( + args.outputDir ?? "", + `${dartFileName(cxxFile)}.dart` + ), //`lib/src/${dartFileName(cxxFile)}.dart`, + file_content: content, + }; + }); + + return renderResults; +} + +export function renderClass( + parseResult: ParseResult, + clazz: Clazz, + options?: { + extraClassBody?: string; + returnTypeRenderer?: ( + memberFunction: MemberFunction, + isSynchronizedFunc: boolean + ) => string; + parameterListBlockRenderer?: ( + parseResult: ParseResult, + memberFunction: MemberFunction + ) => string; + methodFilter?: (method: MemberFunction) => boolean; + } +) { + let baseClassNames = getBaseClasses(parseResult, clazz).map((it) => + dartName(it) + ); + let implementsBlock = + baseClassNames.length > 0 ? `implements ${baseClassNames.join(", ")}` : ""; + + let methodFilter = options?.methodFilter ?? ((method) => true); + + let funcs = clazz.methods + .filter((method) => methodFilter(method)) + .map((method) => { + return `${functionSignature(parseResult, method, { + ignoreAsyncKeyword: true, + returnTypeRenderer: options?.returnTypeRenderer, + parameterListBlockRenderer: options?.parameterListBlockRenderer, + })};`; + }) + .join("\n\n"); + + return ` +abstract class ${dartName(clazz)} ${implementsBlock} { + ${options?.extraClassBody ?? ""} + + ${funcs} +} + `; +} + +export function renderCallbackClass(parseResult: ParseResult, clazz: Clazz) { + let clazzName = dartName(clazz); + let baseClassMethods = getBaseClassMethods(parseResult, clazz); + let baseClassNames = getBaseClasses(parseResult, clazz).map((it) => + dartName(it) + ); + let extendsBlock = + baseClassNames.length > 0 ? `extends ${baseClassNames.join(", ")}` : ""; + + let constructorSuperBlock = ""; + if (baseClassMethods.length > 0) { + constructorSuperBlock = ` +super( +${baseClassMethods.map((it) => `${dartName(it)} : ${dartName(it)},`).join("")} +) +`; + } + + let constructorParameters: string[] = []; + if (baseClassMethods.length > 0) { + baseClassMethods.forEach((it) => { + let funcType = `${dartName(it.return_type)} Function(${it.parameters + .map((param) => `${dartName(param.type)} ${dartName(param)}`) + .join(", ")})?`; + constructorParameters.push(`${funcType} ${dartName(it)},`); + }); + } + clazz.methods.forEach((method) => { + constructorParameters.push(`this.${dartName(method)},`); + }); + + let constructorBlock = _trim(` + /// Construct the [${clazzName}]. + const ${clazzName}({ + ${constructorParameters.join("")} + }) + ${constructorSuperBlock.length > 0 ? `: ${constructorSuperBlock}` : ""};`); + + // NOTE: A new line between the constructor and the methods is required, otherwise the `iris_doc` CLI + // may cause a bug. + return _trim(` + class ${clazzName} ${extendsBlock} { + + ${constructorBlock} + + ${clazz.methods + .map((method) => { + let funcType = `${dartName( + method.return_type + )} Function(${method.parameters + .map((param) => `${dartName(param.type)} ${dartName(param)}`) + .join(", ")})?`; + return `final ${funcType} ${dartName(method)};`; + }) + .join("\n\n")} + } + `); +} + +// We treat the callback as a synchronized function, see the logic inside `isSynchronizedFunction`, +// but some functions with callback parameters are not synchronized, so we need to add them to the `asyncFunctions` list. +const asyncFunctions = [ + // agora::rtc::IMediaRecorder + { + __TYPE: CXXTYPE.MemberFunction, + name: "setMediaRecorderObserver", + parent_name: "IMediaRecorder", + namespaces: ["agora", "rtc"], + }, + // agora::rtc::IRtcEngine::startDirectCdnStreaming + { + __TYPE: CXXTYPE.MemberFunction, + name: "startDirectCdnStreaming", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, +]; + +const synchronizedFunctions = [ + // agora::rtc::IRtcEngine + { + __TYPE: CXXTYPE.MemberFunction, + name: "getVideoDeviceManager", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getAudioDeviceManager", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getMediaEngine", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getMediaRecorder", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getLocalSpatialAudioEngine", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getMusicContentCenter", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getH265Transcoder", + parent_name: "IRtcEngine", + namespaces: ["agora", "rtc"], + }, + // agora::rtc::IMediaPlayer + { + __TYPE: CXXTYPE.MemberFunction, + name: "getMediaPlayerId", + parent_name: "IMediaPlayer", + namespaces: ["agora", "rtc"], + }, + // agora::rtc::MusicCollection + { + __TYPE: CXXTYPE.MemberFunction, + name: "getCount", + parent_name: "MusicCollection", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getTotal", + parent_name: "MusicCollection", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getPage", + parent_name: "MusicCollection", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getPageSize", + parent_name: "MusicCollection", + namespaces: ["agora", "rtc"], + }, + { + __TYPE: CXXTYPE.MemberFunction, + name: "getMusic", + parent_name: "MusicCollection", + namespaces: ["agora", "rtc"], + }, +]; +export function isSynchronizedFunction( + parseResult: ParseResult, + memberFunction: MemberFunction +): boolean { + let isSyncFunc = + synchronizedFunctions.find((it) => isNodeMatched(memberFunction, it)) != + undefined || + isRegisterCallbackFunction(memberFunction) || + isUnregisterCallbackFunction(memberFunction); + if ( + !isSyncFunc && + asyncFunctions.find((it) => isNodeMatched(memberFunction, it)) == undefined + ) { + // If there a callback class in the parameter list, then it's a synchronized function + isSyncFunc = + memberFunction.parameters.find((param) => { + let tmpNode = parseResult.resolveNodeByType(param.type); + if (tmpNode.__TYPE == CXXTYPE.Clazz) { + return isCallbackClass(tmpNode.asClazz().name); + } + return false; + }) != undefined; + } + return isSyncFunc; +} + +export function defaultReturnTypeRenderer( + memberFunction: MemberFunction, + isSynchronizedFunc: boolean +): string { + let returnType = dartName(memberFunction.return_type); + returnType = `${returnType}${ + isNullableType(memberFunction.return_type) ? "?" : "" + }`; + if (!isSynchronizedFunc) { + returnType = `Future<${returnType}>`; + } + return returnType; +} + +export function defaultParameterListBlockRenderer( + parseResult: ParseResult, + memberFunction: MemberFunction +): string { + let parameterListBlock = memberFunction.parameters + .map((param) => { + let typeNode = parseResult.resolveNodeByType(param.type); + let typeName = dartName(param.type); + let variableName = dartName(param); + let defaultValue = ""; + let isNeedRequiredKeyword = memberFunction.parameters.length > 1; + + // Has default value + if (param.default_value.length > 0) { + isNeedRequiredKeyword = false; + + if (typeNode.__TYPE == CXXTYPE.Enumz) { + console.log( + `method: ${memberFunction.name}, enum: ${typeNode.name}, default: ${param.default_value}` + ); + defaultValue = `${typeName}.${toDartMemberName(param.default_value)}`; + } else if (typeNode.__TYPE == CXXTYPE.Struct) { + defaultValue = `const ${dartName(typeNode)}()`; + } else { + defaultValue = param.default_value; + } + + if (isNullableVariable(param)) { + typeName = `${typeName}?`; + defaultValue = ""; + } + } + + let requiredKeyword = isNeedRequiredKeyword ? "required" : ""; + let returnValue = `${requiredKeyword} ${typeName} ${variableName}`; + if (defaultValue.length > 0) { + returnValue = `${returnValue} = ${defaultValue}`; + } + + // let value = `${ + // isNeedRequiredKeyword ? "required" : "" + // } ${typeName} ${variableName}${ + // defaultValue.length > 0 ? ` = ${defaultValue}` : "" + // }`; + + return returnValue; + }) + .join(","); + + if ( + memberFunction.parameters.find((it) => it.default_value) || + memberFunction.parameters.length > 1 + ) { + parameterListBlock = `{${parameterListBlock}}`; + } + return parameterListBlock; +} + +export function functionSignature( + parseResult: ParseResult, + memberFunction: MemberFunction, + options?: { + forceAddOverridePrefix?: boolean; + ignoreAsyncKeyword?: boolean; + returnTypeRenderer?: ( + memberFunction: MemberFunction, + isSynchronizedFunc: boolean + ) => string; + parameterListBlockRenderer?: ( + parseResult: ParseResult, + memberFunction: MemberFunction + ) => string; + } +): string { + let isSynchronizedFunc = isSynchronizedFunction(parseResult, memberFunction); + isSynchronizedFunc = + isSynchronizedFunc || + getSyncFunctionsMarkerParserUserData(memberFunction) + ?.isMarkedAsSyncFunction == true; + + let parentClass = memberFunction.parent! as Clazz; + let forceAddOverridePrefix: boolean = + options?.forceAddOverridePrefix ?? false; + let ignoreAsyncKeyword = options?.ignoreAsyncKeyword ?? false; + let addOverrideSurffix = + forceAddOverridePrefix || + getBaseClassMethods(parseResult, parentClass).find( + (it) => it.name == memberFunction.name + ); + let returnTypeRenderer = + options?.returnTypeRenderer ?? defaultReturnTypeRenderer; + let parameterListBlockRenderer = + options?.parameterListBlockRenderer ?? defaultParameterListBlockRenderer; + + let overridePrefix = addOverrideSurffix ? "@override " : ""; + // let returnType = dartName(memberFunction.return_type); + // returnType = `${returnType}${ + // isNullableType(memberFunction.return_type) ? "?" : "" + // }`; + // if (!isSynchronizedFunc) { + // returnType = `Future<${returnType}>`; + // } + let returnType = returnTypeRenderer(memberFunction, isSynchronizedFunc); + let functionName = dartName(memberFunction); + // let defaultValue = ""; + // let isNeedRequiredKeyword = memberFunction.parameters.length > 1; + let asyncKeywordSurffix = + isSynchronizedFunc || ignoreAsyncKeyword ? "" : "async"; + + // let parameterListBlock = memberFunction.parameters + // .map((param) => { + // let typeNode = parseResult.resolveNodeByType(param.type); + // let typeName = dartName(param.type); + // let variableName = dartName(param); + + // // Has default value + // if (param.default_value) { + // isNeedRequiredKeyword = false; + + // if (typeNode.__TYPE == CXXTYPE.Enumz) { + // console.log( + // `method: ${memberFunction.name}, enum: ${typeNode.name}, default: ${param.default_value}` + // ); + // defaultValue = `${typeName}.${toDartMemberName(param.default_value)}`; + // } else if (typeNode.__TYPE == CXXTYPE.Struct) { + // defaultValue = `const ${dartName(typeNode)}()`; + // } else { + // defaultValue = param.default_value; + // } + + // if (isNullableVariable(param)) { + // typeName = `${typeName}?`; + // defaultValue = ""; + // } + // } + + // return `${ + // isNeedRequiredKeyword ? "required" : "" + // } ${typeName} ${variableName}${defaultValue ? ` = ${defaultValue}` : ""}`; + // }) + // .join(","); + + // if ( + // memberFunction.parameters.find((it) => it.default_value) || + // memberFunction.parameters.length > 1 + // ) { + // parameterListBlock = `{${parameterListBlock}}`; + // } + + let parameterListBlock = parameterListBlockRenderer( + parseResult, + memberFunction + ); + + return _trim(` +${overridePrefix} +${returnType} ${functionName}(${parameterListBlock}) ${asyncKeywordSurffix} +`); +} diff --git a/scripts/terra/renderers/buffer_ext_renderer.ts b/scripts/terra/renderers/buffer_ext_renderer.ts new file mode 100644 index 0000000..ba3141f --- /dev/null +++ b/scripts/terra/renderers/buffer_ext_renderer.ts @@ -0,0 +1,121 @@ +import { + CXXFile, + CXXTYPE, + Clazz, + MemberVariable, + Struct, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { + defaultDartHeader, + defaultIgnoreForFile, + isCallbackClass, + isNeedIgnoreJsonInJsonObject, +} from "./utils"; +import { dartFileName, dartName } from "../parsers/dart_syntax_parser"; +import { ApiInterfaceRendererArgs } from "./api_interface_renderer"; + +import path from "path"; + +/// Generate the files: +/// - lib/src/binding/call_api_event_handler_buffer_ext.dart +export default function BufferExtRenderer( + terraContext: TerraContext, + args: ApiInterfaceRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let cxxFiles = parseResult!.nodes as CXXFile[]; + + let extensionContents = cxxFiles + .flatMap((cxxFile: CXXFile) => cxxFile.nodes) + .filter((node) => node.__TYPE == CXXTYPE.Struct) + .map((node) => + renderBufferExtBlock( + parseResult, + dartName(node.asStruct()), + node.asStruct()!.member_variables + ) + ) + .join("\n\n"); + + let content = ` +${defaultDartHeader} + +${defaultIgnoreForFile}, prefer_is_empty + +import 'package:agora_rtm/src/binding_forward_export.dart'; + +${extensionContents} +`; + + return [ + { + file_name: path.join( + args.outputDir ?? "", + "call_api_event_handler_buffer_ext.dart" + ), + file_content: content, + }, + ]; +} + +export function renderBufferExtBlock( + parseResult: ParseResult, + structName: string, + memberVariables: MemberVariable[] +) { + let extName = `${structName}BufferExt`; + + let bufferMemberNames = memberVariables + .filter((member) => dartName(member.type) == "Uint8List") + .map((member) => dartName(member)); + let constructorNamedParams = memberVariables.map( + (it) => `${dartName(it)}: ${dartName(it)}` + ); + + let output = ` +extension ${extName} on ${structName} { + ${structName} fillBuffers(List bufferList) { + if (bufferList.isEmpty) return this; + ${(function () { + let tmpOutput = ""; + for (let i = 0; i < bufferMemberNames.length; i++) { + let n = bufferMemberNames[i]; + tmpOutput += ` + Uint8List? ${n}; + if (bufferList.length > ${i}) { + ${n} = bufferList[${i}]; + }`.trim(); + } + if (bufferMemberNames.length == 0) { + tmpOutput += "return this;"; + } else { + tmpOutput += `return ${structName}(${constructorNamedParams.join( + ", " + )});`; + } + return tmpOutput; + })()} + } + + List collectBufferList() { + final bufferList = []; + ${(function () { + if (bufferMemberNames.length == 0) return "return bufferList;"; + return ` + ${bufferMemberNames + .map((it) => `if (${it} != null) { bufferList.add(${it}!); }`) + .join("\n")} + return bufferList; + `.trim(); + })()} + } +} + `; + + return output; +} diff --git a/scripts/terra/renderers/callapi_impl_renderer.ts b/scripts/terra/renderers/callapi_impl_renderer.ts new file mode 100644 index 0000000..75fe372 --- /dev/null +++ b/scripts/terra/renderers/callapi_impl_renderer.ts @@ -0,0 +1,351 @@ +import { + CXXFile, + CXXTYPE, + Clazz, + MemberFunction, + SimpleTypeKind, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { + _trim, + defaultDartHeader, + defaultIgnoreForFile, + getBaseClasses, + isCallbackClass, + isDartBufferType, + isNullableVariable, + renderJsonSerializable, + variableToMemberVariable, +} from "./utils"; +import { + dartFileName, + dartName, + toDartStyleNaming, +} from "../parsers/dart_syntax_parser"; +import { + getIrisApiIdValue, + getOutVariable, + getOverrideNodeParserUserData, +} from "@agoraio-extensions/terra_shared_configs"; +import { + ApiInterfaceRendererArgs, + functionSignature, + isSynchronizedFunction, +} from "./api_interface_renderer"; + +import path from "path"; + +/// Generate the implementation files. +export default function CallApiImplRenderer( + terraContext: TerraContext, + args: ApiInterfaceRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let outputDir = args.outputDir ?? ""; + let cxxFiles = (parseResult!.nodes as CXXFile[]).filter((cxxFile) => { + return cxxFile.nodes.find((node) => { + return ( + node.__TYPE == CXXTYPE.Clazz && !isCallbackClass((node as Clazz).name) + ); + }); + }); + let renderResults = cxxFiles.map((cxxFile) => { + let subContents = cxxFile.nodes + .filter((it) => it.__TYPE == CXXTYPE.Clazz) + .filter((it) => !isCallbackClass(it.asClazz().name)) + .map((it) => { + let clazz = it.asClazz(); + let clazzName = dartName(clazz); + let methods = clazz.methods; + let methodImpls = methods + .map((method) => callApiImplBlock(parseResult, clazz, method)) + .join("\n\n"); + let baseClassImplNames = getBaseClasses(parseResult, clazz).map( + (baseClass) => `${dartName(baseClass)}Impl` + ); + let shouldOverrideBaseClass = baseClassImplNames.length > 0; + let classExtendsBlock = shouldOverrideBaseClass + ? `extends ${baseClassImplNames.join(", ")} ` + : ""; + let constructorBlock = `${clazzName}Impl(${ + shouldOverrideBaseClass + ? "IrisMethodChannel irisMethodChannel" + : "this.irisMethodChannel" + })${shouldOverrideBaseClass ? ": super(irisMethodChannel)" : ""};`; + + return ` + class ${clazzName}Impl ${classExtendsBlock} implements ${clazzName} { + ${constructorBlock} + + ${ + shouldOverrideBaseClass + ? "" + : ` + final IrisMethodChannel irisMethodChannel;`.trim() + } + + ${shouldOverrideBaseClass ? "@override" : ""} + @protected + Map createParams(Map param) { + return param; + } + + ${shouldOverrideBaseClass ? "@override" : ""} + @protected + bool get isOverrideClassName => false; + + ${shouldOverrideBaseClass ? "@override" : ""} + @protected + String get className => '${clazzName}'; + + ${methodImpls} + } + `.trim(); + }) + .join("\n\n"); + + let content = ` + ${defaultDartHeader} + + ${defaultIgnoreForFile}, annotate_overrides + + import 'binding_forward_export.dart'; + import 'package:iris_method_channel/iris_method_channel.dart'; + + ${subContents} + `; + + return { + file_name: path.join(outputDir, `${dartFileName(cxxFile)}_impl.dart`), + file_content: content, + }; + }); + + return [ + ...renderResults, + callApiImplParamsJsonFile(parseResult, cxxFiles, outputDir), + ]; +} + +interface JsonMapInitBlock { + preInitBlock: string; + nullCheckBlock: string; + jsonKey: string; + jsonValue: string; + addBufferExtBlock: string; +} + +function callApiImplBlock( + parseResult: ParseResult, + clazz: Clazz, + method: MemberFunction +): string { + let className = dartName(clazz); + let methodName = dartName(method); + + let paramJsonMapBlock = method.parameters.map((param) => { + let paramName = dartName(param); + let preInitBlock = ""; + let nullCheckBlock = ""; + let jsonKey = param.name; + let jsonValue = paramName; + let addBufferExtBlock = ""; + + if (isNullableVariable(param)) { + nullCheckBlock = `if (${paramName} != null)`; + } + + let actualNode = parseResult.resolveNodeByType(param.type); + if (actualNode.__TYPE == CXXTYPE.Enumz) { + jsonValue = `${paramName}.value()`; + } else if (actualNode.__TYPE == CXXTYPE.Struct) { + // Null check is not necessary for struct. + nullCheckBlock = ""; + if (param.type.kind == SimpleTypeKind.array_t) { + let tmpParamName = `${paramName}JsonList`; + preInitBlock = `final ${tmpParamName} = ${paramName}.map((e) => e.toJson()).toList();`; + jsonValue = tmpParamName; + addBufferExtBlock = ` + for (final e in ${paramName}) { + buffers.addAll(e.collectBufferList()); + }`; + if (isNullableVariable(param)) { + addBufferExtBlock = `if (${paramName} != null) { + ${addBufferExtBlock} + }`; + } + } else { + jsonValue = `${paramName}${ + isNullableVariable(param) ? "?" : "" + }.toJson()`; + addBufferExtBlock = `buffers.addAll(${paramName}.collectBufferList());`; + if (isNullableVariable(param)) { + addBufferExtBlock = `if (${paramName} != null) { + ${addBufferExtBlock} + }`; + } + } + } else if (isDartBufferType(param.type)) { + jsonKey = ""; + jsonValue = ""; + addBufferExtBlock = `buffers.add(${paramName});`; + } + + return { + preInitBlock: preInitBlock, + nullCheckBlock: nullCheckBlock, + jsonKey: jsonKey, + jsonValue: jsonValue, + addBufferExtBlock: addBufferExtBlock, + } as JsonMapInitBlock; + }); + + let isNoImplFunc = isSynchronizedFunction(parseResult, method); + let isNeedAddBufferExtBlock = + paramJsonMapBlock.find((it) => it.addBufferExtBlock) != undefined; + let buffersValueInJsonMap = isNeedAddBufferExtBlock ? "buffers" : "null"; + + let irisApiIdValue = getIrisApiIdValue(method); + let overridedNode = getOverrideNodeParserUserData(method); + if (overridedNode && overridedNode.redirectIrisApiId) { + irisApiIdValue = overridedNode.redirectIrisApiId; + } + + let apiType = `final apiType = \'\${isOverrideClassName ? className : '${className}'}_${irisApiIdValue + .split("_") + .slice(1) + .join("_")}\';`; + + let returnBlock = ""; + let returnTypeNode = parseResult.resolveNodeByType(method.return_type); + let outVariable = getOutVariable(method); + if (outVariable) { + returnBlock = _trim(` + if (result < 0) { + throwExceptionHandler(code: result); + } + final ${methodName}Json = ${className}${toDartStyleNaming( + methodName, + true + )}Json.fromJson(rm); + return ${methodName}Json.${toDartStyleNaming(outVariable.name)}; + `); + } else if (returnTypeNode.__TYPE == CXXTYPE.Enumz) { + returnBlock = `return ${dartName(returnTypeNode)}Ext.fromValue(result);`; + } else if (method.return_type.name != "void") { + let returnType = dartName(method.return_type); + returnBlock = `return result as ${returnType};`; + } else { + returnBlock = `if (result < 0) { throwExceptionHandler(code: result); }`; + } + + let funcSignature = functionSignature(parseResult, method, { + forceAddOverridePrefix: true, + }); + let funcBlock = _trim( + ` +${apiType} +${paramJsonMapBlock.map((it) => it.preInitBlock).join("\n")} +final param = createParams({ + ${paramJsonMapBlock + .filter((it) => it.jsonKey && it.jsonValue) + .map((it) => `${it.nullCheckBlock}'${it.jsonKey}': ${it.jsonValue}`) + .join(",")} +}); +${isNeedAddBufferExtBlock ? "final List buffers = [];" : ""} +${paramJsonMapBlock.map((it) => it.addBufferExtBlock).join("\n")} +final callApiResult = await irisMethodChannel.invokeMethod(IrisMethodCall(apiType, jsonEncode(param), buffers:${buffersValueInJsonMap})); +if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); +} +final rm = callApiResult.data; +final result = rm['result']; +${returnBlock} + `, + { eliminateEmptyLine: true } + ); + + if (isNoImplFunc) { + funcBlock = funcBlock + .split("\n") + .map((it) => `// ${it}`) + .join("\n"); + funcBlock = _trim(` +// Implementation template +${funcBlock} + throw UnimplementedError('Unimplement for ${dartName(method)}'); + `); + } + + return _trim( + ` +${funcSignature} { + ${funcBlock} +} +` + ); +} + +/// Generate the file: lib/src/binding/call_api_impl_params_json.dart +function callApiImplParamsJsonFile( + parseResult: ParseResult, + cxxFiles: CXXFile[], + outputDir: string +): RenderResult { + let nodes = cxxFiles.flatMap((cxxFile) => { + return cxxFile.nodes + .filter((it) => it.__TYPE == CXXTYPE.Clazz) + .filter((it) => !isCallbackClass(it.asClazz().name)); + }); + + let jsonClassContents = nodes + .map((node) => { + let clazz = node.asClazz(); + let className = dartName(clazz); + let methods = clazz.methods; + + return methods + .map((method) => { + let methodName = dartName(method); + let jsonClassName = `${className}${toDartStyleNaming( + methodName, + true + )}Json`; + let output = ""; + let outVariable = getOutVariable(method); + if (outVariable) { + output = renderJsonSerializable( + parseResult, + jsonClassName, + [variableToMemberVariable(outVariable)], + { + forceExplicitNullableType: false, + forceNamingConstructor: false, + } + ); + } + + return output; + }) + .filter((it) => it.length > 0) + .join("\n\n"); + }) + .join("\n\n"); + + return { + file_name: path.join(outputDir, "call_api_impl_params_json.dart"), + file_content: _trim(` +${defaultDartHeader} + +${defaultIgnoreForFile} + +import 'binding_forward_export.dart'; +part 'call_api_impl_params_json.g.dart'; + +${jsonClassContents}`), + }; +} diff --git a/scripts/terra/renderers/event_handler_impl_params_json_renderer.ts b/scripts/terra/renderers/event_handler_impl_params_json_renderer.ts new file mode 100644 index 0000000..42e824a --- /dev/null +++ b/scripts/terra/renderers/event_handler_impl_params_json_renderer.ts @@ -0,0 +1,81 @@ +import { + CXXFile, + CXXTYPE, + MemberFunction, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { + defaultDartHeader, + defaultIgnoreForFile, + isCallbackClass, + renderJsonSerializable, + variableToMemberVariable, +} from "./utils"; +import { dartName } from "../parsers/dart_syntax_parser"; +import { renderBufferExtBlock } from "./buffer_ext_renderer"; + +import path from "path"; +import { ApiInterfaceRendererArgs } from "./api_interface_renderer"; + +/// Generate the files: +/// - lib/src/binding/event_handler_param_json.dart +export default function EventHandlerImplParamsJsonRenderer( + terraContext: TerraContext, + args: ApiInterfaceRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let outputDir = args.outputDir ?? ""; + let cxxFiles = parseResult!.nodes as CXXFile[]; + + let subContents = cxxFiles + .flatMap((cxxFile: CXXFile) => cxxFile.nodes) + .filter((it) => it.__TYPE == CXXTYPE.Clazz) + .filter((it) => isCallbackClass(it.asClazz().name)) + .flatMap((it) => it.asClazz()!.methods) + .map((node) => { + let memberFunc = node as MemberFunction; + let dartFuncName = dartName(memberFunc); + dartFuncName = dartFuncName[0].toUpperCase() + dartFuncName.slice(1); + let jsonClassName = + (memberFunc.parent ? dartName(memberFunc.parent!) : "") + + dartFuncName + + "Json"; + + return ` + ${renderJsonSerializable( + parseResult, + jsonClassName, + memberFunc.parameters.map((it) => variableToMemberVariable(it)) + )} + + ${renderBufferExtBlock( + parseResult, + jsonClassName, + memberFunc.parameters.map((it) => variableToMemberVariable(it)) + )} + `.trim(); + }) + .join("\n\n"); + + let content = ` +${defaultDartHeader} + +${defaultIgnoreForFile}, prefer_is_empty + +import 'package:agora_rtm/src/binding_forward_export.dart'; +part 'event_handler_param_json.g.dart'; + +${subContents} +`; + + return [ + { + file_name: path.join(outputDir, "event_handler_param_json.dart") , + file_content: content, + }, + ]; +} diff --git a/scripts/terra/renderers/event_handlers_impl_renderer.ts b/scripts/terra/renderers/event_handlers_impl_renderer.ts new file mode 100644 index 0000000..e67d031 --- /dev/null +++ b/scripts/terra/renderers/event_handlers_impl_renderer.ts @@ -0,0 +1,249 @@ +import { + CXXFile, + CXXTYPE, + CXXTerraNode, + Clazz, + SimpleTypeKind, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { isCallbackClass } from "./utils"; +import { dartFileName, dartName } from "../parsers/dart_syntax_parser"; +import { getIrisApiIdValue } from "@agoraio-extensions/terra_shared_configs"; +import { ApiInterfaceRendererArgs } from "./api_interface_renderer"; + +import path from "path"; + +function processCXXFiles( + terraContext: TerraContext, + parseResult: ParseResult, + args: any +): CXXFile[] { + return (parseResult.nodes as CXXFile[]).filter((cxxFile) => { + return cxxFile.nodes.find((node) => { + return node.__TYPE == CXXTYPE.Clazz && isCallbackClass((node as Clazz).name); + }); + }); +} + +function fileterNodes(cxxFile: CXXFile): CXXTerraNode[] { + return cxxFile.nodes + .filter((it) => it.__TYPE == CXXTYPE.Clazz) + .filter((it) => isCallbackClass(it.asClazz().name)); +} + +const dartHeader = ` +/// GENERATED BY terra, DO NOT MODIFY BY HAND.\n\n// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import + +import 'binding_forward_export.dart'; +// import 'package:agora_rtc_engine/src/binding/impl_forward_export.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; +`.trim(); + +/// TODO(littlegnal): Move to a parser. +const filteredBaseClasses = ["RefCountInterface", "agora::base::IEngineBase"]; +function getBaseClasses(parseResult: ParseResult, clazz: Clazz): Clazz[] { + let output: Clazz[] = []; + clazz.base_clazzs.forEach((it) => { + if (!filteredBaseClasses.includes(it)) { + let found = parseResult.resolveNodeByName(it); + if (found) { + output.push(found! as Clazz); + } + } + }); + + return output; +} + +function genCallbackExtendBlock(parseResult: ParseResult, clazz: Clazz) { + let extendBlock = ""; + let wrapperClassName = getBaseClasses(parseResult, clazz).map( + (it) => `${dartName(it)}Wrapper` + ); + if (wrapperClassName.length === 0) { + extendBlock = `implements EventLoopEventHandler`; + } else { + // Only take first one. + extendBlock = `extends ${wrapperClassName[0]}`; + } + + return extendBlock; +} + +function callbackSwithCaseBlock( + parseResult: ParseResult, + clazz: Clazz, + firstParamNameForWrapperClass: string +): string { + return clazz.methods + .map((it) => { + let className = clazz.name; + let methodName = it.name; + let jsonClassName = `${className.replace("I", "")}${ + methodName[0].toUpperCase() + methodName.slice(1) + }Json`; + let dn = dartName(it); + let eventName = getIrisApiIdValue(it).split("_").slice(1).join("_"); + // Fall back to method name if `getIrisApiIdValue` is empty. This is happen for that nodes from custom headers. + if (eventName === "") { + eventName = it.name; + } + + return ` +case '${eventName}': +if (${firstParamNameForWrapperClass}.${dn} == null) { + return true; +} +final jsonMap = jsonDecode(eventData); +${jsonClassName} paramJson = ${jsonClassName}.fromJson(jsonMap); +paramJson = paramJson.fillBuffers(buffers); +${(function () { + let paramIntList = it.parameters + .map((it) => { + let typeName = dartName(it.type); + let memberName = dartName(it); + return `${typeName}? ${memberName} = paramJson.${memberName};`; + }) + .join("\n"); + + let paramNullCheckList = it.parameters + .map((it) => { + let memberName = dartName(it); + return `${memberName} == null`; + }) + .join("||"); + if (paramNullCheckList.length) { + paramNullCheckList = `if (${paramNullCheckList}) { return true; }`; + } + + let paramFillBufferList = it.parameters + .map((it) => { + let memberName = dartName(it); + let actualNode = parseResult.resolveNodeByType(it.type); + if (actualNode.__TYPE == CXXTYPE.TypeAlias) { + actualNode = parseResult.resolveNodeByType( + actualNode.asTypeAlias()!.underlyingType + ); + } + if (actualNode.__TYPE == CXXTYPE.Struct) { + if (it.type.kind == SimpleTypeKind.array_t) { + return `${memberName} = ${memberName}.map((e) => e.fillBuffers(buffers)).toList();`; + } else { + return `${memberName} = ${memberName}.fillBuffers(buffers);`; + } + } + + return ""; + }) + .join("\n") + .trim(); + + let paramList = it.parameters.map((it) => dartName(it)); + + return ` + ${paramIntList} + ${paramNullCheckList} + ${paramFillBufferList} + ${firstParamNameForWrapperClass}.${methodName}!(${paramList}); + return true; + `.trim(); +})()} +`; + }) + .join("\n"); +} + +export default function EventHandlersImplRenderer( + terraContext: TerraContext, + args: ApiInterfaceRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let outputDir = args.outputDir ?? ""; + let cxxFiles = processCXXFiles(terraContext, parseResult, args); + + return cxxFiles.map((cxxFile) => { + let output = fileterNodes(cxxFile) + .map((it) => it.asClazz()) + .map((it) => { + let clazz = it; + let className = dartName(clazz); + let extendBlock = genCallbackExtendBlock(parseResult, clazz); + let wrapperClassName = `${className}Wrapper`; + let hasBaseClass = clazz.base_clazzs.length > 0; + let firstParamNameForWrapperClass = + className[0].toLowerCase() + className.slice(1); + + return ` +class ${wrapperClassName} ${extendBlock} { +${(function () { + // Constructor + let callSuperBlock = hasBaseClass + ? `: super(${firstParamNameForWrapperClass})` + : ""; + + return ` + const ${wrapperClassName}(this.${firstParamNameForWrapperClass})${callSuperBlock}; + `.trim(); +})()} + +final ${className} ${firstParamNameForWrapperClass}; + +@override +bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is ${wrapperClassName} && + other.${firstParamNameForWrapperClass} == ${firstParamNameForWrapperClass}; +} +@override +int get hashCode => ${firstParamNameForWrapperClass}.hashCode; + +@override +bool handleEventInternal(String eventName, String eventData, List buffers) { + switch (eventName) { + ${callbackSwithCaseBlock( + parseResult, + clazz, + firstParamNameForWrapperClass + )} + } + return false; +} + +@override +bool handleEvent(String eventName, String eventData, List buffers) { + if (!eventName.startsWith('${className}')) return false; + final newEvent = eventName.replaceFirst('${className}_', ''); + if (handleEventInternal(newEvent, eventData, buffers)) { return true; } + ${(function () { + // handle return block + if (hasBaseClass) { + return "return super.handleEventInternal(newEvent, eventData, buffers);"; + } + + return "return false;"; + })()} +} +} +`.trim(); + }) + .join("\n"); + + let content = ` + ${dartHeader} + + ${output} + `.trim(); + + let fileName = `${dartFileName(cxxFile)}_event_impl.dart`; + return { + file_name: path.join(outputDir, fileName), + file_content: content, + }; + }); +} diff --git a/scripts/terra/renderers/event_ids_mapping_renderer.ts b/scripts/terra/renderers/event_ids_mapping_renderer.ts new file mode 100644 index 0000000..7f8453b --- /dev/null +++ b/scripts/terra/renderers/event_ids_mapping_renderer.ts @@ -0,0 +1,82 @@ +import { + CXXFile, + CXXTYPE, + CXXTerraNode, + Clazz, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { irisApiId } from "@agoraio-extensions/terra_shared_configs"; +import { isCallbackClass } from "./utils"; + +const funcNeedCheckWithBaseClasses = [ + "agora::media::IAudioFrameObserver", + "agora::rtc::IRtcEngineEventHandlerEx", +]; +function isNeedCheckWithBaseClasses(clazz: Clazz): boolean { + return funcNeedCheckWithBaseClasses.includes(clazz.fullName); +} + +export default function EventIdsMappingRenderer( + terraContext: TerraContext, + args: any, + parseResult: ParseResult +): RenderResult[] { + let cxxFiles = parseResult!.nodes as CXXFile[]; + let eventIdsMapping: Map = new Map(); + + cxxFiles.forEach((cxxFile: CXXFile) => { + cxxFile.nodes.forEach((node) => { + if (node.__TYPE == CXXTYPE.Clazz) { + let clazz = node as Clazz; + if (isCallbackClass(clazz.name)) { + clazz.methods.forEach((method) => { + let key = `${clazz.name.replace("I", "")}_${method.name}`; + let value = getIrisApiIdValue(method); + if (value.length > 0) { + if (!eventIdsMapping.has(key)) { + eventIdsMapping.set(key, []); + } + eventIdsMapping.get(key)?.push(value); + } + }); + } + } + }); + }); + + let eventIdsMappingContents: string[] = []; + + eventIdsMapping.forEach((value, key) => { + eventIdsMappingContents.push( + `"${key}": [${value.map((it) => `"${it}"`).join(", ")}]` + ); + }); + + let output = ` + /// Event Ids mapping of iris api id. +const eventIdsMapping = { + ${eventIdsMappingContents.join(",\n")} +}; +`.trim(); + + return [ + { + // test_shard/integration_test_app/integration_test/generated/bindings + file_name: + "test_shard/integration_test_app/integration_test/generated/bindings/event_ids_mapping_gen.dart", + file_content: output, + }, + ]; +} + +export function getIrisApiIdKey(node: CXXTerraNode): string { + return node.user_data?.["IrisApiIdParser"]?.key ?? ""; +} + +export function getIrisApiIdValue(node: CXXTerraNode): string { + return node.user_data?.["IrisApiIdParser"]?.value ?? ""; +} diff --git a/scripts/terra/renderers/export_api_renderer.ts b/scripts/terra/renderers/export_api_renderer.ts new file mode 100644 index 0000000..af6f6e0 --- /dev/null +++ b/scripts/terra/renderers/export_api_renderer.ts @@ -0,0 +1,1135 @@ +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { + ApiInterfaceRendererArgs, + defaultParameterListBlockRenderer, + defaultReturnTypeRenderer, + functionSignature, + renderClass, +} from "./api_interface_renderer"; +import { + CXXFile, + CXXTYPE, + CXXTerraNode, + Clazz, + Enumz, + MemberFunction, + MemberVariable, + SimpleType, + Struct, + Variable, +} from "@agoraio-extensions/cxx-parser"; +import { + _trim, + getBaseClasses, + isCallbackClass, + isNullableValue, + isNullableVariable, + renderEnumJsonSerializable, + renderJsonSerializable, + renderTopLevelVariable, + getPointerArrayLengthNameMappings, + filterPointerArrayLengthNames, + isNullableType, + toParameterDeclaration, +} from "./utils"; +import { + dartFileName, + dartName, + enumConstantNaming, + toDartMemberName, + toDartStyleNaming, +} from "../parsers/dart_syntax_parser"; + +import path from "path"; +import _ from "lodash"; +import { getDartTypeRemapping } from "../parsers/dart_type_remapping_parser"; +import { + getIrisApiIdValue, + getOverrideNodeParserUserData, + getPointerArrayNameMapping, + isPointerName, +} from "@agoraio-extensions/terra_shared_configs"; +import { + getDartProjectMarkerParserUserData, + isHiddenNodesAfterFlatten, + isNotANamedParametersAfterFlattenOfMethod, +} from "../parsers/dart_project_marker_parser"; + +export type ExportRtmApiInterfaceRendererArgs = ApiInterfaceRendererArgs & { + configPath?: string; +}; + +export default function ExportRtmApiInterfaceRenderer( + terraContext: TerraContext, + args: ExportRtmApiInterfaceRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let renderResults = (parseResult!.nodes as CXXFile[]).map((cxxFile) => { + let subContents = cxxFile.nodes + .filter((it) => !isNativeBindingsNode(it)) + .filter((it) => it.name.length > 0) + .map((node) => { + switch (node.__TYPE) { + case CXXTYPE.Clazz: { + let clazz = node as Clazz; + if (isCallbackClass(clazz.name)) { + // return renderResultClasses(clazz); + return ""; + } else { + return renderExportRtmClass(parseResult, clazz); + } + } + case CXXTYPE.Struct: { + return renderJsonSerializable( + parseResult, + dartName(node.asStruct()), + memeberVariablesWithoutCallbackClass( + node.asStruct()!.member_variables + ), + !_isFlattenParameter(node) + ? { + parentNode: node, + } + : undefined + ); + } + case CXXTYPE.Enumz: { + return renderEnumJsonSerializable(node.asEnumz()); + } + case CXXTYPE.Variable: { + return renderTopLevelVariable(node.asVariable()); + } + default: { + return ""; + } + } + }) + .join("\n\n"); + + let isNeedImportGDartFile = + cxxFile.nodes.find((node) => { + return node.isStruct() || node.isEnumz(); + }) != undefined; + + let content = _trim(` + import 'binding_forward_export.dart'; + ${ + isNeedImportGDartFile + ? `part '${dartFileName(cxxFile)}.g.dart';` + : "" + } + + ${subContents} + `); + + return { + file_name: path.join( + args.outputDir ?? "", + `${dartFileName(cxxFile)}.dart` + ), + file_content: content, + }; + }); + + return [ + ...renderResults, + ...implRendererResults(parseResult, args.outputDir ?? ""), + renderRtmResultHandler(parseResult, args.outputDir ?? ""), + ]; +} + +function memeberVariablesWithoutCallbackClass( + memberVariables: MemberVariable[] +): MemberVariable[] { + return memberVariables.filter((it) => { + return !isCallbackClass(it.type.name); + }); +} + +function functionsToResultClassMapping( + parseResult: ParseResult +): Map { + let result = new Map(); + + let callbackClassMethods = (parseResult!.nodes as CXXFile[]) + .flatMap((it) => it.nodes) + .filter((e) => isCallbackClass(e.name)) + .flatMap((it) => it.asClazz().methods); + + (parseResult!.nodes as CXXFile[]) + .flatMap((it) => it.nodes) + .filter((e) => e.isClazz() && !isCallbackClass(e.name)) + .flatMap((it) => it.asClazz().methods) + .forEach((method) => { + if (!hasRequestIdParam(method)) { + return ""; + } + + let matchedResultCallback = callbackClassMethods.find( + (callbackMethod) => { + let toMatchName = method.name[0].toUpperCase() + method.name.slice(1); + return callbackMethod.name.includes(`${toMatchName}Result`); + } + ); + + if (!matchedResultCallback) { + matchedResultCallback = callbackClassMethods.find((callbackMethod) => { + let toMatchName = method.name[0].toUpperCase() + method.name.slice(1); + return callbackMethod.name.includes(toMatchName); + }); + } + + if (matchedResultCallback) { + let resultClassName = toDartStyleNaming(`${method.name}Result`, true); + + result.set(matchedResultCallback!.fullName, resultClassName); + result.set(method!.fullName, resultClassName); + } + }); + + return result; +} + +let createdResultClass = new Set(); +function renderResultClasses(parseResult: ParseResult, clazz: Clazz): string { + let callbackClassMethods = (parseResult!.nodes as CXXFile[]) + .flatMap((it) => it.nodes) + .filter((e) => isCallbackClass(e.name)) + .flatMap((it) => it.asClazz().methods); + + return clazz.methods + .map((method) => { + if (!hasRequestIdParam(method)) { + return ""; + } + + let matchedResultCallback = callbackClassMethods.find( + (callbackMethod) => { + let toMatchName = method.name[0].toUpperCase() + method.name.slice(1); + return callbackMethod.name.includes(`${toMatchName}Result`); + } + ); + + if (!matchedResultCallback) { + matchedResultCallback = callbackClassMethods.find((callbackMethod) => { + let toMatchName = method.name[0].toUpperCase() + method.name.slice(1); + return callbackMethod.name.includes(toMatchName); + }); + } + + if (!matchedResultCallback) { + return ""; + } + + let resultClassName = toDartStyleNaming(`${method.name}Result`, true); + + if (createdResultClass.has(resultClassName)) { + return ""; + } + createdResultClass.add(resultClassName); + + let constructorNamedParametersBlock = matchedResultCallback.parameters + .filter((it) => !isRequestIdParamName(it.name)) + .filter((it) => !isErrorCodeParamName(it.name)) + // .filter( + // (it) => + // !getPointerArrayNameByLengthName(it.name, matchedResultCallback!) + // ) + .map((parameter) => { + // TODO(littlegnal): Remove `requestId` + let type = dartName(parameter.type); + let name = parameter.name; + + return `required this.${name}`; + }) + .join(","); + + let memberVariablesBlock = matchedResultCallback.parameters + .filter((it) => !isRequestIdParamName(it.name)) + .filter((it) => !isErrorCodeParamName(it.name)) + // .filter( + // (it) => + // !getPointerArrayNameByLengthName(it.name, matchedResultCallback!) + // ) + .map((parameter) => { + // TODO(littlegnal): Remove `requestId` + let type = + getDartProjectMarkerParserUserData(parameter)?.displayNameConfig + ?.displayType ?? dartName(parameter.type); + let name = parameter.name; + + return `final ${type} ${name};`; + }) + .join("\n\n"); + + if (constructorNamedParametersBlock.length > 0) { + constructorNamedParametersBlock = `{${constructorNamedParametersBlock}}`; + } + + return ` + class ${resultClassName} { + ${resultClassName}(${constructorNamedParametersBlock}); + + ${memberVariablesBlock} + } + `; + }) + .join("\n\n"); +} + +function isNativeBindingsNode(node: CXXTerraNode): boolean { + let optionsNodeRegx = new RegExp("(.*)Options$"); + // return optionsNodeRegx.test(node.name); + return false; +} + +function hasRequestIdParam(memberFunction: MemberFunction): boolean { + return ( + memberFunction.parameters.find((it) => isRequestIdParamName(it.name)) != + undefined + ); +} + +function hasErrorCodeParam(memberFunction: MemberFunction): boolean { + return ( + memberFunction.parameters.find((it) => isErrorCodeParamName(it.name)) != + undefined + ); +} + +function isErrorCodeParamName(name: string): boolean { + return name === "errorCode"; +} + +function isRequestIdParamName(name: string): boolean { + return name === "requestId"; +} + +function isUserIdParamName(name: string): boolean { + return name === "userId"; +} + +function _returnTypeRendererFunc( + clazz: Clazz +): (_: MemberFunction, __: boolean) => string { + return function ( + memberFunction: MemberFunction, + isSynchronizedFunc: boolean + ): string { + return _returnTypeRenderer(clazz, memberFunction, isSynchronizedFunc); + }; +} + +function intToRtmStatusReturnTypeRenderer( + memberFunction: MemberFunction, + isSynchronizedFunc: boolean +): string { + let returnType = dartName(memberFunction.return_type); + if (returnType == "int") { + returnType = "RtmStatus"; + } + returnType = `${returnType}${ + isNullableType(memberFunction.return_type) ? "?" : "" + }`; + if (!isSynchronizedFunc) { + if (returnType != "RtmStatus") { + returnType = `(RtmStatus, ${returnType}?)`; + } + + returnType = `Future<${returnType}>`; + } + return returnType; +} + +function _getResultClassName(clazz: Clazz, memberFunction: MemberFunction) { + let resultClassName = `${memberFunction.name}Result`; + let overrideNodeParserUserData = + getOverrideNodeParserUserData(memberFunction); + if ( + overrideNodeParserUserData && + overrideNodeParserUserData.redirectIrisApiId + ) { + let existedFunction = clazz.methods.find((it) => { + return ( + getIrisApiIdValue(it) == overrideNodeParserUserData!.redirectIrisApiId + ); + }); + // Keep using the redirected function's name + if (existedFunction) { + resultClassName = `${existedFunction.name}Result`; + } + } + + return toDartStyleNaming(resultClassName, true); +} + +function _returnTypeRenderer( + clazz: Clazz, + memberFunction: MemberFunction, + isSynchronizedFunc: boolean +): string { + let hasRequestIdParam = + memberFunction.parameters.find((it) => it.name === "requestId") != + undefined; + if (!hasRequestIdParam) { + return intToRtmStatusReturnTypeRenderer(memberFunction, isSynchronizedFunc); + } + + let resultClassName = _getResultClassName(clazz, memberFunction); + + let dartRecordType = `(RtmStatus, ${resultClassName}?)`; + + let returnType = `Future<${dartRecordType}>`; + return returnType; +} + +function _isFlattenParameter(param: CXXTerraNode): boolean { + return ( + getDartProjectMarkerParserUserData(param)?.flattenParamNodeConfig != + undefined + ); +} + +function _parameterListBlockRenderer( + parseResult: ParseResult, + mf: MemberFunction +): string { + let memberFunction = _.cloneDeep(mf); + memberFunction.parameters = memberFunction.parameters + .filter((it) => filterPointerArrayLengthNames(memberFunction, it)) + .filter((e) => e.name != "requestId"); + + let defaultValue = ""; + + let mustParameters: (Variable | MemberVariable)[] = []; + let namedParameters: (Variable | MemberVariable)[] = []; + memberFunction.parameters.forEach((param) => { + let flattenNodeConfig = + getDartProjectMarkerParserUserData(param)?.flattenParamNodeConfig; + let defaultValueBuilder = + getDartProjectMarkerParserUserData(param)?.displayNameConfig?.converter + ?.defaultValueBuilder; + + if (flattenNodeConfig) { + let actualNode = parseResult.resolveNodeByType(param.type); + let namedParametersInStruct = actualNode + .asStruct() + .member_variables.filter((it) => + filterPointerArrayLengthNames(actualNode.asStruct(), it) + ) + .filter((it) => { + return !isHiddenNodesAfterFlatten(param, it); + }) + .forEach((it) => { + if (isNotANamedParametersAfterFlattenOfMethod(memberFunction, it)) { + mustParameters.push(it); + } else { + namedParameters.push(it); + } + }); + // .filter((it) => { + // return !isNotANamedParametersAfterFlatten(param, it); + // }); + // namedParameters.push(...namedParametersInStruct); + } else if (param.default_value.length != 0 || defaultValueBuilder) { + namedParameters.push(param); + } else { + mustParameters.push(param); + } + }); + + let namedParametersBlock = renderNamedParametersBlock( + parseResult, + namedParameters + ); + + let parameterList = mustParameters // memberFunction.parameters + .map((param) => { + let typeNode = parseResult.resolveNodeByType(param.type); + let flattenNodeConfig = + getDartProjectMarkerParserUserData(param)?.flattenParamNodeConfig; + let defaultValueBuilder = + getDartProjectMarkerParserUserData(param)?.displayNameConfig?.converter + ?.defaultValueBuilder; + // if ( + // flattenNodeConfig || + // param.default_value.length != 0 || + // defaultValueBuilder + // ) { + // return ""; + // } + + let typeName = + getDartProjectMarkerParserUserData(param)?.displayNameConfig + ?.displayType ?? dartName(param.type); + let variableName = + getDartProjectMarkerParserUserData(param)?.displayNameConfig + ?.displayName ?? dartName(param); + + // Has default value + if (param.isVariable() && param.asVariable().default_value) { + // isNeedRequiredKeyword = false; + + if (typeNode.__TYPE == CXXTYPE.Enumz) { + defaultValue = `${typeName}.${toDartMemberName( + param.asVariable().default_value + )}`; + } else if (typeNode.__TYPE == CXXTYPE.Struct) { + defaultValue = `const ${dartName(typeNode)}()`; + } else { + defaultValue = param.asVariable().default_value; + } + + if (isNullableVariable(param.asVariable())) { + typeName = `${typeName}?`; + defaultValue = ""; + } + } + + return `${typeName} ${variableName}`; + }) + .filter((it) => it.length > 0); + if (namedParametersBlock.length > 0) { + parameterList.push(namedParametersBlock); + } + + let parameterListBlock = parameterList.join(", "); + + return parameterListBlock; +} + +const tokenEventClass = ` +class TokenEvent { + const TokenEvent(this.channelName); + final String channelName; +} +`; + +function renderExportRtmClass(parseResult: ParseResult, clazz: Clazz): string { + let extraClassBody = ""; + let extracClasses = ""; + if (clazz.fullName == "agora::rtm::IRtmClient") { + extraClassBody = renderAddListenerFunction(parseResult, false); + extracClasses = tokenEventClass; + } + + return ` + ${renderResultClasses(parseResult, clazz)} + + ${extracClasses} + + ${renderClass(parseResult, clazz, { + extraClassBody: extraClassBody, + returnTypeRenderer: _returnTypeRendererFunc(clazz), + parameterListBlockRenderer: _parameterListBlockRenderer, + methodFilter: (method) => { + return getDartProjectMarkerParserUserData(method)?.isHiddenNode != true; + }, + })} + `; +} + +function renderExportRtmClassImpl( + parseResult: ParseResult, + clazz: Clazz +): string { + let clazzName = dartName(clazz); + let methods = clazz.methods; + let nativeBindingVariableName = `nativeBinding${clazzName}Impl`; + let methodImpls = renderMethodImpls( + parseResult, + clazz, + nativeBindingVariableName + ); + let baseClassImplNames = getBaseClasses(parseResult, clazz).map( + (baseClass) => `${dartName(baseClass)}Impl` + ); + let shouldOverrideBaseClass = baseClassImplNames.length > 0; + let classExtendsBlock = shouldOverrideBaseClass + ? `extends ${baseClassImplNames.join(", ")} ` + : ""; + let constructorBlock = `${clazzName}Impl(this.${nativeBindingVariableName}, this.rtmResultHandler);`; + + let extraClassBody = ""; + if (clazz.fullName == "agora::rtm::IRtmClient") { + extraClassBody = renderAddListenerFunction(parseResult, true); + } + + return ` + class ${clazzName}Impl ${classExtendsBlock} implements ${clazzName} { + ${constructorBlock} + + final RtmResultHandler rtmResultHandler; + + final native_binding.${clazzName}Impl ${nativeBindingVariableName}; + + ${extraClassBody} + ${methodImpls} + } + `.trim(); +} + +function getPointerArrayNameByLengthName( + lengthName: string, + method: MemberFunction | Struct +): string | undefined { + let mappings = getPointerArrayLengthNameMappings(method); + if (!mappings) { + return undefined; + } + for (let [key, value] of mappings) { + if (value == lengthName) { + return key; + } + } + return undefined; +} + +function _getParamDisplayName( + param: CXXTerraNode, + paramTypeNode?: CXXTerraNode +) { + let paramName = dartName(param); + let displayParamName = paramName; + let displayNameConfig = + getDartProjectMarkerParserUserData(param)?.displayNameConfig; + if (displayNameConfig) { + if (displayNameConfig?.converter?.to && paramTypeNode) { + displayParamName = `${paramName}${paramTypeNode.name}`; + } else if (displayNameConfig.displayName) { + displayParamName = displayNameConfig.displayName; + } + } + return displayParamName; +} + +function renderMethodImpls( + parseResult: ParseResult, + clazz: Clazz, + nativeBindingVariableName: string +): string { + let methodImpls = clazz.methods + .filter((method) => { + return getDartProjectMarkerParserUserData(method)?.isHiddenNode != true; + }) + .map((method) => { + let funcName = dartName(method); + let funcSignature = functionSignature(parseResult, method, { + forceAddOverridePrefix: true, + returnTypeRenderer: _returnTypeRendererFunc(clazz), + parameterListBlockRenderer: _parameterListBlockRenderer, + }); + + let returnTypeNode = parseResult.resolveNodeByType(method.return_type); + if (returnTypeNode.isClazz()) { + return _trim(` +${funcSignature} { + // This function's implementation can't be generated automatically, please implement it manually. + throw UnimplementedError(); +} +`); + } + + let isReturnInt = dartName(method.return_type) == "int"; + + let apiCallParamList: string[] = []; + let optionsParamInitList: string[] = []; + let parameters = method.parameters.filter((it) => it.name != "requestId"); + let hasRequestId = hasRequestIdParam(method); + // requestId + let isNeedNamedApiCall = parameters.length > 1; + parameters.forEach((param) => { + let paramName = dartName(param); + let paramTypeNode = parseResult.resolveNodeByType(param.type); + + let displayNameConfig = + getDartProjectMarkerParserUserData(param)?.displayNameConfig; + if (displayNameConfig) { + let displayParamName = _getParamDisplayName(param, paramTypeNode); + if (displayNameConfig?.converter?.to) { + let converterTo = displayNameConfig.converter.to; + let converterToBlock = converterTo(parseResult, displayNameConfig!); + let mappingInitBlock = `final ${displayParamName} = ${converterToBlock};`; + optionsParamInitList.push(mappingInitBlock); + } + + apiCallParamList.push(`${paramName}: ${displayParamName}`); + } else if (!isNeedNamedApiCall) { + apiCallParamList.push(paramName); + } else { + let pointerArrayName = getPointerArrayNameByLengthName( + param.name, + method + ); + let displayName = paramName; + if (pointerArrayName) { + displayName = pointerArrayName; + + let pointerArrayParam = parameters.find((it) => { + return it.name == pointerArrayName; + }); + if (pointerArrayParam) { + let displayNameConfig = + getDartProjectMarkerParserUserData( + pointerArrayParam + )?.displayNameConfig; + if (displayNameConfig) { + let paramTypeNode = parseResult.resolveNodeByType( + pointerArrayParam.type + ); + displayName = _getParamDisplayName( + pointerArrayParam, + paramTypeNode + ); + } + } + } + + if (pointerArrayName) { + apiCallParamList.push(`${paramName}: ${displayName}.length`); + } else { + apiCallParamList.push(`${paramName}: ${displayName}`); + } + } + + if (_isFlattenParameter(param)) { + let paramTypeNodeStruct = paramTypeNode.asStruct(); + let optionsParamInit = _trim(` + final ${paramName} = ${dartName(paramTypeNode)}( + ${paramTypeNodeStruct.member_variables + .filter((it) => { + return !isHiddenNodesAfterFlatten(param, it); + }) + .map((memberVariable) => { + let pointerArrayName = getPointerArrayNameByLengthName( + memberVariable.name, + paramTypeNodeStruct + ); + let displayName = _getParamDisplayName(memberVariable); + if (pointerArrayName) { + displayName = pointerArrayName; + + let pointerArrayParam = + paramTypeNodeStruct.member_variables.find((it) => { + return it.name == pointerArrayName; + }); + if (pointerArrayParam) { + let displayNameConfig = + getDartProjectMarkerParserUserData( + pointerArrayParam + )?.displayNameConfig; + if (displayNameConfig) { + let paramTypeNode = parseResult.resolveNodeByType( + pointerArrayParam.type + ); + displayName = _getParamDisplayName(pointerArrayParam); + } + } + } + + if (pointerArrayName) { + return `${dartName(memberVariable)}: ${displayName}.length`; + } + + return `${dartName(memberVariable)}: ${displayName}`; + })} + ); + `); + optionsParamInitList.push(optionsParamInit); + } + }); + + let optionsParamInitListBlock = optionsParamInitList.join("\n"); + let apiCallParamListBlock = apiCallParamList.join(", "); + + let resultClassName = _getResultClassName(clazz, method); + + let implWithResultClassReturnType = ` +try { + final requestId = await ${nativeBindingVariableName}.${funcName}(${apiCallParamListBlock}); + final (${resultClassName} result, RtmErrorCode errorCode) = await rtmResultHandler.request(requestId); + final status = await ${nativeBindingVariableName}.irisMethodChannel.wrapRtmStatus(errorCode.value(), '${funcName}'); + return (status, result); +} on AgoraRtmException catch (e) { + final status = await ${nativeBindingVariableName}.irisMethodChannel.wrapRtmStatus(e.code, '${funcName}'); + return (status, null); +} + `; + + return _trim(` + ${funcSignature} { + ${optionsParamInitListBlock} + ${(function () { + if (hasRequestId) { + return _trim(implWithResultClassReturnType); + } else if (isReturnInt) { + // int to RtmStatus + return _trim(` + try { + await ${nativeBindingVariableName}.${funcName}(${apiCallParamListBlock}); + final status = await ${nativeBindingVariableName}.irisMethodChannel + .wrapRtmStatus(0, '${funcName}'); + return status; + } on AgoraRtmException catch (e) { + final status = await ${nativeBindingVariableName}.irisMethodChannel + .wrapRtmStatus(e.code, '${funcName}'); + return status; + } + `); + } else { + return _trim(` + try { + final result = await ${nativeBindingVariableName}.${funcName}(${apiCallParamListBlock}); + final status = await ${nativeBindingVariableName}.irisMethodChannel + .wrapRtmStatus(0, '${funcName}'); + return (status, result); + } on AgoraRtmException catch (e) { + final status = await ${nativeBindingVariableName}.irisMethodChannel + .wrapRtmStatus(e.code, '${funcName}'); + return (status, null); + } + `); + } + })()} + + } + `); + }) + .join("\n\n"); + return methodImpls; +} + +function dartPrimitiveTypeDefaultValue(type: SimpleType): string { + let typeName = dartName(type); + switch (typeName) { + case "int": + return "0"; + case "double": + return "0.0"; + case "bool": + return "false"; + case "String": + return '""'; + default: + return ""; + } +} + +function renderNamedParametersBlock( + parseResult: ParseResult, + parameters: (Variable | MemberVariable)[] +): string { + if (parameters.length == 0) { + return ""; + } + let namedList = []; + for (let param of parameters) { + let displayNameConfig = + getDartProjectMarkerParserUserData(param)?.displayNameConfig; + if (param.isMemberVariable()) { + let memberVariableDefaultValues = new Map(); + if (param.parent) { + param.parent.asStruct().constructors.forEach((constructor) => { + constructor.initializerList.forEach((initializer) => { + if (initializer.kind == "Value") { + memberVariableDefaultValues.set( + initializer.name, + initializer.values[0] + ); + } + }); + }); + } + + let markedDefaultValue = ""; + let defaultValueBuilder = + getDartProjectMarkerParserUserData(param)?.displayNameConfig?.converter + ?.defaultValueBuilder; + if (defaultValueBuilder) { + let config = + getDartProjectMarkerParserUserData(param)!.displayNameConfig!; + markedDefaultValue = defaultValueBuilder!(parseResult, config, "")!; + } else if (dartName(param.asMemberVariable().type).includes("List")) { + markedDefaultValue = "const []"; + } + + let holder = toParameterDeclaration( + parseResult, + param.asMemberVariable(), + param.asMemberVariable().type, + markedDefaultValue.length > 0 // Use the default value from the config first + ? markedDefaultValue + : memberVariableDefaultValues.get(param.asMemberVariable().name) ?? "" + ); + + let declaration = `${holder.type} ${holder.name}`; + if (holder.defaultValue.length > 0) { + declaration = `${declaration} = ${holder.defaultValue}`; + } + + namedList.push(declaration); + } else if ( + param.isVariable() && + (param.asVariable().default_value.length > 0 || + displayNameConfig?.converter?.defaultValueBuilder) + ) { + let type = + getDartTypeRemapping(param)?.config?.dartType ?? dartName(param.type); + let name = _getParamDisplayName(param); + + let defaultValue = param.asVariable().default_value; + let defaultValueBuilder = + displayNameConfig?.converter?.defaultValueBuilder; + if (defaultValueBuilder) { + defaultValue = defaultValueBuilder( + parseResult, + displayNameConfig!, + defaultValue + ); + } + namedList.push(`${type} ${name} = ${defaultValue}`); + } + // else if (isUserIdParamName(param.name)) { + // let type = dartName(param.type); + // let name = dartName(param); + // namedList.push(`${type} ${name} = ""`); + // } + } + + return `{${namedList.join(",")}}`; +} + +function implRendererResults( + parseResult: ParseResult, + outputDir: string +): RenderResult[] { + let renderResults = (parseResult!.nodes as CXXFile[]) + .filter((it) => it.nodes.find((node) => node.isClazz()) != undefined) + .map((cxxFile) => { + let subContents = cxxFile.nodes + .filter((it) => !isNativeBindingsNode(it)) + .filter((it) => it.name.length > 0) + .map((node) => { + switch (node.__TYPE) { + case CXXTYPE.Clazz: { + let clazz = node as Clazz; + if (isCallbackClass(clazz.name)) { + // return renderResultClasses(clazz); + return ""; + } else { + return renderExportRtmClassImpl(parseResult, clazz); + } + } + + default: { + return ""; + } + } + }) + .join("\n\n"); + + let isNeedImportGDartFile = + cxxFile.nodes.find((node) => { + return node.isStruct() || node.isEnumz(); + }) != undefined; + + let fileName = `${dartFileName(cxxFile)}_impl`; + + let content = _trim(` + import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart'; + import 'package:agora_rtm/src/impl/extensions.dart'; + import 'package:agora_rtm/src/binding_forward_export.dart'; + import 'package:agora_rtm/src/bindings/gen/${fileName}.dart' as native_binding; + + ${subContents} + `); + + return { + file_name: path.join(outputDir, "impl", "gen", `${fileName}.dart`), //`lib/src/${dartFileName(cxxFile)}.dart`, + file_content: content, + }; + }); + + return renderResults; +} + +function renderRtmResultHandler( + parseResult: ParseResult, + outputDir: string +): RenderResult { + let rtmEventHandlerNode = parseResult + .resolveNodeByName("agora::rtm::IRtmEventHandler") + ?.asClazz()!; + + let funcBlocks = []; + let rtmEventHandlerFuncBlock = rtmEventHandlerNode.methods + .map((method) => { + let returnType = `${dartName(method.return_type)}`; + let funcName = `${dartName(method)}`; + let parameterListBlock = method.parameters + .map((param) => `${dartName(param.type)} ${dartName(param)}`) + .join(", "); + + let callbackImplBlock = ""; + + let resultClassMapping = functionsToResultClassMapping(parseResult); + let resultClassName = resultClassMapping.get(method.fullName); + let resultClassInitParamsList: string[] = []; + // final result = LoginResult(requestId: requestId, errorCode: errorCode); + if (resultClassName) { + let resultClassInitList = method.parameters + .filter((it) => !isRequestIdParamName(it.name)) + .filter((it) => !isErrorCodeParamName(it.name)) + .map((param) => { + let paramName = dartName(param); + let displayNameConfig = + getDartProjectMarkerParserUserData(param)?.displayNameConfig; + if (displayNameConfig) { + if (displayNameConfig?.converter?.from) { + let converterFrom = displayNameConfig.converter.from; + let converterFromBlock = converterFrom( + parseResult, + displayNameConfig! + ); + let mappingInitParamName = `${dartName(param)}${dartName( + param.type + )}`; + paramName = mappingInitParamName; + let mappingInitBlock = `final ${mappingInitParamName} = ${converterFromBlock};`; + resultClassInitParamsList.push(mappingInitBlock); + + // apiCallParamList.push(`${paramName}: ${mappingInitParamName}`); + } + } + + return `${dartName(param)}: ${paramName}`; + }) + .join(", "); + callbackImplBlock = _trim(` + final result = ${resultClassName}(${resultClassInitList}); + response(requestId, (result, errorCode)); + `); + if (resultClassInitParamsList.length > 0) { + callbackImplBlock = _trim(` + ${resultClassInitParamsList.join("\n")} + ${callbackImplBlock} + `); + } + } + + return _trim(` +@visibleForTesting +@protected +${returnType} ${funcName}(${parameterListBlock}) { + ${callbackImplBlock} +}`); + }) + .join("\n\n"); + + let rtmEventHandlerInitListBlock = rtmEventHandlerNode.methods + .map((method) => { + let funcName = `${dartName(method)}`; + return `${funcName}: ${funcName}`; + }) + .join(","); + + let constructorBlock = _trim(` `); + + let content = _trim(` +import 'dart:async'; + +import 'package:agora_rtm/src/binding_forward_export.dart'; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_client.dart'; + +abstract class RtmResultHandler { + RtmResultHandler() { + rtmEventHandler = RtmEventHandler( + ${rtmEventHandlerInitListBlock}, + ); + } + late final RtmEventHandler rtmEventHandler; + + Future request(int requestId); + + void response(int requestId, (Object result, RtmErrorCode errorCode) data); + + ${rtmEventHandlerFuncBlock} +} + `); + + return { + file_name: path.join(outputDir, "impl", "gen", "rtm_result_handler.dart"), + file_content: content, + }; +} + +const tokenEventListener = ` +void Function(TokenEvent event)? token +`; + +function renderAddListenerFunction( + parseResult: ParseResult, + isImpl: boolean +): string { + let eventHandlerFuncs = (parseResult!.nodes as CXXFile[]) + .flatMap((it) => it.nodes) + .filter((e) => isCallbackClass(e.name)) + .flatMap((it) => it.asClazz().methods) + .filter((it) => !hasRequestIdParam(it)) + .filter( + (it) => getDartProjectMarkerParserUserData(it)?.isHiddenNode != true + ); + let addListenerParmetersBlock = eventHandlerFuncs.map((method) => { + let parameterList = method.parameters + .map((param) => `${dartName(param.type)} ${dartName(param)}`) + .join(", "); + let functionParameterName = dartName(method); + if ( + functionParameterName.includes("on") && + functionParameterName.includes("Event") + ) { + functionParameterName = functionParameterName + .replace("on", "") + .replace("Event", ""); + } else { + // Take the first word of the name, e.g., tokenPrivilegeWillExpire -> token + let words = functionParameterName.match(/[A-Z][a-z]+/g); + if (words) { + functionParameterName = words[0]; + } + } + + return `void Function(${parameterList})? ${toDartStyleNaming( + functionParameterName + )}`; + }); + addListenerParmetersBlock.push(tokenEventListener); + + let addListenerFunctionSignature = `void addListener({${addListenerParmetersBlock}})`; + + let removeListenerFunctionSignature = `void removeListener({${addListenerParmetersBlock}})`; + + if (!isImpl) { + return _trim(` + ${addListenerFunctionSignature}; + + ${removeListenerFunctionSignature}; + `); + } + + return _trim(` + @override + ${addListenerFunctionSignature} { + throw UnimplementedError('Implement this function in sub-class.'); + } + + @override + ${removeListenerFunctionSignature} { + throw UnimplementedError('Implement this function in sub-class.'); + } + `); +} diff --git a/scripts/terra/renderers/forward_export_renderer.ts b/scripts/terra/renderers/forward_export_renderer.ts new file mode 100644 index 0000000..91a6943 --- /dev/null +++ b/scripts/terra/renderers/forward_export_renderer.ts @@ -0,0 +1,124 @@ +import { CXXFile, CXXTYPE, Clazz } from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { isCallbackClass } from "./utils"; +import { dartFileName, dartName } from "../parsers/dart_syntax_parser"; +import { ApiInterfaceRendererArgs } from "./api_interface_renderer"; +import path from "path"; + +export type ForwardExportRendererArgs = ApiInterfaceRendererArgs & { + extraBindingExportFiles?: string[]; +}; + +/// Generate the files: +/// - lib/src/binding_forward_export.dart +/// - lib/src/binding/impl_forward_export.dart +export default function ForwardExportRenderer( + terraContext: TerraContext, + args: ForwardExportRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let extraBindingExportFiles = (args?.extraBindingExportFiles ?? []).map( + (e) => { + return `export '${e}';`; + } + ); + let outputDir = args.outputDir ?? ""; + let cxxFiles = parseResult!.nodes as CXXFile[]; + let bindingExportFiles: string[] = []; + let needHideClasses: string[] = []; + let exportApiForwardExportFiles: string[] = []; + let implExportFiles: string[] = []; + cxxFiles.forEach((cxxFile: CXXFile) => { + exportApiForwardExportFiles.push(`export '${dartFileName(cxxFile)}.dart';`); + + let hasImplClass = cxxFile.nodes.find((node) => { + return ( + node.__TYPE == CXXTYPE.Clazz && !isCallbackClass((node as Clazz).name) + ); + }); + let hasCallbackImplClass = cxxFile.nodes.find((node) => { + return ( + node.__TYPE == CXXTYPE.Clazz && isCallbackClass((node as Clazz).name) + ); + }); + if (hasImplClass || hasCallbackImplClass) { + bindingExportFiles.push(`export '${dartFileName(cxxFile)}.dart';`); + } + + if (hasImplClass) { + implExportFiles.push(`export '${dartFileName(cxxFile)}_impl.dart';`); + } + if (hasCallbackImplClass) { + implExportFiles.push( + `export '${dartFileName(cxxFile)}_event_impl.dart';` + ); + } + + cxxFile.nodes.forEach((node) => { + if (node.__TYPE == CXXTYPE.Clazz) { + let clazz = node as Clazz; + if (!isCallbackClass(clazz.name)) { + needHideClasses.push(dartName(clazz)); + } + } + }); + }); + + let needHideClassesBlock = needHideClasses.join(", "); + + // lib/src/binding_forward_export.dart + let bindingExport = ` +export 'package:agora_rtm/src/binding_forward_export.dart' hide ${needHideClassesBlock}; +${bindingExportFiles.join("\n")} +${implExportFiles.join("\n")} +export 'dart:convert'; +export 'dart:typed_data'; +export 'package:json_annotation/json_annotation.dart'; +export 'package:flutter/foundation.dart'; +${extraBindingExportFiles.join("\n")} +`; + + // lib/src/binding_forward_export.dart + let exportApiForwardExport = ` +${exportApiForwardExportFiles.join("\n")} +export 'dart:convert'; +export 'dart:typed_data'; +export 'package:json_annotation/json_annotation.dart'; +export 'package:flutter/foundation.dart'; +export 'package:agora_rtm/src/bindings/json_converters.dart'; +export 'package:agora_rtm/src/agora_rtm_client_ext.dart'; +`; + + // lib/src/binding/impl_forward_export.dart + let implExport = ` +${implExportFiles.join("\n")} +export 'event_handler_param_json.dart'; +export 'call_api_impl_params_json.dart'; +export 'call_api_event_handler_buffer_ext.dart'; +`; + + return [ + { + file_name: path.join(outputDir, `binding_forward_export.dart`), + file_content: exportApiForwardExport, + }, + { + file_name: path.join( + outputDir, + "bindings", + "gen", + `binding_forward_export.dart` + ), + file_content: bindingExport, + }, + + // { + // file_name: "lib/src/binding/impl_forward_export.dart", + // file_content: implExport, + // }, + ]; +} diff --git a/scripts/terra/renderers/native_bindings_renderer.ts b/scripts/terra/renderers/native_bindings_renderer.ts new file mode 100644 index 0000000..74bb948 --- /dev/null +++ b/scripts/terra/renderers/native_bindings_renderer.ts @@ -0,0 +1,363 @@ +import { + CXXFile, + CXXTYPE, + Clazz, + MemberFunction, + SimpleTypeKind, +} from "@agoraio-extensions/cxx-parser"; +import { + ParseResult, + RenderResult, + TerraContext, +} from "@agoraio-extensions/terra-core"; +import { + _trim, + defaultDartHeader, + defaultIgnoreForFile, + getBaseClasses, + isCallbackClass, + isDartBufferType, + isNullableVariable, + renderJsonSerializable, + variableToMemberVariable, +} from "./utils"; +import { + dartFileName, + dartName, + toDartStyleNaming, +} from "../parsers/dart_syntax_parser"; +import { + getIrisApiIdValue, + getOutVariable, + getOverrideNodeParserUserData, +} from "@agoraio-extensions/terra_shared_configs"; +import { + ApiInterfaceRendererArgs, + functionSignature, + isSynchronizedFunction, +} from "./api_interface_renderer"; + +import path from "path"; + +/// Generate the implementation files. +export default function BindingsRenderer( + terraContext: TerraContext, + args: ApiInterfaceRendererArgs, + parseResult: ParseResult +): RenderResult[] { + let outputDir = args.outputDir ?? ""; + let cxxFiles = (parseResult!.nodes as CXXFile[]).filter((cxxFile) => { + return cxxFile.nodes.find((node) => { + return ( + node.__TYPE == CXXTYPE.Clazz && !isCallbackClass((node as Clazz).name) + ); + }); + }); + let renderResults = cxxFiles + .map((cxxFile) => { + let subContents = cxxFile.nodes + .filter((it) => it.__TYPE == CXXTYPE.Clazz) + .filter((it) => !isCallbackClass(it.asClazz().name)) + .map((it) => { + let clazz = it.asClazz(); + let clazzName = `${dartName(clazz)}NativeBinding`; + let methods = clazz.methods; + let methodImpls = methods + .map((method) => callApiImplBlock(parseResult, clazz, method)) + .join("\n\n"); + let baseClassImplNames = getBaseClasses(parseResult, clazz).map( + (baseClass) => `${dartName(baseClass)}Impl` + ); + let shouldOverrideBaseClass = baseClassImplNames.length > 0; + let classExtendsBlock = shouldOverrideBaseClass + ? `extends ${baseClassImplNames.join(", ")} ` + : ""; + let constructorBlock = `${clazzName}(${ + shouldOverrideBaseClass + ? "IrisMethodChannel irisMethodChannel" + : "this.irisMethodChannel" + })${shouldOverrideBaseClass ? ": super(irisMethodChannel)" : ""};`; + + return ` + class ${clazzName} { + ${constructorBlock} + + ${ + shouldOverrideBaseClass + ? "" + : ` + final IrisMethodChannel irisMethodChannel;`.trim() + } + + ${shouldOverrideBaseClass ? "@override" : ""} + @protected + Map createParams(Map param) { + return param; + } + + ${shouldOverrideBaseClass ? "@override" : ""} + @protected + bool get isOverrideClassName => false; + + ${shouldOverrideBaseClass ? "@override" : ""} + @protected + String get className => '${clazzName}'; + + ${methodImpls} + } + `.trim(); + }) + .join("\n\n"); + + return subContents; + + // return { + // file_name: path.join(outputDir, `${dartFileName(cxxFile)}_impl.dart`), + // file_content: content, + // }; + }) + .join("\n\n"); + + let content = ` + ${defaultDartHeader} + + ${defaultIgnoreForFile}, annotate_overrides + + import 'package:agora_rtm/src/binding_forward_export.dart'; + import 'package:iris_method_channel/iris_method_channel.dart'; + import 'call_api_event_handler_buffer_ext.dart'; + + ${renderResults} + `; + + return [ + { + file_name: path.join(outputDir, `rtm_native_bindings.dart`), + file_content: content, + }, + ]; + + // return [ + // ...renderResults, + // // callApiImplParamsJsonFile(parseResult, cxxFiles, outputDir), + // ]; +} + +interface JsonMapInitBlock { + preInitBlock: string; + nullCheckBlock: string; + jsonKey: string; + jsonValue: string; + addBufferExtBlock: string; +} + +function callApiImplBlock( + parseResult: ParseResult, + clazz: Clazz, + method: MemberFunction +): string { + let className = dartName(clazz); + let methodName = dartName(method); + + let paramJsonMapBlock = method.parameters.map((param) => { + let paramName = dartName(param); + let preInitBlock = ""; + let nullCheckBlock = ""; + let jsonKey = param.name; + let jsonValue = paramName; + let addBufferExtBlock = ""; + + if (isNullableVariable(param)) { + nullCheckBlock = `if (${paramName} != null)`; + } + + let actualNode = parseResult.resolveNodeByType(param.type); + if (actualNode.__TYPE == CXXTYPE.Enumz) { + jsonValue = `${paramName}.value()`; + } else if (actualNode.__TYPE == CXXTYPE.Struct) { + // Null check is not necessary for struct. + nullCheckBlock = ""; + if (param.type.kind == SimpleTypeKind.array_t) { + let tmpParamName = `${paramName}JsonList`; + preInitBlock = `final ${tmpParamName} = ${paramName}.map((e) => e.toJson()).toList();`; + jsonValue = tmpParamName; + addBufferExtBlock = ` + for (final e in ${paramName}) { + buffers.addAll(e.collectBufferList()); + }`; + if (isNullableVariable(param)) { + addBufferExtBlock = `if (${paramName} != null) { + ${addBufferExtBlock} + }`; + } + } else { + jsonValue = `${paramName}${ + isNullableVariable(param) ? "?" : "" + }.toJson()`; + addBufferExtBlock = `buffers.addAll(${paramName}.collectBufferList());`; + if (isNullableVariable(param)) { + addBufferExtBlock = `if (${paramName} != null) { + ${addBufferExtBlock} + }`; + } + } + } else if (isDartBufferType(param.type)) { + jsonKey = ""; + jsonValue = ""; + addBufferExtBlock = `buffers.add(${paramName});`; + } + + return { + preInitBlock: preInitBlock, + nullCheckBlock: nullCheckBlock, + jsonKey: jsonKey, + jsonValue: jsonValue, + addBufferExtBlock: addBufferExtBlock, + } as JsonMapInitBlock; + }); + + let isNoImplFunc = isSynchronizedFunction(parseResult, method); + let isNeedAddBufferExtBlock = + paramJsonMapBlock.find((it) => it.addBufferExtBlock) != undefined; + let buffersValueInJsonMap = isNeedAddBufferExtBlock ? "buffers" : "null"; + + let irisApiIdValue = getIrisApiIdValue(method); + let overridedNode = getOverrideNodeParserUserData(method); + if (overridedNode && overridedNode.redirectIrisApiId) { + irisApiIdValue = overridedNode.redirectIrisApiId; + } + + let apiType = `final apiType = \'\${isOverrideClassName ? className : '${className}'}_${irisApiIdValue + .split("_") + .slice(1) + .join("_")}\';`; + + let returnBlock = ""; + let returnTypeNode = parseResult.resolveNodeByType(method.return_type); + let outVariable = getOutVariable(method); + if (outVariable) { + returnBlock = _trim(` + if (result < 0) { + throwExceptionHandler(code: result); + } + final ${methodName}Json = ${className}${toDartStyleNaming( + methodName, + true + )}Json.fromJson(rm); + return ${methodName}Json.${toDartStyleNaming(outVariable.name)}; + `); + } else if (returnTypeNode.__TYPE == CXXTYPE.Enumz) { + returnBlock = `return ${dartName(returnTypeNode)}Ext.fromValue(result);`; + } else if (method.return_type.name != "void") { + let returnType = dartName(method.return_type); + returnBlock = `return result as ${returnType};`; + } else { + returnBlock = `if (result < 0) { throwExceptionHandler(code: result); }`; + } + + let funcSignature = functionSignature(parseResult, method, { + forceAddOverridePrefix: false, + }); + let funcBlock = _trim( + ` +${apiType} +${paramJsonMapBlock.map((it) => it.preInitBlock).join("\n")} +final param = createParams({ + ${paramJsonMapBlock + .filter((it) => it.jsonKey && it.jsonValue) + .map((it) => `${it.nullCheckBlock}'${it.jsonKey}': ${it.jsonValue}`) + .join(",")} +}); +${isNeedAddBufferExtBlock ? "final List buffers = [];" : ""} +${paramJsonMapBlock.map((it) => it.addBufferExtBlock).join("\n")} +final callApiResult = await irisMethodChannel.invokeMethod(IrisMethodCall(apiType, jsonEncode(param), buffers:${buffersValueInJsonMap})); +if (callApiResult.irisReturnCode < 0) { + throwExceptionHandler(code: callApiResult.irisReturnCode); +} +final rm = callApiResult.data; +final result = rm['result']; +${returnBlock} + `, + { eliminateEmptyLine: true } + ); + + if (isNoImplFunc) { + funcBlock = funcBlock + .split("\n") + .map((it) => `// ${it}`) + .join("\n"); + funcBlock = _trim(` +// Implementation template +${funcBlock} + throw UnimplementedError('Unimplement for ${dartName(method)}'); + `); + } + + return _trim( + ` +${funcSignature} { + ${funcBlock} +} +` + ); +} + +/// Generate the file: lib/src/binding/call_api_impl_params_json.dart +function callApiImplParamsJsonFile( + parseResult: ParseResult, + cxxFiles: CXXFile[], + outputDir: string +): RenderResult { + let nodes = cxxFiles.flatMap((cxxFile) => { + return cxxFile.nodes + .filter((it) => it.__TYPE == CXXTYPE.Clazz) + .filter((it) => !isCallbackClass(it.asClazz().name)); + }); + + let jsonClassContents = nodes + .map((node) => { + let clazz = node.asClazz(); + let className = dartName(clazz); + let methods = clazz.methods; + + return methods + .map((method) => { + let methodName = dartName(method); + let jsonClassName = `${className}${toDartStyleNaming( + methodName, + true + )}Json`; + let output = ""; + let outVariable = getOutVariable(method); + if (outVariable) { + output = renderJsonSerializable( + parseResult, + jsonClassName, + [variableToMemberVariable(outVariable)], + { + forceExplicitNullableType: false, + forceNamingConstructor: false, + } + ); + } + + return output; + }) + .filter((it) => it.length > 0) + .join("\n\n"); + }) + .join("\n\n"); + + return { + file_name: path.join(outputDir, "call_api_impl_params_json.dart"), + file_content: _trim(` +${defaultDartHeader} + +${defaultIgnoreForFile} + +import 'package:agora_rtc_engine/src/binding_forward_export.dart'; +part 'call_api_impl_params_json.g.dart'; + +${jsonClassContents}`), + }; +} diff --git a/scripts/terra/renderers/utils.ts b/scripts/terra/renderers/utils.ts new file mode 100644 index 0000000..df3b9e2 --- /dev/null +++ b/scripts/terra/renderers/utils.ts @@ -0,0 +1,559 @@ +import { + CXXTYPE, + CXXTerraNode, + Clazz, + Enumz, + MemberFunction, + MemberVariable, + SimpleType, + SimpleTypeKind, + Struct, + Variable, +} from "@agoraio-extensions/cxx-parser"; +import { ParseResult } from "@agoraio-extensions/terra-core"; +import _ from "lodash"; +import { + dartName, + enumConstantNaming, + toDartMemberName, +} from "../parsers/dart_syntax_parser"; +import { + getPointerArrayNameMapping, + isPointerName, +} from "@agoraio-extensions/terra_shared_configs"; +import { + getDartProjectMarkerParserUserData, + isFlattenParamNodeTypeStruct, +} from "../parsers/dart_project_marker_parser"; +import { getDartTypeRemapping } from "../parsers/dart_type_remapping_parser"; + +export const defaultDartHeader = + "/// GENERATED BY terra, DO NOT MODIFY BY HAND."; + +export const defaultIgnoreForFile = + "// ignore_for_file: public_member_api_docs, unused_local_variable, unused_import"; + +export function isCallbackClass(clazzName: string): boolean { + return new RegExp( + "(EventHandler|Observer|Provider|Sink|Callback|ObserverBase|EventHandlerEx)$" + ).test(clazzName); +} + +export function isRegisterCallbackFunction( + memberFunc: MemberFunction +): boolean { + return new RegExp( + "^(register)[A-Za-z0-9]*(EventHandler|Observer|Provider)$" + ).test(memberFunc.name); +} + +export function isUnregisterCallbackFunction( + memberFunc: MemberFunction +): boolean { + return new RegExp( + "^(unregister)[A-Za-z0-9]*(EventHandler|Observer|Provider)$" + ).test(memberFunc.name); +} + +export function getBaseClasses( + parseResult: ParseResult, + clazz: Clazz +): Clazz[] { + let output: Clazz[] = []; + clazz.base_clazzs.forEach((it) => { + let found = parseResult.resolveNodeByName(it); + if (found) { + output.push(found! as Clazz); + } + }); + + return output; +} + +export function getBaseClassMethods( + parseResult: ParseResult, + clazz: Clazz +): MemberFunction[] { + return clazz.base_clazzs + .map((it) => { + return parseResult.resolveNodeByName(it); + }) + .filter((it) => it?.__TYPE == CXXTYPE.Clazz) + .map((it) => { + return it?.asClazz().methods ?? []; + }) + .flat(); +} + +export function isNullableType(type: SimpleType): boolean { + const cppTypeToNullableTypes = ["agora_refptr", "Optional"]; + + let isNullableType = + cppTypeToNullableTypes.find((it) => type.source.includes(it)) != undefined; + + return isNullableType; +} + +export function isNullableVariable(variable: Variable): boolean { + return ( + variable.default_value == "__null" || variable.default_value == "nullptr" + ); +} + +export function isNullableValue(value: String): boolean { + return value == "__null" || value == "nullptr" || value == "NULL"; +} + +export function setUserdata(node: any, key: string, value: any) { + node.user_data ??= {}; + let old = node.user_data![key]; + if (old) { + node.user_data![key] = _.merge(old, value); + } else { + node.user_data![key] = value; + } +} + +const ignoreJsonTypes = ["Uint8List"]; +export function isNeedIgnoreJsonInJsonObject( + parseResult: ParseResult, + type: SimpleType +): boolean { + let isIgnoreJson = ignoreJsonTypes.includes(dartName(type)); + + return isIgnoreJson; +} + +export function isDartBufferType(type: SimpleType): boolean { + return dartName(type) == "Uint8List"; +} + +export function _trim( + str: string, + options?: { eliminateEmptyLine?: boolean } +): string { + let eliminateEmptyLine = options?.eliminateEmptyLine ?? false; + if (eliminateEmptyLine) { + return str + .split("\n") + .filter((it) => it.trim().length != 0) + .join("\n") + .trim(); + } + + return str.trim(); +} + +export function enumConstantDefaultValue( + enumz: Enumz, + defaultValue: string +): string { + let enumValue = enumz.enum_constants.find( + (it) => it.name == defaultValue.trimNamespace() + ); + if (enumValue) { + return `${dartName(enumz)}.${enumConstantNaming(enumz, enumValue)}`; + } + return defaultValue; +} + +export interface _ParameterDeclarationHolder { + type: string; + name: string; + defaultValue: string; +} + +export function toParameterDeclaration( + parseResult: ParseResult, + param: CXXTerraNode, + type: SimpleType, + defaultValue: string +): _ParameterDeclarationHolder { + let typeNode = parseResult.resolveNodeByType(type); + let typeName = + getDartTypeRemapping(param)?.config?.dartType ?? dartName(type); + let displayNameConfig = + getDartProjectMarkerParserUserData(param)?.displayNameConfig; + let variableName = displayNameConfig?.displayName ?? dartName(param); + let returnDefaultValue = defaultValue; + + // Has default value + if (defaultValue.length > 0) { + if (typeNode.__TYPE == CXXTYPE.Enumz) { + returnDefaultValue = `${enumConstantDefaultValue( + typeNode.asEnumz(), + defaultValue + )}`; + } else if (typeNode.__TYPE == CXXTYPE.Struct) { + returnDefaultValue = defaultValue; + } else { + returnDefaultValue = toDartMemberName(defaultValue); + } + + if (isNullableValue(defaultValue)) { + typeName = `${typeName}?`; + returnDefaultValue = ""; + } + } else { + typeName = `${typeName}?`; + returnDefaultValue = ""; + } + + return { + type: typeName, + name: variableName, + defaultValue: returnDefaultValue, + }; +} + +export function renderJsonSerializable( + parseResult: ParseResult, + jsonClassName: string, + memberVariables: MemberVariable[], + options?: { + forceExplicitNullableType?: boolean; + forceNamingConstructor?: boolean; + parentNode?: CXXTerraNode; + } +) { + let forceExplicitNullableType: boolean = + options?.forceExplicitNullableType ?? true; + let forceNamingConstructor: boolean = options?.forceNamingConstructor ?? true; + let parentNode = options?.parentNode; + + let newMemberVariables = memberVariables + .filter((it) => { + return getDartProjectMarkerParserUserData(it)?.isHiddenNode != true; + }) + .filter((it) => { + let shouldInclude = true; + if (parentNode) { + let isFlattenNode = isFlattenParamNodeTypeStruct( + parseResult, + parentNode + ); + if (!isFlattenNode) { + if (parentNode.isStruct()) { + // let struct = parentNode.asStruct(); + shouldInclude = filterPointerArrayLengthNames( + parentNode.asStruct(), + it + ); + } + + if (parentNode.isMemberFunction()) { + shouldInclude = filterPointerArrayLengthNames( + parentNode.asMemberFunction(), + it + ); + } + } + } + + return shouldInclude; + }); + + let memberVariableDefaultValues = new Map(); + if (parentNode) { + if (parentNode.isStruct()) { + parentNode.asStruct().constructors.forEach((constructor) => { + constructor.initializerList.forEach((initializer) => { + if (initializer.kind == "Value") { + memberVariableDefaultValues.set( + initializer.name, + initializer.values[0] + ); + } + }); + }); + } + } + + let initializeBlock = ""; + if (newMemberVariables.length > 0) { + initializeBlock = newMemberVariables + .map((it) => { + let code = `this.${dartName(it)}`; + + let param = toParameterDeclaration( + parseResult, + it, + it.type, + memberVariableDefaultValues.get(it.name) ?? "" + ); + + let defaultValue = param.defaultValue; + let displayNameConfig = + getDartProjectMarkerParserUserData(it)?.displayNameConfig; + let defaultValueBuilder = + displayNameConfig?.converter?.defaultValueBuilder; + if (defaultValueBuilder) { + defaultValue = defaultValueBuilder( + parseResult, + displayNameConfig!, + param.defaultValue + ); + } + + if (defaultValue.length > 0) { + code += ` = ${defaultValue}`; + } + + return code; + }) + .join(","); + } + + if (forceNamingConstructor && initializeBlock.length) { + initializeBlock = `{${initializeBlock}}`; + } + + let output = ` + @JsonSerializable(explicitToJson: true, includeIfNull: false) + class ${jsonClassName} { + const ${jsonClassName}( + ${initializeBlock} + ); + + ${newMemberVariables + .map((it) => { + let isIgnoreJson = isNeedIgnoreJsonInJsonObject(parseResult, it.type); + let isNeedReadValueWithReadIntPtr = + !isIgnoreJson && isUIntPtr(parseResult, it.type); + let isNeedReadValueWithReadIntPtrList = + !isIgnoreJson && isUIntPtrList(parseResult, it.type); + let actualNode = parseResult.resolveNodeByType(it.type); + // Campatible with the old code. + isIgnoreJson = + isIgnoreJson || dartName(actualNode) == "RtmEventHandler"; + // TODO(littlegnal): Add converter annotation for class type. + // We should add converter annotation for class type, but we only add it for VideoFrameMetaInfo + // due to the historical reason. + let isClazz = actualNode.__TYPE == CXXTYPE.Clazz; + let jsonConverter = ""; + let dartProjectMarkerParserUserData = + getDartProjectMarkerParserUserData(it); + if (dartProjectMarkerParserUserData) { + jsonConverter = + dartProjectMarkerParserUserData.jsonConverter?.jsonConverter ?? ""; + } + + let nullableSurffix = forceExplicitNullableType ? "?" : ""; + let jsonKeyAnnotations = [ + `name: '${it.name}'`, + ...(isIgnoreJson ? ["ignore: true"] : []), + ...(isNeedReadValueWithReadIntPtr && + !isNeedReadValueWithReadIntPtrList + ? ["readValue: readIntPtr"] + : []), + ...(isNeedReadValueWithReadIntPtr && isNeedReadValueWithReadIntPtrList + ? ["readValue: readIntPtrList"] + : []), + ]; + + let dartType = dartName(it.type); + + let displayNameConfig = + getDartProjectMarkerParserUserData(it)?.displayNameConfig; + if (displayNameConfig && displayNameConfig.displayType) { + dartType = displayNameConfig.displayType; + } + dartType = `${dartType}${nullableSurffix}`; + + return ` + ${isClazz ? `@${dartName(actualNode)}Converter()` : jsonConverter} + @JsonKey(${jsonKeyAnnotations.join(",")}) + final ${dartType} ${dartName(it)}; + `.trim(); + }) + .join("\n\n")} + + factory ${jsonClassName}.fromJson(Map json) => _$${jsonClassName}FromJson(json); + + Map toJson() => _$${jsonClassName}ToJson(this); + } + `; + + return output; +} + +export function renderEnumJsonSerializable(enumz: Enumz) { + let enumName = dartName(enumz); + return ` +@JsonEnum(alwaysCreate: true) +enum ${enumName} { + ${enumz.enum_constants + .map((it) => { + return ` + @JsonValue(${it.value}) + ${dartName(it)}, + `; + }) + .join("\n\n")} +} + +extension ${enumName}Ext on ${enumName} { + /// @nodoc + static ${enumName} fromValue(int value) { + return $enumDecode(_$${enumName}EnumMap, value); + } + + /// @nodoc + int value() { + return _$${enumName}EnumMap[this]!; + } +} +`; +} + +export function renderTopLevelVariable(topLevelVariable: Variable): string { + const unsignedIntMaxExpression = "(std::numeric_limits::max)()"; + let dartConst = ""; + dartConst += "/// @nodoc \n"; + let defaultValue = topLevelVariable.default_value; + if (defaultValue === unsignedIntMaxExpression) { + // value of `(std::numeric_limits::max)()` + defaultValue = "4294967295"; + } + dartConst += `const ${dartName(topLevelVariable)} = ${defaultValue};\n`; + return dartConst; +} + +export function variableToMemberVariable(it: Variable): MemberVariable { + return { + name: it.name, + type: it.type, + user_data: it.user_data, + } as MemberVariable; +} + +const stdIntTypes = [ + "int8_t", + "int16_t", + "int32_t", + "int64_t", + "uint8_t", + "uint16_t", + "uint32_t", + "uint64_t", + "size_t", +]; + +// TODO(littlegnal): Move to cxx-parser +export function isStdIntType(typeName: string): boolean { + return stdIntTypes.includes(typeName); +} + +function isBufferPtr(type: SimpleType): boolean { + return ( + type.kind == SimpleTypeKind.pointer_t && + (isStdIntType(type.name) || + type.source.includes("unsigned char") || + type.name.toLowerCase().includes("buffer") || + type.source.includes("void")) + ); +} + +export function isUIntPtr(parseResult: ParseResult, type: SimpleType): boolean { + let isUIntPtr = isBufferPtr(type); + if (!isUIntPtr) { + let actualNode = parseResult.resolveNodeByType(type); + if (actualNode.__TYPE == CXXTYPE.TypeAlias) { + isUIntPtr = isBufferPtr(actualNode.asTypeAlias().underlyingType); + } + } + + return isUIntPtr; +} + +export function isUIntPtrList( + parseResult: ParseResult, + type: SimpleType +): boolean { + let isPtrList = isUIntPtr(parseResult, type); + isPtrList = isPtrList && type.kind == SimpleTypeKind.array_t; + return isPtrList; +} + +function getPointerArrayLengthNameMapping( + name: string, + self: Struct | MemberFunction +): readonly [string, string] | undefined { + function _match( + name: string, + self: Struct | MemberFunction + ): string | undefined { + let ptName = name; + let regex = new RegExp(ptName + "(Size|Count|Length)"); + if (self.isStruct()) { + return self.asStruct().member_variables.find((it) => { + return regex.test(it.name); + })?.name; + } + + return self.asMemberFunction().parameters.find((it) => { + return regex.test(it.name); + })?.name; + } + + // Check if config in `PointerArrayMarkerParser` + let fromParser = getPointerArrayNameMapping(name, self); + if (fromParser) { + return fromParser; + } + + // A pointer name should not be a pointer array length name + if (isPointerName(name, self)) { + return undefined; + } + + let ptName = _match(name, self); + + if (!ptName) { + ptName = name.replace(new RegExp("(s|List)$"), ""); + ptName = _match(ptName, self); + } + + if (!ptName) { + return undefined; + } + + return [name, ptName]; +} + +export function getPointerArrayLengthNameMappings( + self: Struct | MemberFunction +): Map | undefined { + let mapping = new Map(); + if (self.isStruct()) { + self.asStruct().member_variables.forEach((it) => { + let ptName = getPointerArrayLengthNameMapping(it.name, self); + if (ptName) { + mapping.set(ptName[0], ptName[1]); + } + }); + } + if (self.isMemberFunction()) { + self.asMemberFunction().parameters.forEach((it) => { + let ptName = getPointerArrayLengthNameMapping(it.name, self); + if (ptName) { + mapping.set(ptName[0], ptName[1]); + } + }); + } + + return mapping; +} + +export function filterPointerArrayLengthNames( + node: Struct | MemberFunction, + parameter: MemberVariable | Variable +): boolean { + let mappings = getPointerArrayLengthNameMappings(node); + // parameter + return ( + Array.from(mappings?.values() ?? []).find((it) => it == parameter.name) == + undefined + ); + // return false; +} diff --git a/scripts/terra/tsconfig.json b/scripts/terra/tsconfig.json new file mode 100644 index 0000000..49c1826 --- /dev/null +++ b/scripts/terra/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + "rootDir": ".", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/scripts/testcase_gen/.gitignore b/scripts/testcase_gen/.gitignore new file mode 100644 index 0000000..e8e12ff --- /dev/null +++ b/scripts/testcase_gen/.gitignore @@ -0,0 +1,8 @@ +# Files and directories created by pub. +.dart_tool/ +.packages + +# Conventional directory for build output. +build/ +pubspec.lock +./tmp diff --git a/scripts/testcase_gen/CHANGELOG.md b/scripts/testcase_gen/CHANGELOG.md new file mode 100644 index 0000000..effe43c --- /dev/null +++ b/scripts/testcase_gen/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/scripts/testcase_gen/README.md b/scripts/testcase_gen/README.md new file mode 100644 index 0000000..a307539 --- /dev/null +++ b/scripts/testcase_gen/README.md @@ -0,0 +1 @@ +A simple command-line application. diff --git a/scripts/testcase_gen/analysis_options.yaml b/scripts/testcase_gen/analysis_options.yaml new file mode 100644 index 0000000..dee8927 --- /dev/null +++ b/scripts/testcase_gen/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/scripts/testcase_gen/bin/event_handler_gen_config.dart b/scripts/testcase_gen/bin/event_handler_gen_config.dart new file mode 100644 index 0000000..65607d0 --- /dev/null +++ b/scripts/testcase_gen/bin/event_handler_gen_config.dart @@ -0,0 +1,72 @@ +import 'package:testcase_gen/default_generator.dart'; +import 'package:testcase_gen/templated_generator.dart'; + +List createEventHandlerTestCases(String outputDir) { + List templatedTestCases = [ + EventHandlerTemplatedTestCase( + callerObjClassName: 'RtmClient', + className: 'RtmEventHandler', + testCaseFileTemplate: ''' +$defaultHeader + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; +import 'event_ids_mapping_gen.dart'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:iris_tester/iris_tester.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases(ValueGetter irisTester, + ValueGetter> irisMethodChannelInitilizationArgs) { + + Future _createBindingRtmClient(RtmEventHandler rtmEventHandler) async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +testWidgets('{{TEST_CASE_NAME}}', (WidgetTester tester) async { + {{TEST_CASE_BODY}} + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), +); +''', + callerObjName: 'rtmClient', + outputDir: outputDir, + eventPrefixOverride: '', + registerFunctionName: '', + unregisterFunctionName: '', + registerFunctionBlockBuilder: (eventHandlerName) { + return ''' +final rtmClient = await _createBindingRtmClient($eventHandlerName); +'''; + }, + skipMemberFunctions: [], + ), + ]; + return templatedTestCases; +} diff --git a/scripts/testcase_gen/bin/method_call_gen_config.dart b/scripts/testcase_gen/bin/method_call_gen_config.dart new file mode 100644 index 0000000..963fca7 --- /dev/null +++ b/scripts/testcase_gen/bin/method_call_gen_config.dart @@ -0,0 +1,362 @@ +import 'package:testcase_gen/default_generator.dart'; +import 'package:testcase_gen/templated_generator.dart'; + +List createFakeTestCases(String outputDir) { + List templatedTestCases = [ + MethoCallTemplatedTestCase( + className: 'RtmClient', + package: Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_client.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases(ValueGetter> irisMethodChannelInitilizationArgs) { + + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +testWidgets('{{TEST_CASE_NAME}}', (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + {{TEST_CASE_BODY}} + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[{{TEST_CASE_NAME}}] error: \${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, +); +''', + methodInvokeObjectName: 'rtmClient', + outputDir: outputDir, + skipMemberFunctions: [ + 'addListener', + 'removeListener', + 'getStorage', + 'getLock', + 'getPresence', + ], + outputFileSuffixName: 'binding_fake_test', + ), + MethoCallTemplatedTestCase( + className: 'RtmLock', + package: Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_lock.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> irisMethodChannelInitilizationArgs) { + + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +testWidgets('{{TEST_CASE_NAME}}', (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmLock = RtmLockImpl(rtmClient.getIrisMethodChannel()); + + try { + {{TEST_CASE_BODY}} + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[{{TEST_CASE_NAME}}] error: \${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, +); +''', + methodInvokeObjectName: 'rtmLock', + outputDir: outputDir, + skipMemberFunctions: [], + outputFileSuffixName: 'binding_fake_test', + ), + MethoCallTemplatedTestCase( + className: 'RtmPresence', + package: Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_presence.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> irisMethodChannelInitilizationArgs) { + + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +testWidgets('{{TEST_CASE_NAME}}', (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + {{TEST_CASE_BODY}} + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[{{TEST_CASE_NAME}}] error: \${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, +); +''', + methodInvokeObjectName: 'rtmPresence', + outputDir: outputDir, + skipMemberFunctions: [], + outputFileSuffixName: 'binding_fake_test', + ), + MethoCallTemplatedTestCase( + className: 'RtmStorage', + package: Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_storage.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> irisMethodChannelInitilizationArgs) { + + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +testWidgets('{{TEST_CASE_NAME}}', (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + {{TEST_CASE_BODY}} + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[{{TEST_CASE_NAME}}] error: \${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, +); +''', + methodInvokeObjectName: 'rtmStorage', + outputDir: outputDir, + skipMemberFunctions: [], + outputFileSuffixName: 'binding_fake_test', + ), + MethoCallTemplatedTestCase( + className: 'StreamChannel', + package: Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_stream_channel.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> irisMethodChannelInitilizationArgs) { + + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +testWidgets('{{TEST_CASE_NAME}}', (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = await rtmClient.createStreamChannel('stream_channel'); + + try { + {{TEST_CASE_BODY}} + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[{{TEST_CASE_NAME}}] error: \${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, +); +''', + methodInvokeObjectName: 'streamChannel', + outputDir: outputDir, + skipMemberFunctions: [], + outputFileSuffixName: 'binding_fake_test', + ), + ]; + return templatedTestCases; +} diff --git a/scripts/testcase_gen/bin/testcase_gen.dart b/scripts/testcase_gen/bin/testcase_gen.dart new file mode 100644 index 0000000..992271f --- /dev/null +++ b/scripts/testcase_gen/bin/testcase_gen.dart @@ -0,0 +1,69 @@ +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:paraphrase/paraphrase.dart'; +import 'package:file/file.dart' as file; +import 'package:file/local.dart'; +import 'package:path/path.dart' as path; +import 'package:testcase_gen/templated_generator.dart'; + +import 'event_handler_gen_config.dart'; +import 'method_call_gen_config.dart'; +import 'unit_test_gen_config.dart'; + +const file.FileSystem fileSystem = LocalFileSystem(); + +void main(List arguments) { + final parser = ArgParser(); + parser.addFlag('gen-integration-test'); + parser.addFlag('gen-fake-test'); + parser.addOption('output-dir', mandatory: true); + + final results = parser.parse(arguments); + + final genIntegrationTest = results['gen-integration-test'] ?? false; + final genFakeTest = results['gen-fake-test'] ?? false; + final outputDir = results['output-dir']!; + + final srcDir = path.join( + fileSystem.currentDirectory.absolute.path, + 'lib', + 'src', + ); + final List includedPaths = [ + path.join(srcDir, 'agora_rtm_base.dart'), + path.join(srcDir, 'agora_rtm_client.dart'), + path.join(srcDir, 'agora_rtm_lock.dart'), + path.join(srcDir, 'agora_rtm_presence.dart'), + path.join(srcDir, 'agora_rtm_storage.dart'), + path.join(srcDir, 'agora_stream_channel.dart'), + path.join(srcDir, 'bindings', 'gen', 'agora_rtm_client.dart'), + path.join(srcDir, 'bindings', 'gen', 'agora_rtm_lock.dart'), + path.join(srcDir, 'bindings', 'gen', 'agora_rtm_presence.dart'), + path.join(srcDir, 'bindings', 'gen', 'agora_rtm_storage.dart'), + path.join(srcDir, 'bindings', 'gen', 'agora_stream_channel.dart'), + ]; + + final bindingIntegrationTestOutputDir = + path.join(outputDir, 'integration_test', 'generated', 'bindings'); + final unitTestOutputDir = path.join(outputDir, 'test', 'generated'); + List templatedTestCases = []; + if (genIntegrationTest) { + templatedTestCases = [ + ...createUnitTestCases(unitTestOutputDir), + ...createFakeTestCases(bindingIntegrationTestOutputDir), + ...createEventHandlerTestCases(bindingIntegrationTestOutputDir), + ]; + } else if (genFakeTest) {} + + Paraphrase paraphrase = Paraphrase(includedPaths: includedPaths); + final parseResult = paraphrase.visit(); + + final generator = TemplatedGenerator(templatedTestCases); + + final file = File('tmp'); + final fileSink = file.openWrite(); + generator.generate(fileSink, parseResult); + + file.delete(recursive: true); +} diff --git a/scripts/testcase_gen/bin/unit_test_gen_config.dart b/scripts/testcase_gen/bin/unit_test_gen_config.dart new file mode 100644 index 0000000..f853f93 --- /dev/null +++ b/scripts/testcase_gen/bin/unit_test_gen_config.dart @@ -0,0 +1,466 @@ +import 'package:paraphrase/paraphrase.dart'; +import 'package:testcase_gen/default_generator.dart'; +import 'package:testcase_gen/templated_generator.dart'; + +List createUnitTestCases(String outputDir) { + List templatedTestCases = [ + CustomTemplatedTestCase( + className: 'RtmClient', + package: Uri.tryParse('package:agora_rtm/src/agora_rtm_client.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/agora_rtm_client_impl_override.dart' + as agora_rtm_client_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +test('{{TEST_CASE_NAME}}', () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + {{TEST_CASE_BODY}} + + await rtmClient.release(); + }, +); +''', + methodInvokeObjectName: 'rtmClient', + outputDir: outputDir, + skipMemberFunctions: [ + 'addListener', + 'removeListener', + 'release', + ], + outputFileSuffixName: 'unit_test', + customMethodCodeGenerator: _mockUnitTestCodeGenerator, + extraArgs: { + 'mockNativeBindingClass': { + 'name': 'RtmClient', + 'callerName': 'mockRtmClientNativeBinding', + 'package': Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_client.dart'), + } + }, + ), + CustomTemplatedTestCase( + className: 'RtmLock', + package: Uri.tryParse('package:agora_rtm/src/agora_rtm_lock.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_rtm_lock_impl.dart' + as agora_rtm_lock_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +test('{{TEST_CASE_NAME}}', () async { + final mockRtmLockNativeBinding = MockRtmLockImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmLock rtmLock = agora_rtm_lock_impl.RtmLockImpl( + mockRtmLockNativeBinding, + mockRtmResultHandlerImpl, + ); + + {{TEST_CASE_BODY}} + }, +); +''', + methodInvokeObjectName: 'rtmLock', + outputDir: outputDir, + skipMemberFunctions: [ + 'addListener', + 'removeListener', + 'release', + ], + outputFileSuffixName: 'unit_test', + customMethodCodeGenerator: _mockUnitTestCodeGenerator, + extraArgs: { + 'mockNativeBindingClass': { + 'name': 'RtmLock', + 'callerName': 'mockRtmLockNativeBinding', + 'package': Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_lock.dart'), + } + }, + ), + CustomTemplatedTestCase( + className: 'RtmPresence', + package: Uri.tryParse('package:agora_rtm/src/agora_rtm_presence.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_rtm_presence_impl.dart' + as rtm_presence_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +test('{{TEST_CASE_NAME}}', () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + {{TEST_CASE_BODY}} + }, +); +''', + methodInvokeObjectName: 'rtmPresence', + outputDir: outputDir, + skipMemberFunctions: [ + 'setState', + ], + outputFileSuffixName: 'unit_test', + customMethodCodeGenerator: _mockUnitTestCodeGenerator, + extraArgs: { + 'mockNativeBindingClass': { + 'name': 'RtmPresence', + 'callerName': 'mockRtmPresenceNativeBinding', + 'package': Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_presence.dart'), + } + }, + ), + CustomTemplatedTestCase( + className: 'RtmStorage', + package: Uri.tryParse('package:agora_rtm/src/agora_rtm_storage.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_rtm_storage_impl.dart' + as rtm_storage_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +test('{{TEST_CASE_NAME}}', () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + {{TEST_CASE_BODY}} + }, +); +''', + methodInvokeObjectName: 'rtmStorage', + outputDir: outputDir, + skipMemberFunctions: [ + 'addListener', + 'removeListener', + 'release', + ], + outputFileSuffixName: 'unit_test', + customMethodCodeGenerator: _mockUnitTestCodeGenerator, + extraArgs: { + 'mockNativeBindingClass': { + 'name': 'RtmStorage', + 'callerName': 'mockRtmStorageNativeBinding', + 'package': Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_rtm_storage.dart'), + } + }, + ), + CustomTemplatedTestCase( + className: 'StreamChannel', + package: Uri.tryParse('package:agora_rtm/src/agora_stream_channel.dart'), + testCaseFileTemplate: ''' +$defaultHeader + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_stream_channel_impl.dart' + as stream_channel_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + + {{TEST_CASES_CONTENT}} +} +''', + testCaseTemplate: ''' +test('{{TEST_CASE_NAME}}', () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + {{TEST_CASE_BODY}} + }, +); +''', + methodInvokeObjectName: 'streamChannel', + outputDir: outputDir, + skipMemberFunctions: [], + outputFileSuffixName: 'unit_test', + customMethodCodeGenerator: _mockUnitTestCodeGenerator, + extraArgs: { + 'mockNativeBindingClass': { + 'name': 'StreamChannel', + 'callerName': 'mockStreamChannelNativeBinding', + 'package': Uri.tryParse( + 'package:agora_rtm/src/bindings/gen/agora_stream_channel.dart'), + } + }, + ), + ]; + return templatedTestCases; +} + +String _mockUnitTestCodeGenerator( + TemplatedGenerator templatedGenerator, + ParseResult parseResult, + Clazz clazz, + Method method, + CustomTemplatedTestCase templated) { + void buildParameterInitList(StringBuffer initializerBuilder, Method method) { + for (final parameter in method.parameters) { + if (parameter.type.type == 'Function') { + continue; + } + if (parameter.isPrimitiveType) { + final parameterType = templatedGenerator.getParamType(parameter); + + if (parameterType == 'Uint8List') { + initializerBuilder.writeln( + '${templatedGenerator.getParamType(parameter)} ${parameter.name} = ${templatedGenerator.defualtValueOfType(parameter.type)};'); + } else { + if (parameterType.startsWith('List') && + parameter.type.typeArguments.isNotEmpty) { + final listBuilderBlock = templatedGenerator + .createListBuilderBlockForList(parseResult, parameter); + initializerBuilder.writeln(listBuilderBlock); + } else { + initializerBuilder.writeln( + '${templatedGenerator.getParamType(parameter)} ${parameter.name} = ${templatedGenerator.defualtValueOfType(parameter.type)};'); + } + } + } else { + templatedGenerator.createConstructorInitializerForMethodParameter( + parseResult, null, parameter, initializerBuilder); + } + } + } + + StringBuffer initializerBuilder = StringBuffer(); + String fakeParameterName = ''; + String mockReturnValueParamName = ''; + String mockResultHandlerReturnValue = ''; + String expectedResultHandlerReturnValue = ''; + bool shouldMockResultHandler = false; + final fakeParameter = Parameter(); + bool shouldCheckReturnType = false; + if (method.returnType.typeArguments.isNotEmpty) { + String typeArgument = ''; + bool isDartRecords = false; + if (method.returnType.typeArguments.isNotEmpty) { + final typeArgumentType = method.returnType.typeArguments[0]; + if (typeArgumentType.positionalFields.length > 1) { + typeArgument = typeArgumentType.positionalFields[1].type; + isDartRecords = true; + } + } + + shouldMockResultHandler = parseResult.hasClass(typeArgument); + + if (shouldMockResultHandler) { + shouldCheckReturnType = true; + + if (isDartRecords) { + initializerBuilder.writeln( + 'const rtmStatus = RtmStatus.success(operation: \'${method.name}\');'); + + final resultClassType = + method.returnType.typeArguments[0].positionalFields[1]; + + final fakeParameterName = 'the${resultClassType.type}'; + final fakeParameter = Parameter() + ..type = method.returnType.typeArguments[0].positionalFields[1] + ..name = fakeParameterName; + + templatedGenerator.createConstructorInitializerForMethodParameter( + parseResult, null, fakeParameter, initializerBuilder); + + initializerBuilder.writeln( + 'final mockResultHandlerReturnValue = ($fakeParameterName, RtmErrorCode.ok);'); + mockResultHandlerReturnValue = 'mockResultHandlerReturnValue'; + + initializerBuilder.writeln( + 'final expectedResultHandlerReturnValue = (rtmStatus, $fakeParameterName);'); + expectedResultHandlerReturnValue = 'expectedResultHandlerReturnValue'; + + mockReturnValueParamName = 'mockRequestId'; + initializerBuilder.writeln('int $mockReturnValueParamName = 1;'); + } else { + fakeParameterName = + '${typeArgument[0].toLowerCase()}${typeArgument.substring(1)}'; + fakeParameter + ..type = method.returnType.typeArguments[0] + ..name = fakeParameterName; + mockResultHandlerReturnValue = fakeParameterName; + + mockReturnValueParamName = 'mockRequestId'; + initializerBuilder.writeln('int $mockReturnValueParamName = 1;'); + + templatedGenerator.createConstructorInitializerForMethodParameter( + parseResult, null, fakeParameter, initializerBuilder); + } + } else { + if (typeArgument != 'void') { + Type tmpType = method.returnType.typeArguments[0]; + if (method.returnType.typeArguments[0].positionalFields.length > 1) { + tmpType = method.returnType.typeArguments[0].positionalFields[1]; + mockReturnValueParamName = 'the$typeArgument'; + } + + fakeParameter + ..type = tmpType + ..name = mockReturnValueParamName; + if (fakeParameter.type.isPrimitiveType) { + initializerBuilder.writeln( + '${templatedGenerator.getParamType(fakeParameter)} $mockReturnValueParamName = ${templatedGenerator.defualtValueOfType(fakeParameter.type)};'); + } + } + } + } + + final mockNativeBindingClassMap = Map.from( + templated.extraArgs['mockNativeBindingClass'] as Map); + final mockNativeBindingClassName = + mockNativeBindingClassMap['name'] as String; + final mockNativeBindingClassCallerName = + mockNativeBindingClassMap['callerName'] as String; + final mockNativeBindingClassPackage = + mockNativeBindingClassMap['package'] as Uri; + + final mockNativeBindingClass = templatedGenerator.getClazz( + parseResult, mockNativeBindingClassName, mockNativeBindingClassPackage); + + final mockMethod = mockNativeBindingClass.methods + .firstWhere((element) => element.name == method.name); + + final mockNativeBindingCall = StringBuffer(); + mockNativeBindingCall.writeln('{'); + buildParameterInitList(mockNativeBindingCall, mockMethod); + final parameterListBlockBuilder = StringBuffer(); + for (final parameter in mockMethod.parameters) { + String paramValue = parameter.name; + if (!parameter.type.isPrimitiveType) {} + + final parameterTypeClazzes = parseResult.getClazz(parameter.type.type); + if (parameterTypeClazzes.isNotEmpty) { + final cls = parameterTypeClazzes[0]; + paramValue = + 'argThat(isA<${cls.name}>()${parameter.isNamed ? ', named: \'$paramValue\',' : ''})'; + } else { + final paramType = templatedGenerator.getParamType(parameter); + if (paramType.startsWith('List') || paramType.startsWith('Map')) { + paramValue = + 'argThat(isA<${templatedGenerator.getParamType(parameter)}>()${parameter.isNamed ? ', named: \'$paramValue\',' : ''})'; + } + } + + if (parameter.isNamed) { + parameterListBlockBuilder.write('${parameter.name}:$paramValue,'); + } else { + parameterListBlockBuilder.write('$paramValue, '); + } + } + if (mockReturnValueParamName.isNotEmpty) { + mockNativeBindingCall.writeln( + 'when($mockNativeBindingClassCallerName.${mockMethod.name}(${parameterListBlockBuilder.toString()})).thenAnswer((_) async => $mockReturnValueParamName);'); + } + + if (shouldMockResultHandler) { + mockNativeBindingCall.writeln( + 'when(mockRtmResultHandlerImpl.request($mockReturnValueParamName)).thenAnswer((_) async => $mockResultHandlerReturnValue);'); + } + + mockNativeBindingCall.writeln('}'); + initializerBuilder.writeln(mockNativeBindingCall.toString()); + + buildParameterInitList(initializerBuilder, method); + + StringBuffer methodCallBuilder = StringBuffer(); + + if (shouldCheckReturnType) { + methodCallBuilder.write('final ret = '); + } + + bool isFuture = method.returnType.type == 'Future'; + methodCallBuilder.write( + '${isFuture ? 'await ' : ''}${templated.methodInvokeObjectName}.${method.name}('); + for (final parameter in method.parameters) { + if (parameter.isNamed) { + methodCallBuilder.write('${parameter.name}:${parameter.name},'); + } else { + methodCallBuilder.write('${parameter.name}, '); + } + } + methodCallBuilder.write(');'); + if (shouldCheckReturnType) { + if (shouldMockResultHandler) { + methodCallBuilder + .write('expect(ret, $expectedResultHandlerReturnValue);'); + } else { + methodCallBuilder.write('expect(ret, $mockReturnValueParamName);'); + } + } + initializerBuilder.writeln(methodCallBuilder.toString()); + + return initializerBuilder.toString(); +} diff --git a/scripts/testcase_gen/build.sh b/scripts/testcase_gen/build.sh new file mode 100644 index 0000000..215bb3a --- /dev/null +++ b/scripts/testcase_gen/build.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e +set -x + +MY_PATH=$(realpath $(dirname "$0")) +PROJECT_ROOT=$(realpath ${MY_PATH}/../../) + +pushd ${MY_PATH} +dart pub get +popd + +dart run ${MY_PATH}/bin/testcase_gen.dart \ + --gen-integration-test --output-dir=${PROJECT_ROOT}/test_shard/integration_test_app + +dart format . diff --git a/scripts/testcase_gen/lib/default_generator.dart b/scripts/testcase_gen/lib/default_generator.dart new file mode 100644 index 0000000..b78c791 --- /dev/null +++ b/scripts/testcase_gen/lib/default_generator.dart @@ -0,0 +1,216 @@ +import 'package:paraphrase/paraphrase.dart'; +import 'package:testcase_gen/generator.dart'; + +const ignoreForFile = '// ignore_for_file: ' + 'deprecated_member_use,' + 'constant_identifier_names,' + 'unused_local_variable,' + 'unused_import,' + 'unnecessary_import'; + +const defaultHeader = ''' +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +$ignoreForFile +'''; + +abstract class DefaultGenerator implements Generator { + const DefaultGenerator(); + + GeneratorConfig? getConfig(List configs, String methodName) { + for (final config in configs) { + if (config.name == methodName) { + return config; + } + } + return null; + } + + String _concatParamName(String? prefix, String name) { + if (prefix == null) return name; + return '$prefix${name[0].toUpperCase()}${name.substring(1)}'; + } + + String getParamType(Parameter parameter) { + if (parameter.type.typeArguments.isEmpty) { + return parameter.type.type; + } + + return '${parameter.type.type}<${parameter.type.typeArguments.map((e) => e.type).join(', ')}>'; + } + + bool _isPrimitiveType(Type type) => + type.type == 'int' || + type.type == 'double' || + type.type == 'bool' || + type.type == 'String' || + type.type == 'List' || + type.type == 'Map' || + type.type == 'Set' || + type.type == 'Uint8List'; + + String defualtValueOfType( + Type type, + ) { + switch (type.type) { + case 'int': + return '5'; + case 'double': + return '5.0'; + case 'String': + return '"hello"'; + case 'bool': + return 'true'; + case 'List': + if (type.typeArguments.isNotEmpty) { + final typeArgumentType = Type()..type = type.typeArguments[0].type; + if (_isPrimitiveType(typeArgumentType)) { + return 'List.filled(5, ${defualtValueOfType(typeArgumentType)})'; + } else { + return '[]'; + } + } + return '[]'; + case 'Map': + return '{}'; + case 'Uint8List': + return 'Uint8List.fromList([1, 1, 1, 1, 1])'; + case 'Set': + return '{}'; + + default: + throw Exception('not support type $type'); + } + } + + String createListBuilderBlockForList( + ParseResult parseResult, Parameter parameter) { + if (parameter.type.typeArguments.isNotEmpty) { + final listTypeArgumentType = parameter.type.typeArguments[0]; + final listTypeArgumentTypeClazzes = + parseResult.getClazz(listTypeArgumentType.type); + if (listTypeArgumentTypeClazzes.isNotEmpty) { + final clazz = listTypeArgumentTypeClazzes[0]; + final toParamType = Type()..type = clazz.name; + Parameter pp = Parameter() + ..type = toParamType + ..name = '${parameter.name}Item'; + final listInitializerBuilder = StringBuffer(); + createConstructorInitializerForMethodParameter( + parseResult, null, pp, listInitializerBuilder); + final listBuilder = ''' +final ${getParamType(parameter)} ${parameter.name} = () { +${listInitializerBuilder.toString()} + + return List.filled(5, ${parameter.name}Item); +}(); +'''; + return listBuilder; + } + } + return '${getParamType(parameter)} ${parameter.name} = ${defualtValueOfType(parameter.type)};'; + } + + String createConstructorInitializerForMethodParameter( + ParseResult parseResult, + Parameter? rootParameter, + Parameter parameter, + StringBuffer initializerBuilder, + ) { + final bool isEnum = parseResult.hasEnum(parameter.type.type); + + if (isEnum) { + final enumz = parseResult.getEnum(parameter.type.type)[0]; + + initializerBuilder.writeln( + '${getParamType(parameter)} ${_concatParamName(rootParameter?.name, parameter.name)} = ${enumz.enumConstants[0].name};'); + + return _concatParamName(rootParameter?.name, parameter.name); + } + + final parameterClass = parseResult.getClazz(parameter.type.type)[0]; + final initBlockParameterListBuilder = StringBuffer(); + final initBlockBuilder = StringBuffer(); + + bool isNullable = false; + if (parameterClass.constructors.isEmpty) { + // If there're not constructors found, default to null. + isNullable = true; + initBlockBuilder.write('null'); + } else { + initBlockBuilder.write(parameterClass.name); + initBlockBuilder.write('('); + + for (final cp in parameterClass.constructors[0].parameters) { + final adjustedParamName = _concatParamName(parameter.name, cp.name); + if (cp.isNamed) { + if (cp.type.type == 'Function') { + final functionParamsList = cp.type.parameters + .map((t) => '${t.type.type} ${t.name}') + .join(', '); + + initBlockBuilder.write('${cp.name}:($functionParamsList) { },'); + } else if (cp.isPrimitiveType) { + if (getParamType(cp) == 'Uint8List') { + initBlockParameterListBuilder.writeln( + '${getParamType(cp)} $adjustedParamName = ${defualtValueOfType(cp.type)};'); + } else { + if (getParamType(cp).startsWith('List') && + parameter.type.typeArguments.isNotEmpty) { + final listBuilderBlock = + createListBuilderBlockForList(parseResult, parameter); + initBlockParameterListBuilder.writeln(listBuilderBlock); + } else { + initBlockParameterListBuilder.writeln( + '${getParamType(cp)} $adjustedParamName = ${defualtValueOfType(cp.type)};'); + } + } + + initBlockBuilder.write('${cp.name}: $adjustedParamName,'); + } else { + createConstructorInitializerForMethodParameter( + parseResult, parameter, cp, initializerBuilder); + initBlockBuilder.write('${cp.name}: $adjustedParamName,'); + } + } else { + if (cp.type.type == 'Function') { + final functionParamsList = cp.type.parameters + .map((t) => '${t.type.type} ${t.name}') + .join(', '); + + initBlockBuilder.write('${cp.name}:($functionParamsList) { },'); + } else if (cp.isPrimitiveType) { + if (getParamType(cp) == 'Uint8List') { + initBlockParameterListBuilder.writeln( + '${getParamType(cp)} $adjustedParamName = ${defualtValueOfType(cp.type)};'); + } else { + if (getParamType(cp).startsWith('List') && + parameter.type.typeArguments.isNotEmpty) { + final listBuilderBlock = + createListBuilderBlockForList(parseResult, parameter); + initBlockParameterListBuilder.writeln(listBuilderBlock); + } else { + initBlockParameterListBuilder.writeln( + '${getParamType(cp)} $adjustedParamName = ${defualtValueOfType(cp.type)};'); + } + } + + initBlockBuilder.write('$adjustedParamName,'); + } else { + createConstructorInitializerForMethodParameter( + parseResult, parameter, cp, initializerBuilder); + initBlockBuilder.write('$adjustedParamName,'); + } + } + } + + initBlockBuilder.write(')'); + } + + initializerBuilder.write(initBlockParameterListBuilder.toString()); + + initializerBuilder.writeln( + '${getParamType(parameter)}${isNullable ? '?' : ''} ${_concatParamName(rootParameter?.name, parameter.name)} = ${initBlockBuilder.toString()};'); + return _concatParamName(rootParameter?.name, parameter.name); + } +} diff --git a/scripts/testcase_gen/lib/generator.dart b/scripts/testcase_gen/lib/generator.dart new file mode 100644 index 0000000..201e8e0 --- /dev/null +++ b/scripts/testcase_gen/lib/generator.dart @@ -0,0 +1,81 @@ +import 'dart:io'; + +import 'package:paraphrase/paraphrase.dart'; + +enum GeneratorConfigPlatform { + android, + iOS, + macOS, + windows, + linux, +} + +extension GeneratorConfigPlatformExt on GeneratorConfigPlatform { + String toPlatformExpression() { + switch (this) { + case GeneratorConfigPlatform.android: + return 'Platform.isAndroid'; + case GeneratorConfigPlatform.iOS: + return 'Platform.isIOS'; + case GeneratorConfigPlatform.macOS: + return 'Platform.isMacOS'; + case GeneratorConfigPlatform.windows: + return 'Platform.isWindows'; + case GeneratorConfigPlatform.linux: + return 'Platform.isLinux'; + } + } +} + +class GeneratorConfig { + const GeneratorConfig({ + required this.name, + this.donotGenerate = false, + this.supportedPlatforms = const [ + GeneratorConfigPlatform.android, + GeneratorConfigPlatform.iOS, + GeneratorConfigPlatform.macOS, + GeneratorConfigPlatform.windows, + GeneratorConfigPlatform.linux, + ], + this.shouldMockResult = false, + this.shouldMockReturnCode = false, + }); + final String name; + final bool donotGenerate; + final List supportedPlatforms; + final bool shouldMockReturnCode; + final bool shouldMockResult; +} + +const List desktopPlatforms = [ + GeneratorConfigPlatform.macOS, + GeneratorConfigPlatform.windows, + GeneratorConfigPlatform.linux, +]; + +const List mobilePlatforms = [ + GeneratorConfigPlatform.android, + GeneratorConfigPlatform.iOS, +]; + +IOSink? openSink(String? output) { + if (output == null) { + return null; + } + IOSink sink; + File file; + if (output == 'stdout') { + sink = stdout; + } else { + file = File(output); + sink = file.openWrite(); + } + return sink; +} + +abstract class Generator { + void generate(StringSink sink, ParseResult parseResult); + + IOSink? shouldGenerate(ParseResult parseResult); +} diff --git a/scripts/testcase_gen/lib/templated_generator.dart b/scripts/testcase_gen/lib/templated_generator.dart new file mode 100644 index 0000000..6dc0e8e --- /dev/null +++ b/scripts/testcase_gen/lib/templated_generator.dart @@ -0,0 +1,587 @@ +import 'package:dart_style/dart_style.dart'; +import 'package:paraphrase/paraphrase.dart'; +import 'dart:io'; + +import 'package:testcase_gen/default_generator.dart'; +import 'package:path/path.dart' as path; + +import 'generator.dart'; + +abstract class TemplatedTestCase { + TemplatedTestCase({ + required this.className, + this.package, + required this.testCaseFileTemplate, + required this.testCaseTemplate, + required this.outputDir, + this.outputFileSuffixName = 'testcases', + this.skipMemberFunctions = const [], + }); + + final String className; + Uri? package; + final String testCaseFileTemplate; + final String testCaseTemplate; + final String outputDir; + final String outputFileSuffixName; + final List skipMemberFunctions; +} + +class MethoCallTemplatedTestCase extends TemplatedTestCase { + MethoCallTemplatedTestCase({ + required String className, + Uri? package, + required String testCaseFileTemplate, + required String testCaseTemplate, + required String outputDir, + required this.methodInvokeObjectName, + List skipMemberFunctions = const [], + required String outputFileSuffixName, + }) : super( + className: className, + package: package, + testCaseFileTemplate: testCaseFileTemplate, + testCaseTemplate: testCaseTemplate, + outputDir: outputDir, + outputFileSuffixName: outputFileSuffixName, + skipMemberFunctions: skipMemberFunctions, + ); + + final String methodInvokeObjectName; +} + +class EventHandlerTemplatedTestCase extends TemplatedTestCase { + EventHandlerTemplatedTestCase({ + required this.callerObjClassName, + required String className, + required String testCaseFileTemplate, + required String testCaseTemplate, + required String outputDir, + required this.callerObjName, + this.eventPrefixOverride, + required this.registerFunctionName, + required this.unregisterFunctionName, + this.registerFunctionBlockBuilder, + this.unregisterFunctionBlockBuilder, + this.isUpperFirstCaseOfEventName = false, + List skipMemberFunctions = const [], + }) : super( + className: className, + testCaseFileTemplate: testCaseFileTemplate, + testCaseTemplate: testCaseTemplate, + outputDir: outputDir, + skipMemberFunctions: skipMemberFunctions, + ); + + final String callerObjClassName; + final String callerObjName; + final String? eventPrefixOverride; + final String registerFunctionName; + final String unregisterFunctionName; + final String Function(String eventHandlerName)? registerFunctionBlockBuilder; + final String Function(String eventHandlerName)? + unregisterFunctionBlockBuilder; + final bool isUpperFirstCaseOfEventName; +} + +class CustomTemplatedTestCase extends MethoCallTemplatedTestCase { + CustomTemplatedTestCase({ + required String className, + Uri? package, + required String testCaseFileTemplate, + required String testCaseTemplate, + required String outputDir, + required String methodInvokeObjectName, + List skipMemberFunctions = const [], + required String outputFileSuffixName, + required this.customMethodCodeGenerator, + this.extraArgs = const {}, + }) : super( + className: className, + package: package, + testCaseFileTemplate: testCaseFileTemplate, + testCaseTemplate: testCaseTemplate, + outputDir: outputDir, + outputFileSuffixName: outputFileSuffixName, + skipMemberFunctions: skipMemberFunctions, + methodInvokeObjectName: methodInvokeObjectName, + ); + + // final String methodInvokeObjectName; + final String Function( + TemplatedGenerator templatedGenerator, + ParseResult parseResult, + Clazz clazz, + Method method, + CustomTemplatedTestCase template) customMethodCodeGenerator; + + final Map extraArgs; +} + +class TemplatedGenerator extends DefaultGenerator { + const TemplatedGenerator(this.templatedTestCases); + + final List templatedTestCases; + + Clazz getClazz(ParseResult parseResult, String className, Uri? package) { + final classes = parseResult.getClazz(className); + for (final cls in classes) { + if (cls.name == className && cls.uri == package) { + return cls; + } + } + throw StateError('Can not find $className'); + } + + @override + void generate(StringSink sink, ParseResult parseResult) { + for (final templated in templatedTestCases) { + String output = ''; + String outputFileName = ''; + if (templated is CustomTemplatedTestCase) { + late Clazz clazz; + try { + clazz = getClazz(parseResult, templated.className, templated.package); + } catch (e) { + stderr.writeln('Can not find the className: ${templated.className}.'); + rethrow; + } + + output = generateWithTemplate( + parseResult: parseResult, + clazz: clazz, + testCaseTemplate: templated.testCaseTemplate, + testCasesContentTemplate: templated.testCaseFileTemplate, + methodInvokeObjectName: templated.methodInvokeObjectName, + configs: const [], + supportedPlatformsOverride: const [], + skipMemberFunctions: templated.skipMemberFunctions, + templated: templated, + customMethodCodeGenerator: templated.customMethodCodeGenerator, + ); + outputFileName = + '${templated.className.toLowerCase()}_${templated.outputFileSuffixName}.generated.dart'; + } else if (templated is MethoCallTemplatedTestCase) { + late Clazz clazz; + try { + clazz = getClazz(parseResult, templated.className, templated.package); + } catch (e) { + stderr.writeln('Can not find the className: ${templated.className}.'); + rethrow; + } + + output = generateWithTemplate( + parseResult: parseResult, + clazz: clazz, + testCaseTemplate: templated.testCaseTemplate, + testCasesContentTemplate: templated.testCaseFileTemplate, + methodInvokeObjectName: templated.methodInvokeObjectName, + configs: const [], + supportedPlatformsOverride: const [], + skipMemberFunctions: templated.skipMemberFunctions, + templated: templated, + ); + outputFileName = + '${templated.className.toLowerCase()}_${templated.outputFileSuffixName}.generated.dart'; + } else if (templated is EventHandlerTemplatedTestCase) { + late Clazz templatedCallerObjClazz; + late Clazz templatedClazz; + try { + templatedCallerObjClazz = + parseResult.getClazz(templated.callerObjClassName)[0]; + } catch (e) { + stderr.writeln( + 'Can not find the callerObjClassName: ${templated.callerObjClassName}, make sure the class name is correct.'); + } + + try { + templatedClazz = parseResult.getClazz(templated.className)[0]; + } catch (e) { + stderr.writeln( + 'Can not find the className: ${templated.className}, make sure the class name is correct.'); + } + output = _generateEventHandlerCasesWithTemplate( + parseResult: parseResult, + callerObjClazz: templatedCallerObjClazz, + eventHandlerClazz: templatedClazz, + testCaseTemplate: templated.testCaseFileTemplate, + testCasesContentTemplate: templated.testCaseTemplate, + callerObjName: templated.callerObjName, + eventPrefixOverride: templated.eventPrefixOverride, + registerFunctionName: templated.registerFunctionName, + unregisterFunctionName: templated.unregisterFunctionName, + registerFunctionBlockBuilder: templated.registerFunctionBlockBuilder, + unregisterFunctionBlockBuilder: + templated.unregisterFunctionBlockBuilder, + isUpperFirstCaseOfEventName: templated.isUpperFirstCaseOfEventName, + skipMemberFunctions: templated.skipMemberFunctions, + ); + outputFileName = + '${templated.callerObjClassName.toLowerCase()}_${templated.className.toLowerCase()}_${templated.outputFileSuffixName}.generated.dart'; + } + + final fileSink = openSink(path.join( + templated.outputDir, + outputFileName, + )); + fileSink!.writeln(output); + fileSink.flush(); + } + } + + @override + IOSink? shouldGenerate(ParseResult parseResult) { + return null; + } + + String _generateEventHandlerCasesWithTemplate({ + required ParseResult parseResult, + required Clazz callerObjClazz, + required Clazz eventHandlerClazz, + required String testCaseTemplate, + required String testCasesContentTemplate, + String? eventPrefixOverride, + required String callerObjName, + required String registerFunctionName, + required String unregisterFunctionName, + String Function(String eventHandlerName)? registerFunctionBlockBuilder, + String Function(String eventHandlerName)? unregisterFunctionBlockBuilder, + required bool isUpperFirstCaseOfEventName, + List skipMemberFunctions = const [], + }) { + // final fields = clazz.fields; + final eventHandlerName = 'the${eventHandlerClazz.name}'; + + final testCases = []; + + final registerFunctionImpl = registerFunctionBlockBuilder != null + ? registerFunctionBlockBuilder(eventHandlerName) + : _getMethodCallImpl( + parseResult: parseResult, + callerObjClazz: callerObjClazz, + eventHandlerClazz: eventHandlerClazz, + callerObjName: callerObjName, + functionName: registerFunctionName, + eventHandlerName: eventHandlerName, + ); + final unregisterFunctionImpl = unregisterFunctionBlockBuilder != null + ? unregisterFunctionBlockBuilder(eventHandlerName) + : _getMethodCallImpl( + parseResult: parseResult, + callerObjClazz: callerObjClazz, + eventHandlerClazz: eventHandlerClazz, + callerObjName: callerObjName, + functionName: unregisterFunctionName, + eventHandlerName: eventHandlerName, + ); + + for (final field in eventHandlerClazz.fields) { + StringBuffer bodyBuffer = StringBuffer(); + + final functionParamsList = field.type.parameters + .map((t) => '${t.type.type} ${t.name}') + .join(', '); + + String eventName = field.name; + + if (skipMemberFunctions.contains(eventName)) { + continue; + } + + StringBuffer jsonBuffer = StringBuffer(); + StringBuffer pb = StringBuffer(); + _createParameterInitializedList( + parseResult, pb, field.type.parameters, []); + + jsonBuffer.writeln('final eventJson = {'); + // for (final parameter in field.type.parameters) { + // if (parameter.isPrimitiveType) { + // final parameterType = getParamType(parameter); + + // if (parameterType == 'Uint8List') { + // jsonBuffer + // .writeln('\'${parameter.name}\': ${parameter.name}.toList(),'); + // } else { + // jsonBuffer.writeln('\'${parameter.name}\': ${parameter.name},'); + // } + // } else { + // final bool isEnum = parseResult.hasEnum(parameter.type.type); + // if (isEnum) { + // jsonBuffer + // .writeln('\'${parameter.name}\': ${parameter.name}.value(),'); + // } else { + // final parameterClass = parseResult.getClazz(parameter.type.type)[0]; + // if (parameterClass.constructors.isEmpty) { + // continue; + // } + // jsonBuffer + // .writeln('\'${parameter.name}\': ${parameter.name}.toJson(),'); + // } + // } + // } + jsonBuffer.writeln('};'); + + final eventCompleterName = '${field.name}Completer'; + StringBuffer fireEventImplBuffer = StringBuffer(); + + String fireEventSuffix = eventName; + if (isUpperFirstCaseOfEventName) { + fireEventSuffix = + '${fireEventSuffix[0].toUpperCase()}${fireEventSuffix.substring(1)}'; + } + + final event = '${eventHandlerClazz.name}_$fireEventSuffix'; + fireEventImplBuffer.writeln(''' +{ + ${pb.toString()} + ${jsonBuffer.toString()} + final eventIds = eventIdsMapping['$event'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!$eventCompleterName.isCompleted) { + $eventCompleterName.complete(true); + } + } + } +} +'''); + + bodyBuffer.writeln(''' +final $eventCompleterName = Completer(); +final $eventHandlerName = ${eventHandlerClazz.name}( + $eventName: ($functionParamsList) { + $eventCompleterName.complete(true); + }, +); + +$registerFunctionImpl + +// Delay 500 milliseconds to ensure the $registerFunctionName call completed. +await Future.delayed(const Duration(milliseconds: 500)); + +${fireEventImplBuffer.toString()} + +final eventCalled = await $eventCompleterName.future; +expect(eventCalled, isTrue); + +{ + $unregisterFunctionImpl +} +// Delay 500 milliseconds to ensure the $unregisterFunctionName call completed. +await Future.delayed(const Duration(milliseconds: 500)); +'''); + + String testCase = testCasesContentTemplate.replaceAll( + '{{TEST_CASE_NAME}}', '${eventHandlerClazz.name}.$eventName'); + testCase = + testCase.replaceAll('{{TEST_CASE_BODY}}', bodyBuffer.toString()); + + testCases.add(testCase); + } + + final output = testCaseTemplate.replaceAll( + '{{TEST_CASES_CONTENT}}', + testCases.join('\n'), + ); + + return DartFormatter().format(output); + } + + String _getMethodCallImpl({ + required ParseResult parseResult, + required Clazz callerObjClazz, + required Clazz eventHandlerClazz, + required String callerObjName, + required String functionName, + required String eventHandlerName, + }) { + StringBuffer methodCallBuilder = StringBuffer(); + + for (final method in callerObjClazz.methods) { + final methodName = method.name; + + if (functionName == methodName) { + StringBuffer pb = StringBuffer(); + + _createParameterInitializedList( + parseResult, pb, method.parameters, [eventHandlerClazz.name]); + + methodCallBuilder.writeln(pb.toString()); + bool isFuture = method.returnType.type == 'Future'; + methodCallBuilder + .write('${isFuture ? 'await ' : ''}$callerObjName.$methodName('); + for (final parameter in method.parameters) { + final pn = parameter.type.type == eventHandlerClazz.name + ? eventHandlerName + : parameter.name; + if (parameter.isNamed) { + methodCallBuilder.write('${parameter.name}:$pn,'); + } else { + methodCallBuilder.write('$pn, '); + } + } + methodCallBuilder.write(');'); + + break; + } + } + + return methodCallBuilder.toString(); + } + + String _createParameterInitializedList( + ParseResult parseResult, + StringBuffer pb, + List parameters, + List skipTypes, + ) { + for (final parameter in parameters) { + if (parameter.type.type == 'Function') { + continue; + } + if (skipTypes.contains(parameter.type.type)) { + continue; + } + + if (parameter.isPrimitiveType) { + final parameterType = getParamType(parameter); + if (parameterType == 'Uint8List') { + pb.writeln( + '${getParamType(parameter)} ${parameter.name} = ${defualtValueOfType(parameter.type)};'); + } else { + if (parameterType.startsWith('List') && + parameter.type.typeArguments.isNotEmpty) { + final listBuilderBlock = + createListBuilderBlockForList(parseResult, parameter); + pb.writeln(listBuilderBlock); + } else { + pb.writeln( + '${getParamType(parameter)} ${parameter.name} = ${defualtValueOfType(parameter.type)};'); + } + } + } else { + createConstructorInitializerForMethodParameter( + parseResult, null, parameter, pb); + } + } + + return pb.toString(); + } + + String generateWithTemplate({ + required ParseResult parseResult, + required Clazz clazz, + required String testCaseTemplate, + required String testCasesContentTemplate, + required String methodInvokeObjectName, + required List configs, + List? supportedPlatformsOverride, + List skipMemberFunctions = const [], + required TemplatedTestCase templated, + String Function( + TemplatedGenerator templatedGenerator, + ParseResult parseResult, + Clazz clazz, + Method method, + CustomTemplatedTestCase template)? + customMethodCodeGenerator, + }) { + final testCases = []; + for (final method in clazz.methods) { + final methodName = method.name; + + if (skipMemberFunctions.contains(methodName)) { + continue; + } + + final config = getConfig(configs, methodName); + if (config?.donotGenerate == true) continue; + if (methodName.startsWith('_')) continue; + if (methodName.startsWith('create')) continue; + + StringBuffer pb = StringBuffer(); + + if (customMethodCodeGenerator != null) { + final out = customMethodCodeGenerator(this, parseResult, clazz, method, + templated as CustomTemplatedTestCase); + pb.writeln(out); + } else { + for (final parameter in method.parameters) { + if (parameter.type.type == 'Function') { + continue; + } + if (parameter.isPrimitiveType) { + final parameterType = getParamType(parameter); + + if (parameterType == 'Uint8List') { + pb.writeln( + '${getParamType(parameter)} ${parameter.name} = ${defualtValueOfType(parameter.type)};'); + } else { + if (parameterType.startsWith('List') && + parameter.type.typeArguments.isNotEmpty) { + final listBuilderBlock = + createListBuilderBlockForList(parseResult, parameter); + pb.writeln(listBuilderBlock); + } else { + pb.writeln( + '${getParamType(parameter)} ${parameter.name} = ${defualtValueOfType(parameter.type)};'); + } + } + } else { + createConstructorInitializerForMethodParameter( + parseResult, null, parameter, pb); + } + } + + StringBuffer methodCallBuilder = StringBuffer(); + bool isFuture = method.returnType.type == 'Future'; + // methodCallBuilder.write('await screenShareHelper.$methodName('); + methodCallBuilder.write( + '${isFuture ? 'await ' : ''}$methodInvokeObjectName.$methodName('); + for (final parameter in method.parameters) { + if (parameter.isNamed) { + methodCallBuilder.write('${parameter.name}:${parameter.name},'); + } else { + methodCallBuilder.write('${parameter.name}, '); + } + } + methodCallBuilder.write(');'); + + pb.writeln(methodCallBuilder.toString()); + } + + String skipExpression = 'false'; + + if (supportedPlatformsOverride != null) { + // skipExpression = + // '!(${desktopPlatforms.map((e) => e.toPlatformExpression()).join(' || ')})'; + skipExpression = + '!(${supportedPlatformsOverride.map((e) => e.toPlatformExpression()).join(' || ')})'; + } else { + if (config != null && + config.supportedPlatforms.length < + GeneratorConfigPlatform.values.length) { + skipExpression = + '!(${config.supportedPlatforms.map((e) => e.toPlatformExpression()).join(' || ')})'; + } + } + + String testCase = testCaseTemplate.replaceAll( + '{{TEST_CASE_NAME}}', '${clazz.name}.$methodName'); + testCase = testCase.replaceAll('{{TEST_CASE_BODY}}', pb.toString()); + testCase = testCase.replaceAll('{{TEST_CASE_SKIP}}', skipExpression); + testCases.add(testCase); + } + + final output = testCasesContentTemplate.replaceAll( + '{{TEST_CASES_CONTENT}}', + testCases.join('\n'), + ); + + return output; + } +} diff --git a/scripts/testcase_gen/pubspec.yaml b/scripts/testcase_gen/pubspec.yaml new file mode 100644 index 0000000..a609ddd --- /dev/null +++ b/scripts/testcase_gen/pubspec.yaml @@ -0,0 +1,22 @@ +name: testcase_gen +description: A simple command-line application. +version: 1.0.0 +# homepage: https://www.example.com + +environment: + sdk: '>=3.0.0 <4.0.0' + +publish_to: none + +dependencies: + paraphrase: + git: + url: https://github.com/littleGnAl/paraphrase.git + ref: main + + args: ^2.3.1 + + dart_style: ^2.2.1 + +dev_dependencies: + lints: ^1.0.0 diff --git a/test_shard/integration_test_app/.gitignore b/test_shard/integration_test_app/.gitignore new file mode 100644 index 0000000..9c6177d --- /dev/null +++ b/test_shard/integration_test_app/.gitignore @@ -0,0 +1,52 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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 +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +pubspec.lock + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +macos/Flutter/GeneratedPluginRegistrant.swift +windows/flutter/generated_plugin_registrant.cc diff --git a/test_shard/integration_test_app/.metadata b/test_shard/integration_test_app/.metadata new file mode 100644 index 0000000..b606793 --- /dev/null +++ b/test_shard/integration_test_app/.metadata @@ -0,0 +1,42 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: android + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: ios + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: macos + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: web + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: windows + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + + # 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/test_shard/integration_test_app/README.md b/test_shard/integration_test_app/README.md new file mode 100644 index 0000000..8d5f0df --- /dev/null +++ b/test_shard/integration_test_app/README.md @@ -0,0 +1,3 @@ +# integration_test_app + +Integration test cases. \ No newline at end of file diff --git a/test_shard/integration_test_app/analysis_options.yaml b/test_shard/integration_test_app/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/test_shard/integration_test_app/analysis_options.yaml @@ -0,0 +1,29 @@ +# 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 + +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-lang.github.io/linter/lints/index.html. + # + # 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/test_shard/integration_test_app/android/.gitignore b/test_shard/integration_test_app/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/test_shard/integration_test_app/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/test_shard/integration_test_app/android/app/build.gradle b/test_shard/integration_test_app/android/app/build.gradle new file mode 100644 index 0000000..486f3e4 --- /dev/null +++ b/test_shard/integration_test_app/android/app/build.gradle @@ -0,0 +1,74 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 34 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "io.agora.integration_test_app.integration_test_app" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + def devFile = project.file("${rootProject.projectDir.getParentFile().parentFile.parentFile}/android/.plugin_dev") + if (devFile.exists()) { + implementation fileTree(dir: "${rootProject.projectDir.getParentFile().parentFile.parentFile}/android/libs", include: ["*.aar"]) + } + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/test_shard/integration_test_app/android/app/src/debug/AndroidManifest.xml b/test_shard/integration_test_app/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..1f6892e --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/test_shard/integration_test_app/android/app/src/main/AndroidManifest.xml b/test_shard/integration_test_app/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8331f1f --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_app/android/app/src/main/kotlin/io/agora/integration_test_app/integration_test_app/MainActivity.kt b/test_shard/integration_test_app/android/app/src/main/kotlin/io/agora/integration_test_app/integration_test_app/MainActivity.kt new file mode 100644 index 0000000..dc7556c --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/main/kotlin/io/agora/integration_test_app/integration_test_app/MainActivity.kt @@ -0,0 +1,6 @@ +package io.agora.integration_test_app.integration_test_app + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/test_shard/integration_test_app/android/app/src/main/res/drawable-v21/launch_background.xml b/test_shard/integration_test_app/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/test_shard/integration_test_app/android/app/src/main/res/drawable/launch_background.xml b/test_shard/integration_test_app/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/test_shard/integration_test_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/test_shard/integration_test_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/test_shard/integration_test_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/test_shard/integration_test_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/test_shard/integration_test_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/test_shard/integration_test_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/test_shard/integration_test_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/test_shard/integration_test_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/test_shard/integration_test_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/test_shard/integration_test_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/test_shard/integration_test_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/test_shard/integration_test_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/test_shard/integration_test_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/test_shard/integration_test_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/test_shard/integration_test_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/test_shard/integration_test_app/android/app/src/main/res/values-night/styles.xml b/test_shard/integration_test_app/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/test_shard/integration_test_app/android/app/src/main/res/values/styles.xml b/test_shard/integration_test_app/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/test_shard/integration_test_app/android/app/src/profile/AndroidManifest.xml b/test_shard/integration_test_app/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..1f6892e --- /dev/null +++ b/test_shard/integration_test_app/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/test_shard/integration_test_app/android/build.gradle b/test_shard/integration_test_app/android/build.gradle new file mode 100644 index 0000000..6617c1e --- /dev/null +++ b/test_shard/integration_test_app/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.0' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/test_shard/integration_test_app/android/gradle.properties b/test_shard/integration_test_app/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/test_shard/integration_test_app/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/test_shard/integration_test_app/android/gradle/wrapper/gradle-wrapper.properties b/test_shard/integration_test_app/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cc5527d --- /dev/null +++ b/test_shard/integration_test_app/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/test_shard/integration_test_app/android/settings.gradle b/test_shard/integration_test_app/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/test_shard/integration_test_app/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/test_shard/integration_test_app/assets/Agora.io-Interactions.mp3 b/test_shard/integration_test_app/assets/Agora.io-Interactions.mp3 new file mode 100644 index 0000000..337cdeb Binary files /dev/null and b/test_shard/integration_test_app/assets/Agora.io-Interactions.mp3 differ diff --git a/test_shard/integration_test_app/assets/agora-logo.png b/test_shard/integration_test_app/assets/agora-logo.png new file mode 100644 index 0000000..0b93e8b Binary files /dev/null and b/test_shard/integration_test_app/assets/agora-logo.png differ diff --git a/test_shard/integration_test_app/integration_test/apis_call_fake_test.dart b/test_shard/integration_test_app/integration_test/apis_call_fake_test.dart new file mode 100644 index 0000000..85e64a9 --- /dev/null +++ b/test_shard/integration_test_app/integration_test/apis_call_fake_test.dart @@ -0,0 +1,66 @@ +// import 'dart:async'; + +// import 'package:flutter/foundation.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:integration_test/integration_test.dart'; + +// import 'package:iris_tester/iris_tester.dart'; +// import 'package:iris_method_channel/iris_method_channel.dart'; + +// // import 'generated/rtmclient_fake_test.generated.dart' as rtmclient_fake_test; +// import 'generated/rtmlock_fake_test.generated.dart' as rtmlock_fake_test; +// import 'generated/rtmpresence_fake_test.generated.dart' +// as rtmpresence_fake_test; +// import 'generated/rtmstorage_fake_test.generated.dart' as rtmstorage_fake_test; +// import 'generated/streamchannel_fake_test.generated.dart' +// as streamchannel_fake_test; + +// import 'package:agora_rtm/src/impl/agora_rtm_client_impl_override.dart' +// as rtm_client_impl_override; + +// class TestInitilizationArgProvider implements InitilizationArgProvider { +// TestInitilizationArgProvider(this.testerArgs); +// TestInitilizationArgProvider.fromValue(IrisHandle this.value) +// : testerArgs = []; +// final List testerArgs; +// IrisHandle? value; +// @override +// IrisHandle provide(IrisApiEngineHandle apiEngineHandle) { +// return value ?? ObjectIrisHandle(testerArgs[0](apiEngineHandle())); +// } +// } + +// void main() { +// IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + +// IrisTester? irisTester; + +// setUp(() { +// irisTester = createIrisTester(); +// irisTester!.initialize(); +// if (kIsWeb) { +// rtm_client_impl_override.setMockSharedNativeHandleProvider( +// TestInitilizationArgProvider(irisTester!.getTesterArgs())); +// } else { +// // On IO, the function return from the `irisTester.getTesterArgs()` capture +// // the `Pointer` from `IrisTester`, which is invalid to pass to the `Isolate`, +// // so directly pass the `ObjectIrisHandle` as value to the `setMockSharedNativeHandleProvider` +// final value = +// irisTester!.getTesterArgs()[0](const IrisApiEngineHandle(0)); +// rtm_client_impl_override.setMockSharedNativeHandleProvider( +// TestInitilizationArgProvider.fromValue(ObjectIrisHandle(value))); +// } +// }); + +// tearDown(() { +// rtm_client_impl_override.setMockSharedNativeHandleProvider(null); +// irisTester!.dispose(); +// irisTester = null; +// }); + +// rtmclient_fake_test.testCases(); +// rtmlock_fake_test.testCases(); +// rtmpresence_fake_test.testCases(); +// rtmstorage_fake_test.testCases(); +// streamchannel_fake_test.testCases(); +// } diff --git a/test_shard/integration_test_app/integration_test/binding_apis_call_fake_test.dart b/test_shard/integration_test_app/integration_test/binding_apis_call_fake_test.dart new file mode 100644 index 0000000..149b058 --- /dev/null +++ b/test_shard/integration_test_app/integration_test/binding_apis_call_fake_test.dart @@ -0,0 +1,81 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:iris_tester/iris_tester.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +import 'generated/bindings/rtmclient_binding_fake_test.generated.dart' + as rtmclient_binding_fake_test; +import 'generated/bindings/rtmlock_binding_fake_test.generated.dart' + as rtmlock_binding_fake_test; +import 'generated/bindings/rtmpresence_binding_fake_test.generated.dart' + as rtmpresence_binding_fake_test; +import 'generated/bindings/rtmstorage_binding_fake_test.generated.dart' + as rtmstorage_binding_fake_test; +import 'generated/bindings/streamchannel_binding_fake_test.generated.dart' + as streamchannel_binding_fake_test; +// import 'generated/bindings/rtmclient_rtmeventhandler_testcases.generated.dart' +// as rtmclient_rtmeventhandler_testcases; + +import 'package:agora_rtm/src/impl/agora_rtm_client_impl_override.dart' + as rtm_client_impl_override; + +class TestInitilizationArgProvider implements InitilizationArgProvider { + TestInitilizationArgProvider(this.testerArgs); + TestInitilizationArgProvider.fromValue(IrisHandle this.value) + : testerArgs = []; + final List testerArgs; + IrisHandle? value; + @override + IrisHandle provide(IrisApiEngineHandle apiEngineHandle) { + return value ?? ObjectIrisHandle(testerArgs[0](apiEngineHandle())); + } +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + IrisTester? irisTester; + + List irisMethodChannelInitilizationArgs = []; + + setUp(() { + irisTester = createIrisTester(); + irisTester!.initialize(); + if (kIsWeb) { + rtm_client_impl_override.setMockSharedNativeHandleProvider( + TestInitilizationArgProvider(irisTester!.getTesterArgs())); + } else { + // On IO, the function return from the `irisTester.getTesterArgs()` capture + // the `Pointer` from `IrisTester`, which is invalid to pass to the `Isolate`, + // so directly pass the `ObjectIrisHandle` as value to the `setMockSharedNativeHandleProvider` + final value = + irisTester!.getTesterArgs()[0](const IrisApiEngineHandle(0)); + // rtm_client_impl_override.setMockSharedNativeHandleProvider( + // TestInitilizationArgProvider.fromValue(ObjectIrisHandle(value))); + + irisMethodChannelInitilizationArgs = [ + TestInitilizationArgProvider.fromValue(ObjectIrisHandle(value)) + ]; + } + }); + + tearDown(() { + rtm_client_impl_override.setMockSharedNativeHandleProvider(null); + irisTester!.dispose(); + irisTester = null; + }); + + rtmclient_binding_fake_test + .testCases(() => irisMethodChannelInitilizationArgs); + rtmlock_binding_fake_test.testCases(() => irisMethodChannelInitilizationArgs); + rtmpresence_binding_fake_test + .testCases(() => irisMethodChannelInitilizationArgs); + rtmstorage_binding_fake_test + .testCases(() => irisMethodChannelInitilizationArgs); + streamchannel_binding_fake_test + .testCases(() => irisMethodChannelInitilizationArgs); + // rtmclient_rtmeventhandler_testcases.testCases( + // () => irisTester!, () => irisMethodChannelInitilizationArgs); +} diff --git a/test_shard/integration_test_app/integration_test/generated/bindings/event_ids_mapping_gen.dart b/test_shard/integration_test_app/integration_test/generated/bindings/event_ids_mapping_gen.dart new file mode 100644 index 0000000..dc008f7 --- /dev/null +++ b/test_shard/integration_test_app/integration_test/generated/bindings/event_ids_mapping_gen.dart @@ -0,0 +1,120 @@ +/// Event Ids mapping of iris api id. +const eventIdsMapping = { + "RtmEventHandler_onLinkStateEvent": [ + "RtmEventHandler_onLinkStateEvent_6d5317f" + ], + "RtmEventHandler_onMessageEvent": ["RtmEventHandler_onMessageEvent_ce5f8f5"], + "RtmEventHandler_onPresenceEvent": [ + "RtmEventHandler_onPresenceEvent_32b5db1" + ], + "RtmEventHandler_onTopicEvent": ["RtmEventHandler_onTopicEvent_b4e4bd7"], + "RtmEventHandler_onLockEvent": ["RtmEventHandler_onLockEvent_5e7fbef"], + "RtmEventHandler_onStorageEvent": ["RtmEventHandler_onStorageEvent_4304228"], + "RtmEventHandler_onJoinResult": ["RtmEventHandler_onJoinResult_ce14e01"], + "RtmEventHandler_onLeaveResult": ["RtmEventHandler_onLeaveResult_ce14e01"], + "RtmEventHandler_onPublishTopicMessageResult": [ + "RtmEventHandler_onPublishTopicMessageResult_ce14e01" + ], + "RtmEventHandler_onJoinTopicResult": [ + "RtmEventHandler_onJoinTopicResult_bfd472e" + ], + "RtmEventHandler_onLeaveTopicResult": [ + "RtmEventHandler_onLeaveTopicResult_bfd472e" + ], + "RtmEventHandler_onSubscribeTopicResult": [ + "RtmEventHandler_onSubscribeTopicResult_ae281ad" + ], + "RtmEventHandler_onUnsubscribeTopicResult": [ + "RtmEventHandler_onUnsubscribeTopicResult_ce14e01" + ], + "RtmEventHandler_onGetSubscribedUserListResult": [ + "RtmEventHandler_onGetSubscribedUserListResult_79f7ec8" + ], + "RtmEventHandler_onConnectionStateChanged": [ + "RtmEventHandler_onConnectionStateChanged_ac0fd53" + ], + "RtmEventHandler_onTokenPrivilegeWillExpire": [ + "RtmEventHandler_onTokenPrivilegeWillExpire_3a2037f" + ], + "RtmEventHandler_onSubscribeResult": [ + "RtmEventHandler_onSubscribeResult_edf84fc" + ], + "RtmEventHandler_onUnsubscribeResult": [ + "RtmEventHandler_onUnsubscribeResult_edf84fc" + ], + "RtmEventHandler_onPublishResult": [ + "RtmEventHandler_onPublishResult_4f46899" + ], + "RtmEventHandler_onLoginResult": ["RtmEventHandler_onLoginResult_4f46899"], + "RtmEventHandler_onLogoutResult": ["RtmEventHandler_onLogoutResult_4f46899"], + "RtmEventHandler_onRenewTokenResult": [ + "RtmEventHandler_onRenewTokenResult_e9203be" + ], + "RtmEventHandler_onSetChannelMetadataResult": [ + "RtmEventHandler_onSetChannelMetadataResult_7cb1a6e" + ], + "RtmEventHandler_onUpdateChannelMetadataResult": [ + "RtmEventHandler_onUpdateChannelMetadataResult_7cb1a6e" + ], + "RtmEventHandler_onRemoveChannelMetadataResult": [ + "RtmEventHandler_onRemoveChannelMetadataResult_7cb1a6e" + ], + "RtmEventHandler_onGetChannelMetadataResult": [ + "RtmEventHandler_onGetChannelMetadataResult_d105bb5" + ], + "RtmEventHandler_onSetUserMetadataResult": [ + "RtmEventHandler_onSetUserMetadataResult_edf84fc" + ], + "RtmEventHandler_onUpdateUserMetadataResult": [ + "RtmEventHandler_onUpdateUserMetadataResult_edf84fc" + ], + "RtmEventHandler_onRemoveUserMetadataResult": [ + "RtmEventHandler_onRemoveUserMetadataResult_edf84fc" + ], + "RtmEventHandler_onGetUserMetadataResult": [ + "RtmEventHandler_onGetUserMetadataResult_7065706" + ], + "RtmEventHandler_onSubscribeUserMetadataResult": [ + "RtmEventHandler_onSubscribeUserMetadataResult_edf84fc" + ], + "RtmEventHandler_onUnsubscribeUserMetadataResult": [ + "RtmEventHandler_onUnsubscribeUserMetadataResult_edf84fc" + ], + "RtmEventHandler_onSetLockResult": [ + "RtmEventHandler_onSetLockResult_01034fa" + ], + "RtmEventHandler_onRemoveLockResult": [ + "RtmEventHandler_onRemoveLockResult_01034fa" + ], + "RtmEventHandler_onReleaseLockResult": [ + "RtmEventHandler_onReleaseLockResult_01034fa" + ], + "RtmEventHandler_onAcquireLockResult": [ + "RtmEventHandler_onAcquireLockResult_aad7ec0" + ], + "RtmEventHandler_onRevokeLockResult": [ + "RtmEventHandler_onRevokeLockResult_01034fa" + ], + "RtmEventHandler_onGetLocksResult": [ + "RtmEventHandler_onGetLocksResult_6e98f5f" + ], + "RtmEventHandler_onWhoNowResult": ["RtmEventHandler_onWhoNowResult_b070777"], + "RtmEventHandler_onGetOnlineUsersResult": [ + "RtmEventHandler_onGetOnlineUsersResult_b070777" + ], + "RtmEventHandler_onWhereNowResult": [ + "RtmEventHandler_onWhereNowResult_ae64023" + ], + "RtmEventHandler_onGetUserChannelsResult": [ + "RtmEventHandler_onGetUserChannelsResult_ae64023" + ], + "RtmEventHandler_onPresenceSetStateResult": [ + "RtmEventHandler_onPresenceSetStateResult_4f46899" + ], + "RtmEventHandler_onPresenceRemoveStateResult": [ + "RtmEventHandler_onPresenceRemoveStateResult_4f46899" + ], + "RtmEventHandler_onPresenceGetStateResult": [ + "RtmEventHandler_onPresenceGetStateResult_3d764cc" + ] +}; diff --git a/test_shard/integration_test_app/integration_test/generated/bindings/rtmclient_binding_fake_test.generated.dart b/test_shard/integration_test_app/integration_test/generated/bindings/rtmclient_binding_fake_test.generated.dart new file mode 100644 index 0000000..dbcdf4d --- /dev/null +++ b/test_shard/integration_test_app/integration_test/generated/bindings/rtmclient_binding_fake_test.generated.dart @@ -0,0 +1,311 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> + irisMethodChannelInitilizationArgs) { + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + testWidgets( + 'RtmClient.release', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + await rtmClient.release(); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.release] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.login', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + String token = "hello"; + await rtmClient.login( + token, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.login] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.logout', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + await rtmClient.logout(); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.logout] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.renewToken', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + String token = "hello"; + await rtmClient.renewToken( + token, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.renewToken] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.publish', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + String channelName = "hello"; + String message = "hello"; + int length = 5; + RtmChannelType optionChannelType = RtmChannelType.none; + RtmMessageType optionMessageType = RtmMessageType.binary; + String optionCustomType = "hello"; + PublishOptions option = PublishOptions( + channelType: optionChannelType, + messageType: optionMessageType, + customType: optionCustomType, + ); + await rtmClient.publish( + channelName: channelName, + message: message, + length: length, + option: option, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.publish] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.subscribe', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + String channelName = "hello"; + bool optionsWithMessage = true; + bool optionsWithMetadata = true; + bool optionsWithPresence = true; + bool optionsWithLock = true; + bool optionsBeQuiet = true; + SubscribeOptions options = SubscribeOptions( + withMessage: optionsWithMessage, + withMetadata: optionsWithMetadata, + withPresence: optionsWithPresence, + withLock: optionsWithLock, + beQuiet: optionsBeQuiet, + ); + await rtmClient.subscribe( + channelName: channelName, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.subscribe] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.unsubscribe', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + String channelName = "hello"; + await rtmClient.unsubscribe( + channelName, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.unsubscribe] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.setParameters', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + String parameters = "hello"; + await rtmClient.setParameters( + parameters, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.setParameters] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmClient.publishBinaryMessage', + (WidgetTester tester) async { + RtmClient rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + + try { + String channelName = "hello"; + Uint8List message = Uint8List.fromList([1, 1, 1, 1, 1]); + int length = 5; + RtmChannelType optionChannelType = RtmChannelType.none; + RtmMessageType optionMessageType = RtmMessageType.binary; + String optionCustomType = "hello"; + PublishOptions option = PublishOptions( + channelType: optionChannelType, + messageType: optionMessageType, + customType: optionCustomType, + ); + await rtmClient.publishBinaryMessage( + channelName: channelName, + message: message, + length: length, + option: option, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmClient.publishBinaryMessage] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); +} diff --git a/test_shard/integration_test_app/integration_test/generated/bindings/rtmclient_rtmeventhandler_testcases.generated.dart b/test_shard/integration_test_app/integration_test/generated/bindings/rtmclient_rtmeventhandler_testcases.generated.dart new file mode 100644 index 0000000..921896b --- /dev/null +++ b/test_shard/integration_test_app/integration_test/generated/bindings/rtmclient_rtmeventhandler_testcases.generated.dart @@ -0,0 +1,2522 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; +import 'event_ids_mapping_gen.dart'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:iris_tester/iris_tester.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter irisTester, + ValueGetter> + irisMethodChannelInitilizationArgs) { + Future _createBindingRtmClient( + RtmEventHandler rtmEventHandler) async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + testWidgets( + 'RtmEventHandler.onLinkStateEvent', + (WidgetTester tester) async { + final onLinkStateEventCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onLinkStateEvent: (LinkStateEvent event) { + onLinkStateEventCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + RtmLinkState eventCurrentState = RtmLinkState.idle; + RtmLinkState eventPreviousState = RtmLinkState.idle; + RtmServiceType eventServiceType = RtmServiceType.none; + RtmLinkOperation eventOperation = RtmLinkOperation.login; + String eventReason = "hello"; + List eventAffectedChannels = List.filled(5, "hello"); + List eventUnrestoredChannels = List.filled(5, "hello"); + bool eventIsResumed = true; + int eventTimestamp = 5; + LinkStateEvent event = LinkStateEvent( + currentState: eventCurrentState, + previousState: eventPreviousState, + serviceType: eventServiceType, + operation: eventOperation, + reason: eventReason, + affectedChannels: eventAffectedChannels, + unrestoredChannels: eventUnrestoredChannels, + isResumed: eventIsResumed, + timestamp: eventTimestamp, + ); + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onLinkStateEvent'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onLinkStateEventCompleter.isCompleted) { + onLinkStateEventCompleter.complete(true); + } + } + } + } + + final eventCalled = await onLinkStateEventCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onMessageEvent', + (WidgetTester tester) async { + final onMessageEventCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onMessageEvent: (MessageEvent event) { + onMessageEventCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + RtmChannelType eventChannelType = RtmChannelType.none; + RtmMessageType eventMessageType = RtmMessageType.binary; + String eventChannelName = "hello"; + String eventChannelTopic = "hello"; + Uint8List eventMessage = Uint8List.fromList([1, 1, 1, 1, 1]); + int eventMessageLength = 5; + String eventPublisher = "hello"; + String eventCustomType = "hello"; + int eventTimestamp = 5; + MessageEvent event = MessageEvent( + channelType: eventChannelType, + messageType: eventMessageType, + channelName: eventChannelName, + channelTopic: eventChannelTopic, + message: eventMessage, + messageLength: eventMessageLength, + publisher: eventPublisher, + customType: eventCustomType, + timestamp: eventTimestamp, + ); + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onMessageEvent'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onMessageEventCompleter.isCompleted) { + onMessageEventCompleter.complete(true); + } + } + } + } + + final eventCalled = await onMessageEventCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onPresenceEvent', + (WidgetTester tester) async { + final onPresenceEventCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onPresenceEvent: (PresenceEvent event) { + onPresenceEventCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + RtmPresenceEventType eventType = RtmPresenceEventType.none; + RtmChannelType eventChannelType = RtmChannelType.none; + List joinUserListUsers = List.filled(5, "hello"); + UserList intervalJoinUserList = UserList( + users: joinUserListUsers, + ); + List leaveUserListUsers = List.filled(5, "hello"); + UserList intervalLeaveUserList = UserList( + users: leaveUserListUsers, + ); + List timeoutUserListUsers = List.filled(5, "hello"); + UserList intervalTimeoutUserList = UserList( + users: timeoutUserListUsers, + ); + List intervalUserStateList = []; + IntervalInfo eventInterval = IntervalInfo( + joinUserList: intervalJoinUserList, + leaveUserList: intervalLeaveUserList, + timeoutUserList: intervalTimeoutUserList, + userStateList: intervalUserStateList, + ); + List snapshotUserStateList = []; + SnapshotInfo eventSnapshot = SnapshotInfo( + userStateList: snapshotUserStateList, + ); + String eventChannelName = "hello"; + String eventPublisher = "hello"; + List eventStateItems = []; + int eventTimestamp = 5; + PresenceEvent event = PresenceEvent( + type: eventType, + channelType: eventChannelType, + channelName: eventChannelName, + publisher: eventPublisher, + stateItems: eventStateItems, + interval: eventInterval, + snapshot: eventSnapshot, + timestamp: eventTimestamp, + ); + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onPresenceEvent'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onPresenceEventCompleter.isCompleted) { + onPresenceEventCompleter.complete(true); + } + } + } + } + + final eventCalled = await onPresenceEventCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onTopicEvent', + (WidgetTester tester) async { + final onTopicEventCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onTopicEvent: (TopicEvent event) { + onTopicEventCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + RtmTopicEventType eventType = RtmTopicEventType.none; + String eventChannelName = "hello"; + String eventPublisher = "hello"; + List eventTopicInfos = []; + int eventTimestamp = 5; + TopicEvent event = TopicEvent( + type: eventType, + channelName: eventChannelName, + publisher: eventPublisher, + topicInfos: eventTopicInfos, + timestamp: eventTimestamp, + ); + + final eventJson = {}; + + final eventIds = eventIdsMapping['RtmEventHandler_onTopicEvent'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onTopicEventCompleter.isCompleted) { + onTopicEventCompleter.complete(true); + } + } + } + } + + final eventCalled = await onTopicEventCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onLockEvent', + (WidgetTester tester) async { + final onLockEventCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onLockEvent: (LockEvent event) { + onLockEventCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + RtmChannelType eventChannelType = RtmChannelType.none; + RtmLockEventType eventEventType = RtmLockEventType.none; + String eventChannelName = "hello"; + List eventLockDetailList = []; + int eventTimestamp = 5; + LockEvent event = LockEvent( + channelType: eventChannelType, + eventType: eventEventType, + channelName: eventChannelName, + lockDetailList: eventLockDetailList, + timestamp: eventTimestamp, + ); + + final eventJson = {}; + + final eventIds = eventIdsMapping['RtmEventHandler_onLockEvent'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onLockEventCompleter.isCompleted) { + onLockEventCompleter.complete(true); + } + } + } + } + + final eventCalled = await onLockEventCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onStorageEvent', + (WidgetTester tester) async { + final onStorageEventCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onStorageEvent: (StorageEvent event) { + onStorageEventCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + RtmChannelType eventChannelType = RtmChannelType.none; + RtmStorageType eventStorageType = RtmStorageType.none; + RtmStorageEventType eventEventType = RtmStorageEventType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata eventData = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + String eventTarget = "hello"; + int eventTimestamp = 5; + StorageEvent event = StorageEvent( + channelType: eventChannelType, + storageType: eventStorageType, + eventType: eventEventType, + target: eventTarget, + data: eventData, + timestamp: eventTimestamp, + ); + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onStorageEvent'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onStorageEventCompleter.isCompleted) { + onStorageEventCompleter.complete(true); + } + } + } + } + + final eventCalled = await onStorageEventCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onJoinResult', + (WidgetTester tester) async { + final onJoinResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onJoinResult: (int requestId, String channelName, String userId, + RtmErrorCode errorCode) { + onJoinResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String userId = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = eventIdsMapping['RtmEventHandler_onJoinResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onJoinResultCompleter.isCompleted) { + onJoinResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onJoinResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onLeaveResult', + (WidgetTester tester) async { + final onLeaveResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onLeaveResult: (int requestId, String channelName, String userId, + RtmErrorCode errorCode) { + onLeaveResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String userId = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = eventIdsMapping['RtmEventHandler_onLeaveResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onLeaveResultCompleter.isCompleted) { + onLeaveResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onLeaveResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onPublishTopicMessageResult', + (WidgetTester tester) async { + final onPublishTopicMessageResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onPublishTopicMessageResult: (int requestId, String channelName, + String topic, RtmErrorCode errorCode) { + onPublishTopicMessageResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String topic = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onPublishTopicMessageResult'] ?? + []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onPublishTopicMessageResultCompleter.isCompleted) { + onPublishTopicMessageResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onPublishTopicMessageResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onJoinTopicResult', + (WidgetTester tester) async { + final onJoinTopicResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onJoinTopicResult: (int requestId, String channelName, String userId, + String topic, String meta, RtmErrorCode errorCode) { + onJoinTopicResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String userId = "hello"; + String topic = "hello"; + String meta = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onJoinTopicResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onJoinTopicResultCompleter.isCompleted) { + onJoinTopicResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onJoinTopicResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onLeaveTopicResult', + (WidgetTester tester) async { + final onLeaveTopicResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onLeaveTopicResult: (int requestId, String channelName, String userId, + String topic, String meta, RtmErrorCode errorCode) { + onLeaveTopicResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String userId = "hello"; + String topic = "hello"; + String meta = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onLeaveTopicResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onLeaveTopicResultCompleter.isCompleted) { + onLeaveTopicResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onLeaveTopicResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onSubscribeTopicResult', + (WidgetTester tester) async { + final onSubscribeTopicResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onSubscribeTopicResult: (int requestId, + String channelName, + String userId, + String topic, + UserList succeedUsers, + UserList failedUsers, + RtmErrorCode errorCode) { + onSubscribeTopicResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String userId = "hello"; + String topic = "hello"; + List succeedUsersUsers = List.filled(5, "hello"); + UserList succeedUsers = UserList( + users: succeedUsersUsers, + ); + List failedUsersUsers = List.filled(5, "hello"); + UserList failedUsers = UserList( + users: failedUsersUsers, + ); + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onSubscribeTopicResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onSubscribeTopicResultCompleter.isCompleted) { + onSubscribeTopicResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onSubscribeTopicResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onUnsubscribeTopicResult', + (WidgetTester tester) async { + final onUnsubscribeTopicResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onUnsubscribeTopicResult: (int requestId, String channelName, + String topic, RtmErrorCode errorCode) { + onUnsubscribeTopicResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String topic = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onUnsubscribeTopicResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onUnsubscribeTopicResultCompleter.isCompleted) { + onUnsubscribeTopicResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onUnsubscribeTopicResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onGetSubscribedUserListResult', + (WidgetTester tester) async { + final onGetSubscribedUserListResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onGetSubscribedUserListResult: (int requestId, String channelName, + String topic, UserList users, RtmErrorCode errorCode) { + onGetSubscribedUserListResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + String topic = "hello"; + List usersUsers = List.filled(5, "hello"); + UserList users = UserList( + users: usersUsers, + ); + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onGetSubscribedUserListResult'] ?? + []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onGetSubscribedUserListResultCompleter.isCompleted) { + onGetSubscribedUserListResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onGetSubscribedUserListResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onConnectionStateChanged', + (WidgetTester tester) async { + final onConnectionStateChangedCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onConnectionStateChanged: (String channelName, RtmConnectionState state, + RtmConnectionChangeReason reason) { + onConnectionStateChangedCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + String channelName = "hello"; + RtmConnectionState state = RtmConnectionState.disconnected; + RtmConnectionChangeReason reason = RtmConnectionChangeReason.connecting; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onConnectionStateChanged'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onConnectionStateChangedCompleter.isCompleted) { + onConnectionStateChangedCompleter.complete(true); + } + } + } + } + + final eventCalled = await onConnectionStateChangedCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onTokenPrivilegeWillExpire', + (WidgetTester tester) async { + final onTokenPrivilegeWillExpireCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onTokenPrivilegeWillExpire: (String channelName) { + onTokenPrivilegeWillExpireCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + String channelName = "hello"; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onTokenPrivilegeWillExpire'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onTokenPrivilegeWillExpireCompleter.isCompleted) { + onTokenPrivilegeWillExpireCompleter.complete(true); + } + } + } + } + + final eventCalled = await onTokenPrivilegeWillExpireCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onSubscribeResult', + (WidgetTester tester) async { + final onSubscribeResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onSubscribeResult: + (int requestId, String channelName, RtmErrorCode errorCode) { + onSubscribeResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onSubscribeResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onSubscribeResultCompleter.isCompleted) { + onSubscribeResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onSubscribeResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onUnsubscribeResult', + (WidgetTester tester) async { + final onUnsubscribeResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onUnsubscribeResult: + (int requestId, String channelName, RtmErrorCode errorCode) { + onUnsubscribeResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onUnsubscribeResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onUnsubscribeResultCompleter.isCompleted) { + onUnsubscribeResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onUnsubscribeResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onPublishResult', + (WidgetTester tester) async { + final onPublishResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onPublishResult: (int requestId, RtmErrorCode errorCode) { + onPublishResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onPublishResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onPublishResultCompleter.isCompleted) { + onPublishResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onPublishResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onLoginResult', + (WidgetTester tester) async { + final onLoginResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onLoginResult: (int requestId, RtmErrorCode errorCode) { + onLoginResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = eventIdsMapping['RtmEventHandler_onLoginResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onLoginResultCompleter.isCompleted) { + onLoginResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onLoginResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onLogoutResult', + (WidgetTester tester) async { + final onLogoutResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onLogoutResult: (int requestId, RtmErrorCode errorCode) { + onLogoutResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onLogoutResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onLogoutResultCompleter.isCompleted) { + onLogoutResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onLogoutResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onRenewTokenResult', + (WidgetTester tester) async { + final onRenewTokenResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onRenewTokenResult: (int requestId, RtmServiceType serverType, + String channelName, RtmErrorCode errorCode) { + onRenewTokenResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + RtmServiceType serverType = RtmServiceType.none; + String channelName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onRenewTokenResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onRenewTokenResultCompleter.isCompleted) { + onRenewTokenResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onRenewTokenResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onSetChannelMetadataResult', + (WidgetTester tester) async { + final onSetChannelMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onSetChannelMetadataResult: (int requestId, String channelName, + RtmChannelType channelType, RtmErrorCode errorCode) { + onSetChannelMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onSetChannelMetadataResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onSetChannelMetadataResultCompleter.isCompleted) { + onSetChannelMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onSetChannelMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onUpdateChannelMetadataResult', + (WidgetTester tester) async { + final onUpdateChannelMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onUpdateChannelMetadataResult: (int requestId, String channelName, + RtmChannelType channelType, RtmErrorCode errorCode) { + onUpdateChannelMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onUpdateChannelMetadataResult'] ?? + []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onUpdateChannelMetadataResultCompleter.isCompleted) { + onUpdateChannelMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onUpdateChannelMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onRemoveChannelMetadataResult', + (WidgetTester tester) async { + final onRemoveChannelMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onRemoveChannelMetadataResult: (int requestId, String channelName, + RtmChannelType channelType, RtmErrorCode errorCode) { + onRemoveChannelMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onRemoveChannelMetadataResult'] ?? + []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onRemoveChannelMetadataResultCompleter.isCompleted) { + onRemoveChannelMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onRemoveChannelMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onGetChannelMetadataResult', + (WidgetTester tester) async { + final onGetChannelMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onGetChannelMetadataResult: (int requestId, String channelName, + RtmChannelType channelType, Metadata data, RtmErrorCode errorCode) { + onGetChannelMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onGetChannelMetadataResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onGetChannelMetadataResultCompleter.isCompleted) { + onGetChannelMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onGetChannelMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onSetUserMetadataResult', + (WidgetTester tester) async { + final onSetUserMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onSetUserMetadataResult: + (int requestId, String userId, RtmErrorCode errorCode) { + onSetUserMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String userId = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onSetUserMetadataResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onSetUserMetadataResultCompleter.isCompleted) { + onSetUserMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onSetUserMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onUpdateUserMetadataResult', + (WidgetTester tester) async { + final onUpdateUserMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onUpdateUserMetadataResult: + (int requestId, String userId, RtmErrorCode errorCode) { + onUpdateUserMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String userId = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onUpdateUserMetadataResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onUpdateUserMetadataResultCompleter.isCompleted) { + onUpdateUserMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onUpdateUserMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onRemoveUserMetadataResult', + (WidgetTester tester) async { + final onRemoveUserMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onRemoveUserMetadataResult: + (int requestId, String userId, RtmErrorCode errorCode) { + onRemoveUserMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String userId = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onRemoveUserMetadataResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onRemoveUserMetadataResultCompleter.isCompleted) { + onRemoveUserMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onRemoveUserMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onGetUserMetadataResult', + (WidgetTester tester) async { + final onGetUserMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onGetUserMetadataResult: (int requestId, String userId, Metadata data, + RtmErrorCode errorCode) { + onGetUserMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String userId = "hello"; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onGetUserMetadataResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onGetUserMetadataResultCompleter.isCompleted) { + onGetUserMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onGetUserMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onSubscribeUserMetadataResult', + (WidgetTester tester) async { + final onSubscribeUserMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onSubscribeUserMetadataResult: + (int requestId, String userId, RtmErrorCode errorCode) { + onSubscribeUserMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String userId = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onSubscribeUserMetadataResult'] ?? + []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onSubscribeUserMetadataResultCompleter.isCompleted) { + onSubscribeUserMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onSubscribeUserMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onUnsubscribeUserMetadataResult', + (WidgetTester tester) async { + final onUnsubscribeUserMetadataResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onUnsubscribeUserMetadataResult: + (int requestId, String userId, RtmErrorCode errorCode) { + onUnsubscribeUserMetadataResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String userId = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = eventIdsMapping[ + 'RtmEventHandler_onUnsubscribeUserMetadataResult'] ?? + []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onUnsubscribeUserMetadataResultCompleter.isCompleted) { + onUnsubscribeUserMetadataResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onUnsubscribeUserMetadataResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onSetLockResult', + (WidgetTester tester) async { + final onSetLockResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onSetLockResult: (int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode) { + onSetLockResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onSetLockResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onSetLockResultCompleter.isCompleted) { + onSetLockResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onSetLockResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onRemoveLockResult', + (WidgetTester tester) async { + final onRemoveLockResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onRemoveLockResult: (int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode) { + onRemoveLockResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onRemoveLockResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onRemoveLockResultCompleter.isCompleted) { + onRemoveLockResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onRemoveLockResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onReleaseLockResult', + (WidgetTester tester) async { + final onReleaseLockResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onReleaseLockResult: (int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode) { + onReleaseLockResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onReleaseLockResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onReleaseLockResultCompleter.isCompleted) { + onReleaseLockResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onReleaseLockResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onAcquireLockResult', + (WidgetTester tester) async { + final onAcquireLockResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onAcquireLockResult: (int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode, + String errorDetails) { + onAcquireLockResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + String errorDetails = "hello"; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onAcquireLockResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onAcquireLockResultCompleter.isCompleted) { + onAcquireLockResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onAcquireLockResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onRevokeLockResult', + (WidgetTester tester) async { + final onRevokeLockResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onRevokeLockResult: (int requestId, + String channelName, + RtmChannelType channelType, + String lockName, + RtmErrorCode errorCode) { + onRevokeLockResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onRevokeLockResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onRevokeLockResultCompleter.isCompleted) { + onRevokeLockResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onRevokeLockResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onGetLocksResult', + (WidgetTester tester) async { + final onGetLocksResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onGetLocksResult: (int requestId, + String channelName, + RtmChannelType channelType, + List lockDetailList, + int count, + RtmErrorCode errorCode) { + onGetLocksResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + final List lockDetailList = () { + String lockDetailListItemLockName = "hello"; + String lockDetailListItemOwner = "hello"; + int lockDetailListItemTtl = 5; + LockDetail lockDetailListItem = LockDetail( + lockName: lockDetailListItemLockName, + owner: lockDetailListItemOwner, + ttl: lockDetailListItemTtl, + ); + + return List.filled(5, lockDetailListItem); + }(); + + int count = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onGetLocksResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onGetLocksResultCompleter.isCompleted) { + onGetLocksResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onGetLocksResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onWhoNowResult', + (WidgetTester tester) async { + final onWhoNowResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onWhoNowResult: (int requestId, List userStateList, int count, + String nextPage, RtmErrorCode errorCode) { + onWhoNowResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + final List userStateList = () { + String userStateListItemUserId = "hello"; + List userStateListItemStates = []; + UserState userStateListItem = UserState( + userId: userStateListItemUserId, + states: userStateListItemStates, + ); + + return List.filled(5, userStateListItem); + }(); + + int count = 5; + String nextPage = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onWhoNowResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onWhoNowResultCompleter.isCompleted) { + onWhoNowResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onWhoNowResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onGetOnlineUsersResult', + (WidgetTester tester) async { + final onGetOnlineUsersResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onGetOnlineUsersResult: (int requestId, List userStateList, int count, + String nextPage, RtmErrorCode errorCode) { + onGetOnlineUsersResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + final List userStateList = () { + String userStateListItemUserId = "hello"; + List userStateListItemStates = []; + UserState userStateListItem = UserState( + userId: userStateListItemUserId, + states: userStateListItemStates, + ); + + return List.filled(5, userStateListItem); + }(); + + int count = 5; + String nextPage = "hello"; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onGetOnlineUsersResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onGetOnlineUsersResultCompleter.isCompleted) { + onGetOnlineUsersResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onGetOnlineUsersResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onWhereNowResult', + (WidgetTester tester) async { + final onWhereNowResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onWhereNowResult: + (int requestId, List channels, int count, RtmErrorCode errorCode) { + onWhereNowResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + final List channels = () { + RtmChannelType channelsItemChannelType = RtmChannelType.none; + String channelsItemChannelName = "hello"; + ChannelInfo channelsItem = ChannelInfo( + channelName: channelsItemChannelName, + channelType: channelsItemChannelType, + ); + + return List.filled(5, channelsItem); + }(); + + int count = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onWhereNowResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onWhereNowResultCompleter.isCompleted) { + onWhereNowResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onWhereNowResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onGetUserChannelsResult', + (WidgetTester tester) async { + final onGetUserChannelsResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onGetUserChannelsResult: (int requestId, ChannelInfo channels, + int count, RtmErrorCode errorCode) { + onGetUserChannelsResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + RtmChannelType channelsChannelType = RtmChannelType.none; + String channelsChannelName = "hello"; + ChannelInfo channels = ChannelInfo( + channelName: channelsChannelName, + channelType: channelsChannelType, + ); + int count = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onGetUserChannelsResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onGetUserChannelsResultCompleter.isCompleted) { + onGetUserChannelsResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onGetUserChannelsResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onPresenceSetStateResult', + (WidgetTester tester) async { + final onPresenceSetStateResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onPresenceSetStateResult: (int requestId, RtmErrorCode errorCode) { + onPresenceSetStateResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onPresenceSetStateResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onPresenceSetStateResultCompleter.isCompleted) { + onPresenceSetStateResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onPresenceSetStateResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onPresenceRemoveStateResult', + (WidgetTester tester) async { + final onPresenceRemoveStateResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onPresenceRemoveStateResult: (int requestId, RtmErrorCode errorCode) { + onPresenceRemoveStateResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onPresenceRemoveStateResult'] ?? + []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onPresenceRemoveStateResultCompleter.isCompleted) { + onPresenceRemoveStateResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onPresenceRemoveStateResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); + + testWidgets( + 'RtmEventHandler.onPresenceGetStateResult', + (WidgetTester tester) async { + final onPresenceGetStateResultCompleter = Completer(); + final theRtmEventHandler = RtmEventHandler( + onPresenceGetStateResult: + (int requestId, UserState state, RtmErrorCode errorCode) { + onPresenceGetStateResultCompleter.complete(true); + }, + ); + + final rtmClient = await _createBindingRtmClient(theRtmEventHandler); + +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + { + int requestId = 5; + String stateUserId = "hello"; + List stateStates = []; + UserState state = UserState( + userId: stateUserId, + states: stateStates, + ); + RtmErrorCode errorCode = RtmErrorCode.ok; + + final eventJson = {}; + + final eventIds = + eventIdsMapping['RtmEventHandler_onPresenceGetStateResult'] ?? []; + for (final event in eventIds) { + final ret = irisTester().fireEvent(event, params: eventJson); + // Delay 200 milliseconds to ensure the callback is called. + await Future.delayed(const Duration(milliseconds: 200)); + // TODO(littlegnal): Most of callbacks on web are not implemented, we're temporarily skip these callbacks at this time. + if (kIsWeb && ret) { + if (!onPresenceGetStateResultCompleter.isCompleted) { + onPresenceGetStateResultCompleter.complete(true); + } + } + } + } + + final eventCalled = await onPresenceGetStateResultCompleter.future; + expect(eventCalled, isTrue); + + {} +// Delay 500 milliseconds to ensure the call completed. + await Future.delayed(const Duration(milliseconds: 500)); + + await rtmClient.release(); + }, + timeout: const Timeout(Duration(minutes: 2)), + ); +} diff --git a/test_shard/integration_test_app/integration_test/generated/bindings/rtmlock_binding_fake_test.generated.dart b/test_shard/integration_test_app/integration_test/generated/bindings/rtmlock_binding_fake_test.generated.dart new file mode 100644 index 0000000..bf6656c --- /dev/null +++ b/test_shard/integration_test_app/integration_test/generated/bindings/rtmlock_binding_fake_test.generated.dart @@ -0,0 +1,231 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> + irisMethodChannelInitilizationArgs) { + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + testWidgets( + 'RtmLock.setLock', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmLock = RtmLockImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + int ttl = 5; + await rtmLock.setLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + ttl: ttl, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmLock.setLock] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmLock.getLocks', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmLock = RtmLockImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + await rtmLock.getLocks( + channelName: channelName, + channelType: channelType, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmLock.getLocks] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmLock.removeLock', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmLock = RtmLockImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + await rtmLock.removeLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmLock.removeLock] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmLock.acquireLock', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmLock = RtmLockImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + bool retry = true; + await rtmLock.acquireLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + retry: retry, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmLock.acquireLock] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmLock.releaseLock', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmLock = RtmLockImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + await rtmLock.releaseLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmLock.releaseLock] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmLock.revokeLock', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmLock = RtmLockImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + String owner = "hello"; + await rtmLock.revokeLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + owner: owner, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmLock.revokeLock] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); +} diff --git a/test_shard/integration_test_app/integration_test/generated/bindings/rtmpresence_binding_fake_test.generated.dart b/test_shard/integration_test_app/integration_test/generated/bindings/rtmpresence_binding_fake_test.generated.dart new file mode 100644 index 0000000..ad6ce06 --- /dev/null +++ b/test_shard/integration_test_app/integration_test/generated/bindings/rtmpresence_binding_fake_test.generated.dart @@ -0,0 +1,279 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> + irisMethodChannelInitilizationArgs) { + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + testWidgets( + 'RtmPresence.whoNow', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + bool optionsIncludeUserId = true; + bool optionsIncludeState = true; + String optionsPage = "hello"; + PresenceOptions options = PresenceOptions( + includeUserId: optionsIncludeUserId, + includeState: optionsIncludeState, + page: optionsPage, + ); + await rtmPresence.whoNow( + channelName: channelName, + channelType: channelType, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmPresence.whoNow] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmPresence.whereNow', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + await rtmPresence.whereNow( + userId, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmPresence.whereNow] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmPresence.setState', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + final List items = () { + String itemsItemKey = "hello"; + String itemsItemValue = "hello"; + StateItem itemsItem = StateItem( + key: itemsItemKey, + value: itemsItemValue, + ); + + return List.filled(5, itemsItem); + }(); + + int count = 5; + await rtmPresence.setState( + channelName: channelName, + channelType: channelType, + items: items, + count: count, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmPresence.setState] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmPresence.removeState', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + List keys = List.filled(5, "hello"); + int count = 5; + await rtmPresence.removeState( + channelName: channelName, + channelType: channelType, + keys: keys, + count: count, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmPresence.removeState] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmPresence.getState', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String userId = "hello"; + await rtmPresence.getState( + channelName: channelName, + channelType: channelType, + userId: userId, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmPresence.getState] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmPresence.getOnlineUsers', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + bool optionsIncludeUserId = true; + bool optionsIncludeState = true; + String optionsPage = "hello"; + GetOnlineUsersOptions options = GetOnlineUsersOptions( + includeUserId: optionsIncludeUserId, + includeState: optionsIncludeState, + page: optionsPage, + ); + await rtmPresence.getOnlineUsers( + channelName: channelName, + channelType: channelType, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmPresence.getOnlineUsers] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmPresence.getUserChannels', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmPresence = RtmPresenceImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + await rtmPresence.getUserChannels( + userId, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmPresence.getUserChannels] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); +} diff --git a/test_shard/integration_test_app/integration_test/generated/bindings/rtmstorage_binding_fake_test.generated.dart b/test_shard/integration_test_app/integration_test/generated/bindings/rtmstorage_binding_fake_test.generated.dart new file mode 100644 index 0000000..b43428a --- /dev/null +++ b/test_shard/integration_test_app/integration_test/generated/bindings/rtmstorage_binding_fake_test.generated.dart @@ -0,0 +1,429 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> + irisMethodChannelInitilizationArgs) { + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + testWidgets( + 'RtmStorage.setChannelMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + String lockName = "hello"; + await rtmStorage.setChannelMetadata( + channelName: channelName, + channelType: channelType, + data: data, + options: options, + lockName: lockName, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmStorage.setChannelMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.updateChannelMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + String lockName = "hello"; + await rtmStorage.updateChannelMetadata( + channelName: channelName, + channelType: channelType, + data: data, + options: options, + lockName: lockName, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[RtmStorage.updateChannelMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.removeChannelMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + String lockName = "hello"; + await rtmStorage.removeChannelMetadata( + channelName: channelName, + channelType: channelType, + data: data, + options: options, + lockName: lockName, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[RtmStorage.removeChannelMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.getChannelMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + await rtmStorage.getChannelMetadata( + channelName: channelName, + channelType: channelType, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmStorage.getChannelMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.setUserMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + await rtmStorage.setUserMetadata( + userId: userId, + data: data, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmStorage.setUserMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.updateUserMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + await rtmStorage.updateUserMetadata( + userId: userId, + data: data, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmStorage.updateUserMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.removeUserMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + await rtmStorage.removeUserMetadata( + userId: userId, + data: data, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmStorage.removeUserMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.getUserMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + await rtmStorage.getUserMetadata( + userId, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[RtmStorage.getUserMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.subscribeUserMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + await rtmStorage.subscribeUserMetadata( + userId, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[RtmStorage.subscribeUserMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'RtmStorage.unsubscribeUserMetadata', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final rtmStorage = RtmStorageImpl(rtmClient.getIrisMethodChannel()); + + try { + String userId = "hello"; + await rtmStorage.unsubscribeUserMetadata( + userId, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[RtmStorage.unsubscribeUserMetadata] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); +} diff --git a/test_shard/integration_test_app/integration_test/generated/bindings/streamchannel_binding_fake_test.generated.dart b/test_shard/integration_test_app/integration_test/generated/bindings/streamchannel_binding_fake_test.generated.dart new file mode 100644 index 0000000..85e4fdd --- /dev/null +++ b/test_shard/integration_test_app/integration_test/generated/bindings/streamchannel_binding_fake_test.generated.dart @@ -0,0 +1,482 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart' show AgoraRtmException; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart'; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:agora_rtm/src/bindings/native_iris_api_engine_binding_delegate.dart'; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; + +void testCases( + ValueGetter> + irisMethodChannelInitilizationArgs) { + Future _createBindingRtmClient() async { + String appId = const String.fromEnvironment('TEST_APP_ID', + defaultValue: ''); + final rtmResultHandler = RtmResultHandlerImpl(); + final client = RtmClientImplOverride.create( + IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + ); + await client.initialize( + appId, + 'user_id', + rtmResultHandler.rtmEventHandler, + args: irisMethodChannelInitilizationArgs(), + ); + return client; + } + + testWidgets( + 'StreamChannel.join', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String optionsToken = "hello"; + bool optionsWithMetadata = true; + bool optionsWithPresence = true; + bool optionsWithLock = true; + bool optionsBeQuiet = true; + JoinChannelOptions options = JoinChannelOptions( + token: optionsToken, + withMetadata: optionsWithMetadata, + withPresence: optionsWithPresence, + withLock: optionsWithLock, + beQuiet: optionsBeQuiet, + ); + await streamChannel.join( + options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.join] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.renewToken', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String token = "hello"; + await streamChannel.renewToken( + token, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.renewToken] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.leave', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + await streamChannel.leave(); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.leave] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.getChannelName', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + await streamChannel.getChannelName(); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.getChannelName] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.joinTopic', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + RtmMessageQos optionsQos = RtmMessageQos.unordered; + RtmMessagePriority optionsPriority = RtmMessagePriority.highest; + String optionsMeta = "hello"; + bool optionsSyncWithMedia = true; + JoinTopicOptions options = JoinTopicOptions( + qos: optionsQos, + priority: optionsPriority, + meta: optionsMeta, + syncWithMedia: optionsSyncWithMedia, + ); + await streamChannel.joinTopic( + topic: topic, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.joinTopic] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.publishTopicMessage', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + String message = "hello"; + int length = 5; + RtmMessageType optionMessageType = RtmMessageType.binary; + int optionSendTs = 5; + String optionCustomType = "hello"; + TopicMessageOptions option = TopicMessageOptions( + messageType: optionMessageType, + sendTs: optionSendTs, + customType: optionCustomType, + ); + await streamChannel.publishTopicMessage( + topic: topic, + message: message, + length: length, + option: option, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[StreamChannel.publishTopicMessage] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.leaveTopic', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + await streamChannel.leaveTopic( + topic, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.leaveTopic] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.subscribeTopic', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + List optionsUsers = List.filled(5, "hello"); + int optionsUserCount = 5; + TopicOptions options = TopicOptions( + users: optionsUsers, + userCount: optionsUserCount, + ); + await streamChannel.subscribeTopic( + topic: topic, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.subscribeTopic] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.unsubscribeTopic', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + List optionsUsers = List.filled(5, "hello"); + int optionsUserCount = 5; + TopicOptions options = TopicOptions( + users: optionsUsers, + userCount: optionsUserCount, + ); + await streamChannel.unsubscribeTopic( + topic: topic, + options: options, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.unsubscribeTopic] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.getSubscribedUserList', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + await streamChannel.getSubscribedUserList( + topic, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[StreamChannel.getSubscribedUserList] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.release', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + await streamChannel.release(); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint('[StreamChannel.release] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.publishTextMessage', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + String message = "hello"; + int length = 5; + RtmMessageType optionMessageType = RtmMessageType.binary; + int optionSendTs = 5; + String optionCustomType = "hello"; + TopicMessageOptions option = TopicMessageOptions( + messageType: optionMessageType, + sendTs: optionSendTs, + customType: optionCustomType, + ); + await streamChannel.publishTextMessage( + topic: topic, + message: message, + length: length, + option: option, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[StreamChannel.publishTextMessage] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); + + testWidgets( + 'StreamChannel.publishBinaryMessage', + (WidgetTester tester) async { + final rtmClient = await _createBindingRtmClient(); + await rtmClient.setParameters('{"rtm.log_filter":2063}'); + final streamChannel = + await rtmClient.createStreamChannel('stream_channel'); + + try { + String topic = "hello"; + Uint8List message = Uint8List.fromList([1, 1, 1, 1, 1]); + int length = 5; + RtmMessageType optionMessageType = RtmMessageType.binary; + int optionSendTs = 5; + String optionCustomType = "hello"; + TopicMessageOptions option = TopicMessageOptions( + messageType: optionMessageType, + sendTs: optionSendTs, + customType: optionCustomType, + ); + await streamChannel.publishBinaryMessage( + topic: topic, + message: message, + length: length, + option: option, + ); + } catch (e) { + if (e is! AgoraRtmException) { + debugPrint( + '[StreamChannel.publishBinaryMessage] error: ${e.toString()}'); + rethrow; + } + + if (e.code != -4) { + // Only not supported error supported. + rethrow; + } + } + + await rtmClient.release(); + }, + ); +} diff --git a/test_shard/integration_test_app/ios/.gitignore b/test_shard/integration_test_app/ios/.gitignore new file mode 100644 index 0000000..c17ee20 --- /dev/null +++ b/test_shard/integration_test_app/ios/.gitignore @@ -0,0 +1,35 @@ +**/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 +Podfile.lock diff --git a/test_shard/integration_test_app/ios/Flutter/AppFrameworkInfo.plist b/test_shard/integration_test_app/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..9625e10 --- /dev/null +++ b/test_shard/integration_test_app/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/test_shard/integration_test_app/ios/Flutter/Debug.xcconfig b/test_shard/integration_test_app/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/ios/Flutter/Release.xcconfig b/test_shard/integration_test_app/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/ios/Podfile b/test_shard/integration_test_app/ios/Podfile new file mode 100644 index 0000000..80f20eb --- /dev/null +++ b/test_shard/integration_test_app/ios/Podfile @@ -0,0 +1,63 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def plugin_dev + generated_xcode_build_settings_path = File.join('.symlinks', 'plugins', 'agora_rtc_engine', 'ios', '.plugin_dev') + + if not File.exist?(generated_xcode_build_settings_path) + return nil + end + + return generated_xcode_build_settings_path +end + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + if plugin_dev != nil + pod 'AgoraRtcWrapper', :path => File.join('.symlinks', 'plugins', 'agora_rtc_engine', 'ios') + # pod 'iris_event_handler', :path => File.join('.symlinks', 'plugins', 'iris_event', 'ios') + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + + # Temporary exclude simulator arm64 to allow run app on simulator + # https://developer.apple.com/forums/thread/656509 + target.build_configurations.each do |config| + config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES' + config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "i386" + end + end +end diff --git a/test_shard/integration_test_app/ios/Runner.xcodeproj/project.pbxproj b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..192c86b --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,552 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 8D7B69F06DC3EF5EAFF8CA7A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F0C4CB4BCD2B8CCE5A60124 /* 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1214EC7B36F09712D06E5572 /* 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 = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 67EA64C41E1D9E1E4F7A5311 /* 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 = ""; }; + 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 = ""; }; + 7F0C4CB4BCD2B8CCE5A60124 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 82BEC1199950B363131A87D4 /* 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; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7B69F06DC3EF5EAFF8CA7A /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1F59594015E468F08F6704E2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7F0C4CB4BCD2B8CCE5A60124 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5AFDDF696155BB1DEA8F45F9 /* Pods */ = { + isa = PBXGroup; + children = ( + 67EA64C41E1D9E1E4F7A5311 /* Pods-Runner.debug.xcconfig */, + 1214EC7B36F09712D06E5572 /* Pods-Runner.release.xcconfig */, + 82BEC1199950B363131A87D4 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + 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 */, + 5AFDDF696155BB1DEA8F45F9 /* Pods */, + 1F59594015E468F08F6704E2 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 2809DDD17DCEC16E11FE63C5 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 27AB6733D50F57FC60AD6CA7 /* [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 = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 27AB6733D50F57FC60AD6CA7 /* [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; + }; + 2809DDD17DCEC16E11FE63C5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = SKNR67U6GB; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.agora.integrationtestapp.integrationTestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = SKNR67U6GB; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.agora.integrationtestapp.integrationTestApp; + 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 = SKNR67U6GB; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.agora.integrationtestapp.integrationTestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/test_shard/integration_test_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test_shard/integration_test_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c87d15a --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_app/ios/Runner.xcworkspace/contents.xcworkspacedata b/test_shard/integration_test_app/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/test_shard/integration_test_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/integration_test_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/integration_test_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/test_shard/integration_test_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/test_shard/integration_test_app/ios/Runner/AppDelegate.swift b/test_shard/integration_test_app/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/test_shard/integration_test_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/ios/Runner/Base.lproj/LaunchScreen.storyboard b/test_shard/integration_test_app/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_app/ios/Runner/Base.lproj/Main.storyboard b/test_shard/integration_test_app/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_app/ios/Runner/Info.plist b/test_shard/integration_test_app/ios/Runner/Info.plist new file mode 100644 index 0000000..0c58810 --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Integration Test App + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + integration_test_app + 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 + + + diff --git a/test_shard/integration_test_app/ios/Runner/Runner-Bridging-Header.h b/test_shard/integration_test_app/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/test_shard/integration_test_app/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/test_shard/integration_test_app/lib/main.dart b/test_shard/integration_test_app/lib/main.dart new file mode 100644 index 0000000..aea4658 --- /dev/null +++ b/test_shard/integration_test_app/lib/main.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:iris_method_channel/iris_method_channel.dart'; +import 'package:iris_tester/iris_tester.dart'; + +class TestInitilizationArgProvider implements InitilizationArgProvider { + TestInitilizationArgProvider(this.testerArgs); + TestInitilizationArgProvider.fromValue(IrisHandle this.value) + : testerArgs = []; + final List testerArgs; + IrisHandle? value; + @override + IrisHandle provide(IrisApiEngineHandle apiEngineHandle) { + return value ?? ObjectIrisHandle(testerArgs[0](apiEngineHandle())); + } +} + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + // List irisMethodChannelInitilizationArgs = []; + // IrisTester? irisTester; + // irisTester = createIrisTester(); + // irisTester!.initialize(); + // if (kIsWeb) { + // rtm_client_impl_override.setMockSharedNativeHandleProvider( + // TestInitilizationArgProvider(irisTester!.getTesterArgs())); + // } else { + // // On IO, the function return from the `irisTester.getTesterArgs()` capture + // // the `Pointer` from `IrisTester`, which is invalid to pass to the `Isolate`, + // // so directly pass the `ObjectIrisHandle` as value to the `setMockSharedNativeHandleProvider` + // final value = + // irisTester!.getTesterArgs()[0](const IrisApiEngineHandle(0)); + // // rtm_client_impl_override.setMockSharedNativeHandleProvider( + // // TestInitilizationArgProvider.fromValue(ObjectIrisHandle(value))); + + // irisMethodChannelInitilizationArgs = [ + // TestInitilizationArgProvider.fromValue(ObjectIrisHandle(value)) + // ]; + // } + + // Future _createBindingRtmClient() async { + // String appId = const String.fromEnvironment('TEST_APP_ID', + // defaultValue: ''); + // final rtmResultHandler = RtmResultHandlerImpl(); + // return RtmClientImplOverride.create( + // IrisMethodChannel(IrisApiEngineNativeBindingDelegateProvider()), + // RtmConfig(appId: appId), + // rtmResultHandler.rtmEventHandler, + // args: irisMethodChannelInitilizationArgs, + // ); + // } + + // await _createBindingRtmClient(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on:\n'), + ), + ), + ); + } +} diff --git a/test_shard/integration_test_app/macos/.gitignore b/test_shard/integration_test_app/macos/.gitignore new file mode 100644 index 0000000..e273000 --- /dev/null +++ b/test_shard/integration_test_app/macos/.gitignore @@ -0,0 +1,8 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ +Podfile.lock diff --git a/test_shard/integration_test_app/macos/Flutter/Flutter-Debug.xcconfig b/test_shard/integration_test_app/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..4b81f9b --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/macos/Flutter/Flutter-Release.xcconfig b/test_shard/integration_test_app/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5caa9d1 --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/macos/Podfile b/test_shard/integration_test_app/macos/Podfile new file mode 100644 index 0000000..74dce66 --- /dev/null +++ b/test_shard/integration_test_app/macos/Podfile @@ -0,0 +1,50 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def plugin_dev + generated_xcode_build_settings_path = File.join(File.join('Flutter', 'ephemeral', '.symlinks'), 'plugins', 'agora_rtc_engine', 'macos', '.plugin_dev') + + if not File.exist?(generated_xcode_build_settings_path) + return nil + end + + return generated_xcode_build_settings_path +end + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/test_shard/integration_test_app/macos/Runner.xcodeproj/project.pbxproj b/test_shard/integration_test_app/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ac82749 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,632 @@ +// !$*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 */ + 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 */; }; + 769670B167E15D666838C783 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 300CA9A11ECABEA15988A9B8 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 300CA9A11ECABEA15988A9B8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 /* integration_test_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test_app.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 = ""; }; + 49DAFEF7CA315FF16DC226B2 /* 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 = ""; }; + 792C153C94A84176F1D6C36B /* 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 = ""; }; + A29D00B97A6FAE00946B9BB8 /* 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 */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 769670B167E15D666838C783 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + B0D55B80487838425B3FC8DB /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* integration_test_app.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + B0D55B80487838425B3FC8DB /* Pods */ = { + isa = PBXGroup; + children = ( + 792C153C94A84176F1D6C36B /* Pods-Runner.debug.xcconfig */, + 49DAFEF7CA315FF16DC226B2 /* Pods-Runner.release.xcconfig */, + A29D00B97A6FAE00946B9BB8 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 300CA9A11ECABEA15988A9B8 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + EC0D36323D4164D4E8A2FF8A /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 08B1E3D871BDD0D14920F25C /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* integration_test_app.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1430; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 08B1E3D871BDD0D14920F25C /* [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; + 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"; + }; + EC0D36323D4164D4E8A2FF8A /* [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 */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/test_shard/integration_test_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/integration_test_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/integration_test_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test_shard/integration_test_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..6369118 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_app/macos/Runner.xcworkspace/contents.xcworkspacedata b/test_shard/integration_test_app/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/test_shard/integration_test_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/integration_test_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/integration_test_app/macos/Runner/AppDelegate.swift b/test_shard/integration_test_app/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..3c4935a Binary files /dev/null and b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..ed4cc16 Binary files /dev/null and b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..483be61 Binary files /dev/null and b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bcbf36d Binary files /dev/null and b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..9c0a652 Binary files /dev/null and b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..e71a726 Binary files /dev/null and b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..8a31fe2 Binary files /dev/null and b/test_shard/integration_test_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/test_shard/integration_test_app/macos/Runner/Base.lproj/MainMenu.xib b/test_shard/integration_test_app/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_app/macos/Runner/Configs/AppInfo.xcconfig b/test_shard/integration_test_app/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..15a2eb0 --- /dev/null +++ b/test_shard/integration_test_app/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 = integration_test_app + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = io.agora.integrationtestapp.integrationTestApp + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 io.agora.integration_test_app. All rights reserved. diff --git a/test_shard/integration_test_app/macos/Runner/Configs/Debug.xcconfig b/test_shard/integration_test_app/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/test_shard/integration_test_app/macos/Runner/Configs/Release.xcconfig b/test_shard/integration_test_app/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/test_shard/integration_test_app/macos/Runner/Configs/Warnings.xcconfig b/test_shard/integration_test_app/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/macos/Runner/DebugProfile.entitlements b/test_shard/integration_test_app/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..e92c0f0 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.device.audio-input + + com.apple.security.device.camera + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/test_shard/integration_test_app/macos/Runner/Info.plist b/test_shard/integration_test_app/macos/Runner/Info.plist new file mode 100644 index 0000000..52fbfa0 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/Info.plist @@ -0,0 +1,36 @@ + + + + + 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) + NSCameraUsageDescription + CAM + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSMicrophoneUsageDescription + MIC + NSPrincipalClass + NSApplication + + diff --git a/test_shard/integration_test_app/macos/Runner/MainFlutterWindow.swift b/test_shard/integration_test_app/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..2722837 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/test_shard/integration_test_app/macos/Runner/Release.entitlements b/test_shard/integration_test_app/macos/Runner/Release.entitlements new file mode 100644 index 0000000..0a36428 --- /dev/null +++ b/test_shard/integration_test_app/macos/Runner/Release.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.device.audio-input + + com.apple.security.device.camera + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/test_shard/integration_test_app/pubspec.yaml b/test_shard/integration_test_app/pubspec.yaml new file mode 100644 index 0000000..3be67be --- /dev/null +++ b/test_shard/integration_test_app/pubspec.yaml @@ -0,0 +1,106 @@ +name: integration_test_app +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 used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.10.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + agora_rtm: + path: ../../ + + iris_tester: + path: ../iris_tester + + # iris_tester: + # path: ../iris_tester + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + path_provider: ^2.0.8 + permission_handler: ^8.3.0 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + mockito: '>=5.3.2' + build_runner: ^2.1.7 + + # 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: ^1.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: + - assets/agora-logo.png + - assets/Agora.io-Interactions.mp3 + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test_shard/integration_test_app/test/agora_rtm_client_test.dart b/test_shard/integration_test_app/test/agora_rtm_client_test.dart new file mode 100644 index 0000000..2d57b56 --- /dev/null +++ b/test_shard/integration_test_app/test/agora_rtm_client_test.dart @@ -0,0 +1,18 @@ +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Can get xor value from RtmConfig.areaCode', () { + const config = RtmConfig(areaCode: {RtmAreaCode.cn, RtmAreaCode.na}); + final json = config.toJson(); + expect(json['areaCode'], RtmAreaCode.cn.value() | RtmAreaCode.na.value()); + }); + + test('Can get xor value from RtmPrivateConfig.serviceType', () { + const config = RtmPrivateConfig( + serviceType: {RtmServiceType.message, RtmServiceType.stream}); + final json = config.toJson(); + expect(json['serviceType'], + RtmServiceType.message.value() | RtmServiceType.stream.value()); + }); +} diff --git a/test_shard/integration_test_app/test/all_mocks.dart b/test_shard/integration_test_app/test/all_mocks.dart new file mode 100644 index 0000000..87de7b8 --- /dev/null +++ b/test_shard/integration_test_app/test/all_mocks.dart @@ -0,0 +1,27 @@ +import 'package:mockito/annotations.dart'; + +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart' + as rtm_client_impl_native_binding; + +import 'package:agora_rtm/src/bindings/gen/agora_rtm_lock_impl.dart' + as rtm_lock_impl_native_binding; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_presence_impl.dart' + as rtm_presence_impl_native_binding; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_storage_impl.dart' + as rtm_storage_impl_native_binding; +import 'package:agora_rtm/src/bindings/gen/agora_stream_channel_impl.dart' + as stream_channel_impl_native_binding; + +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + // RtmResultHandlerImpl + MockSpec(), +]) +// ignore: unused_import +import 'all_mocks.mocks.dart'; diff --git a/test_shard/integration_test_app/test/all_mocks.mocks.dart b/test_shard/integration_test_app/test/all_mocks.mocks.dart new file mode 100644 index 0000000..dd39e58 --- /dev/null +++ b/test_shard/integration_test_app/test/all_mocks.mocks.dart @@ -0,0 +1,2252 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in integration_test_app/test/all_mocks.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i8; +import 'dart:typed_data' as _i13; + +import 'package:agora_rtm/src/agora_rtm_base.dart' as _i12; +import 'package:agora_rtm/src/agora_rtm_client.dart' as _i11; +import 'package:agora_rtm/src/agora_rtm_client_ext.dart' as _i4; +import 'package:agora_rtm/src/bindings/agora_rtm_client_impl_override.dart' + as _i9; +import 'package:agora_rtm/src/bindings/gen/agora_rtm_client.dart' as _i7; +import 'package:agora_rtm/src/bindings/gen/agora_stream_channel.dart' as _i5; +import 'package:agora_rtm/src/bindings/gen/binding_forward_export.dart' as _i6; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart' as _i14; +import 'package:flutter/services.dart' as _i2; +import 'package:iris_method_channel/iris_method_channel.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i10; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeMethodChannel_0 extends _i1.SmartFake implements _i2.MethodChannel { + _FakeMethodChannel_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeIrisMethodChannel_1 extends _i1.SmartFake + implements _i3.IrisMethodChannel { + _FakeIrisMethodChannel_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRtmStatus_2 extends _i1.SmartFake implements _i4.RtmStatus { + _FakeRtmStatus_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeStreamChannel_3 extends _i1.SmartFake implements _i5.StreamChannel { + _FakeStreamChannel_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRtmStorage_4 extends _i1.SmartFake implements _i6.RtmStorage { + _FakeRtmStorage_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRtmLock_5 extends _i1.SmartFake implements _i6.RtmLock { + _FakeRtmLock_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRtmPresence_6 extends _i1.SmartFake implements _i6.RtmPresence { + _FakeRtmPresence_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRtmEventHandler_7 extends _i1.SmartFake + implements _i7.RtmEventHandler { + _FakeRtmEventHandler_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFuture_8 extends _i1.SmartFake implements _i8.Future { + _FakeFuture_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [RtmClientImplOverride]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockRtmClientImplOverride extends _i1.Mock + implements _i9.RtmClientImplOverride { + @override + _i2.MethodChannel get rtmMethodChannel => (super.noSuchMethod( + Invocation.getter(#rtmMethodChannel), + returnValue: _FakeMethodChannel_0( + this, + Invocation.getter(#rtmMethodChannel), + ), + returnValueForMissingStub: _FakeMethodChannel_0( + this, + Invocation.getter(#rtmMethodChannel), + ), + ) as _i2.MethodChannel); + + @override + set rtmMethodChannel(_i2.MethodChannel? _rtmMethodChannel) => + super.noSuchMethod( + Invocation.setter( + #rtmMethodChannel, + _rtmMethodChannel, + ), + returnValueForMissingStub: null, + ); + + @override + _i3.IrisMethodChannel get irisMethodChannel => (super.noSuchMethod( + Invocation.getter(#irisMethodChannel), + returnValue: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + returnValueForMissingStub: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + ) as _i3.IrisMethodChannel); + + @override + bool get isOverrideClassName => (super.noSuchMethod( + Invocation.getter(#isOverrideClassName), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + String get className => (super.noSuchMethod( + Invocation.getter(#className), + returnValue: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + returnValueForMissingStub: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + ) as String); + + @override + _i8.Future<_i4.RtmStatus> initialize( + String? appId, + String? userId, + _i7.RtmEventHandler? rtmEventHandler, { + _i11.RtmConfig? config, + List<_i3.InitilizationArgProvider>? args = const [], + }) => + (super.noSuchMethod( + Invocation.method( + #initialize, + [ + appId, + userId, + rtmEventHandler, + ], + { + #config: config, + #args: args, + }, + ), + returnValue: _i8.Future<_i4.RtmStatus>.value(_FakeRtmStatus_2( + this, + Invocation.method( + #initialize, + [ + appId, + userId, + rtmEventHandler, + ], + { + #config: config, + #args: args, + }, + ), + )), + returnValueForMissingStub: + _i8.Future<_i4.RtmStatus>.value(_FakeRtmStatus_2( + this, + Invocation.method( + #initialize, + [ + appId, + userId, + rtmEventHandler, + ], + { + #config: config, + #args: args, + }, + ), + )), + ) as _i8.Future<_i4.RtmStatus>); + + @override + _i3.IrisMethodChannel getIrisMethodChannel() => (super.noSuchMethod( + Invocation.method( + #getIrisMethodChannel, + [], + ), + returnValue: _FakeIrisMethodChannel_1( + this, + Invocation.method( + #getIrisMethodChannel, + [], + ), + ), + returnValueForMissingStub: _FakeIrisMethodChannel_1( + this, + Invocation.method( + #getIrisMethodChannel, + [], + ), + ), + ) as _i3.IrisMethodChannel); + + @override + _i8.Future<_i5.StreamChannel> createStreamChannel(String? channelName) => + (super.noSuchMethod( + Invocation.method( + #createStreamChannel, + [channelName], + ), + returnValue: _i8.Future<_i5.StreamChannel>.value(_FakeStreamChannel_3( + this, + Invocation.method( + #createStreamChannel, + [channelName], + ), + )), + returnValueForMissingStub: + _i8.Future<_i5.StreamChannel>.value(_FakeStreamChannel_3( + this, + Invocation.method( + #createStreamChannel, + [channelName], + ), + )), + ) as _i8.Future<_i5.StreamChannel>); + + @override + _i8.Future release() => (super.noSuchMethod( + Invocation.method( + #release, + [], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + + @override + _i8.Future publish({ + required String? channelName, + required String? message, + required int? length, + required _i12.PublishOptions? option, + }) => + (super.noSuchMethod( + Invocation.method( + #publish, + [], + { + #channelName: channelName, + #message: message, + #length: length, + #option: option, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + Map createParams(Map? param) => + (super.noSuchMethod( + Invocation.method( + #createParams, + [param], + ), + returnValue: {}, + returnValueForMissingStub: {}, + ) as Map); + + @override + _i8.Future login(String? token) => (super.noSuchMethod( + Invocation.method( + #login, + [token], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future logout() => (super.noSuchMethod( + Invocation.method( + #logout, + [], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future<_i6.RtmStorage> getStorage() => (super.noSuchMethod( + Invocation.method( + #getStorage, + [], + ), + returnValue: _i8.Future<_i6.RtmStorage>.value(_FakeRtmStorage_4( + this, + Invocation.method( + #getStorage, + [], + ), + )), + returnValueForMissingStub: + _i8.Future<_i6.RtmStorage>.value(_FakeRtmStorage_4( + this, + Invocation.method( + #getStorage, + [], + ), + )), + ) as _i8.Future<_i6.RtmStorage>); + + @override + _i8.Future<_i6.RtmLock> getLock() => (super.noSuchMethod( + Invocation.method( + #getLock, + [], + ), + returnValue: _i8.Future<_i6.RtmLock>.value(_FakeRtmLock_5( + this, + Invocation.method( + #getLock, + [], + ), + )), + returnValueForMissingStub: _i8.Future<_i6.RtmLock>.value(_FakeRtmLock_5( + this, + Invocation.method( + #getLock, + [], + ), + )), + ) as _i8.Future<_i6.RtmLock>); + + @override + _i8.Future<_i6.RtmPresence> getPresence() => (super.noSuchMethod( + Invocation.method( + #getPresence, + [], + ), + returnValue: _i8.Future<_i6.RtmPresence>.value(_FakeRtmPresence_6( + this, + Invocation.method( + #getPresence, + [], + ), + )), + returnValueForMissingStub: + _i8.Future<_i6.RtmPresence>.value(_FakeRtmPresence_6( + this, + Invocation.method( + #getPresence, + [], + ), + )), + ) as _i8.Future<_i6.RtmPresence>); + + @override + _i8.Future renewToken(String? token) => (super.noSuchMethod( + Invocation.method( + #renewToken, + [token], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future subscribe({ + required String? channelName, + required _i12.SubscribeOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribe, + [], + { + #channelName: channelName, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future unsubscribe(String? channelName) => (super.noSuchMethod( + Invocation.method( + #unsubscribe, + [channelName], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future setParameters(String? parameters) => (super.noSuchMethod( + Invocation.method( + #setParameters, + [parameters], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + + @override + _i8.Future publishBinaryMessage({ + required String? channelName, + required _i13.Uint8List? message, + required int? length, + required _i12.PublishOptions? option, + }) => + (super.noSuchMethod( + Invocation.method( + #publishBinaryMessage, + [], + { + #channelName: channelName, + #message: message, + #length: length, + #option: option, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); +} + +/// A class which mocks [RtmLockImpl]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockRtmLockImpl extends _i1.Mock implements _i6.RtmLockImpl { + @override + _i3.IrisMethodChannel get irisMethodChannel => (super.noSuchMethod( + Invocation.getter(#irisMethodChannel), + returnValue: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + returnValueForMissingStub: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + ) as _i3.IrisMethodChannel); + + @override + bool get isOverrideClassName => (super.noSuchMethod( + Invocation.getter(#isOverrideClassName), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + String get className => (super.noSuchMethod( + Invocation.getter(#className), + returnValue: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + returnValueForMissingStub: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + ) as String); + + @override + Map createParams(Map? param) => + (super.noSuchMethod( + Invocation.method( + #createParams, + [param], + ), + returnValue: {}, + returnValueForMissingStub: {}, + ) as Map); + + @override + _i8.Future setLock({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required String? lockName, + required int? ttl, + }) => + (super.noSuchMethod( + Invocation.method( + #setLock, + [], + { + #channelName: channelName, + #channelType: channelType, + #lockName: lockName, + #ttl: ttl, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getLocks({ + required String? channelName, + required _i12.RtmChannelType? channelType, + }) => + (super.noSuchMethod( + Invocation.method( + #getLocks, + [], + { + #channelName: channelName, + #channelType: channelType, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future removeLock({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required String? lockName, + }) => + (super.noSuchMethod( + Invocation.method( + #removeLock, + [], + { + #channelName: channelName, + #channelType: channelType, + #lockName: lockName, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future acquireLock({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required String? lockName, + required bool? retry, + }) => + (super.noSuchMethod( + Invocation.method( + #acquireLock, + [], + { + #channelName: channelName, + #channelType: channelType, + #lockName: lockName, + #retry: retry, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future releaseLock({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required String? lockName, + }) => + (super.noSuchMethod( + Invocation.method( + #releaseLock, + [], + { + #channelName: channelName, + #channelType: channelType, + #lockName: lockName, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future revokeLock({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required String? lockName, + required String? owner, + }) => + (super.noSuchMethod( + Invocation.method( + #revokeLock, + [], + { + #channelName: channelName, + #channelType: channelType, + #lockName: lockName, + #owner: owner, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); +} + +/// A class which mocks [RtmPresenceImpl]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockRtmPresenceImpl extends _i1.Mock implements _i6.RtmPresenceImpl { + @override + _i3.IrisMethodChannel get irisMethodChannel => (super.noSuchMethod( + Invocation.getter(#irisMethodChannel), + returnValue: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + returnValueForMissingStub: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + ) as _i3.IrisMethodChannel); + + @override + bool get isOverrideClassName => (super.noSuchMethod( + Invocation.getter(#isOverrideClassName), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + String get className => (super.noSuchMethod( + Invocation.getter(#className), + returnValue: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + returnValueForMissingStub: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + ) as String); + + @override + Map createParams(Map? param) => + (super.noSuchMethod( + Invocation.method( + #createParams, + [param], + ), + returnValue: {}, + returnValueForMissingStub: {}, + ) as Map); + + @override + _i8.Future whoNow({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required _i12.PresenceOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #whoNow, + [], + { + #channelName: channelName, + #channelType: channelType, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future whereNow(String? userId) => (super.noSuchMethod( + Invocation.method( + #whereNow, + [userId], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future setState({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required List<_i12.StateItem>? items, + required int? count, + }) => + (super.noSuchMethod( + Invocation.method( + #setState, + [], + { + #channelName: channelName, + #channelType: channelType, + #items: items, + #count: count, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future removeState({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required List? keys, + required int? count, + }) => + (super.noSuchMethod( + Invocation.method( + #removeState, + [], + { + #channelName: channelName, + #channelType: channelType, + #keys: keys, + #count: count, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getState({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required String? userId, + }) => + (super.noSuchMethod( + Invocation.method( + #getState, + [], + { + #channelName: channelName, + #channelType: channelType, + #userId: userId, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getOnlineUsers({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required _i12.GetOnlineUsersOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #getOnlineUsers, + [], + { + #channelName: channelName, + #channelType: channelType, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getUserChannels(String? userId) => (super.noSuchMethod( + Invocation.method( + #getUserChannels, + [userId], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); +} + +/// A class which mocks [RtmStorageImpl]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockRtmStorageImpl extends _i1.Mock implements _i6.RtmStorageImpl { + @override + _i3.IrisMethodChannel get irisMethodChannel => (super.noSuchMethod( + Invocation.getter(#irisMethodChannel), + returnValue: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + returnValueForMissingStub: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + ) as _i3.IrisMethodChannel); + + @override + bool get isOverrideClassName => (super.noSuchMethod( + Invocation.getter(#isOverrideClassName), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + String get className => (super.noSuchMethod( + Invocation.getter(#className), + returnValue: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + returnValueForMissingStub: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + ) as String); + + @override + Map createParams(Map? param) => + (super.noSuchMethod( + Invocation.method( + #createParams, + [param], + ), + returnValue: {}, + returnValueForMissingStub: {}, + ) as Map); + + @override + _i8.Future setChannelMetadata({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required _i6.Metadata? data, + required _i6.MetadataOptions? options, + required String? lockName, + }) => + (super.noSuchMethod( + Invocation.method( + #setChannelMetadata, + [], + { + #channelName: channelName, + #channelType: channelType, + #data: data, + #options: options, + #lockName: lockName, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future updateChannelMetadata({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required _i6.Metadata? data, + required _i6.MetadataOptions? options, + required String? lockName, + }) => + (super.noSuchMethod( + Invocation.method( + #updateChannelMetadata, + [], + { + #channelName: channelName, + #channelType: channelType, + #data: data, + #options: options, + #lockName: lockName, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future removeChannelMetadata({ + required String? channelName, + required _i12.RtmChannelType? channelType, + required _i6.Metadata? data, + required _i6.MetadataOptions? options, + required String? lockName, + }) => + (super.noSuchMethod( + Invocation.method( + #removeChannelMetadata, + [], + { + #channelName: channelName, + #channelType: channelType, + #data: data, + #options: options, + #lockName: lockName, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getChannelMetadata({ + required String? channelName, + required _i12.RtmChannelType? channelType, + }) => + (super.noSuchMethod( + Invocation.method( + #getChannelMetadata, + [], + { + #channelName: channelName, + #channelType: channelType, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future setUserMetadata({ + required String? userId, + required _i6.Metadata? data, + required _i6.MetadataOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #setUserMetadata, + [], + { + #userId: userId, + #data: data, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future updateUserMetadata({ + required String? userId, + required _i6.Metadata? data, + required _i6.MetadataOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #updateUserMetadata, + [], + { + #userId: userId, + #data: data, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future removeUserMetadata({ + required String? userId, + required _i6.Metadata? data, + required _i6.MetadataOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #removeUserMetadata, + [], + { + #userId: userId, + #data: data, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getUserMetadata(String? userId) => (super.noSuchMethod( + Invocation.method( + #getUserMetadata, + [userId], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future subscribeUserMetadata(String? userId) => (super.noSuchMethod( + Invocation.method( + #subscribeUserMetadata, + [userId], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future unsubscribeUserMetadata(String? userId) => + (super.noSuchMethod( + Invocation.method( + #unsubscribeUserMetadata, + [userId], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); +} + +/// A class which mocks [StreamChannelImpl]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockStreamChannelImpl extends _i1.Mock implements _i6.StreamChannelImpl { + @override + _i3.IrisMethodChannel get irisMethodChannel => (super.noSuchMethod( + Invocation.getter(#irisMethodChannel), + returnValue: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + returnValueForMissingStub: _FakeIrisMethodChannel_1( + this, + Invocation.getter(#irisMethodChannel), + ), + ) as _i3.IrisMethodChannel); + + @override + bool get isOverrideClassName => (super.noSuchMethod( + Invocation.getter(#isOverrideClassName), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + String get className => (super.noSuchMethod( + Invocation.getter(#className), + returnValue: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + returnValueForMissingStub: _i10.dummyValue( + this, + Invocation.getter(#className), + ), + ) as String); + + @override + Map createParams(Map? param) => + (super.noSuchMethod( + Invocation.method( + #createParams, + [param], + ), + returnValue: {}, + returnValueForMissingStub: {}, + ) as Map); + + @override + _i8.Future join(_i6.JoinChannelOptions? options) => (super.noSuchMethod( + Invocation.method( + #join, + [options], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future renewToken(String? token) => (super.noSuchMethod( + Invocation.method( + #renewToken, + [token], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future leave() => (super.noSuchMethod( + Invocation.method( + #leave, + [], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getChannelName() => (super.noSuchMethod( + Invocation.method( + #getChannelName, + [], + ), + returnValue: _i8.Future.value(_i10.dummyValue( + this, + Invocation.method( + #getChannelName, + [], + ), + )), + returnValueForMissingStub: + _i8.Future.value(_i10.dummyValue( + this, + Invocation.method( + #getChannelName, + [], + ), + )), + ) as _i8.Future); + + @override + _i8.Future joinTopic({ + required String? topic, + required _i6.JoinTopicOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #joinTopic, + [], + { + #topic: topic, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future publishTopicMessage({ + required String? topic, + required String? message, + required int? length, + required _i12.TopicMessageOptions? option, + }) => + (super.noSuchMethod( + Invocation.method( + #publishTopicMessage, + [], + { + #topic: topic, + #message: message, + #length: length, + #option: option, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future leaveTopic(String? topic) => (super.noSuchMethod( + Invocation.method( + #leaveTopic, + [topic], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future subscribeTopic({ + required String? topic, + required _i6.TopicOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribeTopic, + [], + { + #topic: topic, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future unsubscribeTopic({ + required String? topic, + required _i6.TopicOptions? options, + }) => + (super.noSuchMethod( + Invocation.method( + #unsubscribeTopic, + [], + { + #topic: topic, + #options: options, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future getSubscribedUserList(String? topic) => (super.noSuchMethod( + Invocation.method( + #getSubscribedUserList, + [topic], + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future release() => (super.noSuchMethod( + Invocation.method( + #release, + [], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + + @override + _i8.Future publishTextMessage({ + required String? topic, + required String? message, + required int? length, + required _i12.TopicMessageOptions? option, + }) => + (super.noSuchMethod( + Invocation.method( + #publishTextMessage, + [], + { + #topic: topic, + #message: message, + #length: length, + #option: option, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); + + @override + _i8.Future publishBinaryMessage({ + required String? topic, + required _i13.Uint8List? message, + required int? length, + required _i12.TopicMessageOptions? option, + }) => + (super.noSuchMethod( + Invocation.method( + #publishBinaryMessage, + [], + { + #topic: topic, + #message: message, + #length: length, + #option: option, + }, + ), + returnValue: _i8.Future.value(0), + returnValueForMissingStub: _i8.Future.value(0), + ) as _i8.Future); +} + +/// A class which mocks [RtmResultHandlerImpl]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockRtmResultHandlerImpl extends _i1.Mock + implements _i14.RtmResultHandlerImpl { + @override + _i7.RtmEventHandler get rtmEventHandler => (super.noSuchMethod( + Invocation.getter(#rtmEventHandler), + returnValue: _FakeRtmEventHandler_7( + this, + Invocation.getter(#rtmEventHandler), + ), + returnValueForMissingStub: _FakeRtmEventHandler_7( + this, + Invocation.getter(#rtmEventHandler), + ), + ) as _i7.RtmEventHandler); + + @override + set rtmEventHandler(_i7.RtmEventHandler? _rtmEventHandler) => + super.noSuchMethod( + Invocation.setter( + #rtmEventHandler, + _rtmEventHandler, + ), + returnValueForMissingStub: null, + ); + + @override + _i8.Future request(int? requestId) => (super.noSuchMethod( + Invocation.method( + #request, + [requestId], + ), + returnValue: _i10.ifNotNull( + _i10.dummyValueOrNull( + this, + Invocation.method( + #request, + [requestId], + ), + ), + (T v) => _i8.Future.value(v), + ) ?? + _FakeFuture_8( + this, + Invocation.method( + #request, + [requestId], + ), + ), + returnValueForMissingStub: _i10.ifNotNull( + _i10.dummyValueOrNull( + this, + Invocation.method( + #request, + [requestId], + ), + ), + (T v) => _i8.Future.value(v), + ) ?? + _FakeFuture_8( + this, + Invocation.method( + #request, + [requestId], + ), + ), + ) as _i8.Future); + + @override + void response( + int? requestId, + (Object, _i12.RtmErrorCode)? data, + ) => + super.noSuchMethod( + Invocation.method( + #response, + [ + requestId, + data, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onLinkStateEvent(_i11.LinkStateEvent? event) => super.noSuchMethod( + Invocation.method( + #onLinkStateEvent, + [event], + ), + returnValueForMissingStub: null, + ); + + @override + void onMessageEvent(_i11.MessageEvent? event) => super.noSuchMethod( + Invocation.method( + #onMessageEvent, + [event], + ), + returnValueForMissingStub: null, + ); + + @override + void onPresenceEvent(_i11.PresenceEvent? event) => super.noSuchMethod( + Invocation.method( + #onPresenceEvent, + [event], + ), + returnValueForMissingStub: null, + ); + + @override + void onTopicEvent(_i11.TopicEvent? event) => super.noSuchMethod( + Invocation.method( + #onTopicEvent, + [event], + ), + returnValueForMissingStub: null, + ); + + @override + void onLockEvent(_i11.LockEvent? event) => super.noSuchMethod( + Invocation.method( + #onLockEvent, + [event], + ), + returnValueForMissingStub: null, + ); + + @override + void onStorageEvent(_i11.StorageEvent? event) => super.noSuchMethod( + Invocation.method( + #onStorageEvent, + [event], + ), + returnValueForMissingStub: null, + ); + + @override + void onConnectionStateChanged( + String? channelName, + _i12.RtmConnectionState? state, + _i12.RtmConnectionChangeReason? reason, + ) => + super.noSuchMethod( + Invocation.method( + #onConnectionStateChanged, + [ + channelName, + state, + reason, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onTokenPrivilegeWillExpire(String? channelName) => super.noSuchMethod( + Invocation.method( + #onTokenPrivilegeWillExpire, + [channelName], + ), + returnValueForMissingStub: null, + ); + + @override + void setListener( + String? key, + Object? listener, + ) => + super.noSuchMethod( + Invocation.method( + #setListener, + [ + key, + listener, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(String? key) => super.noSuchMethod( + Invocation.method( + #removeListener, + [key], + ), + returnValueForMissingStub: null, + ); + + @override + void onJoinResult( + int? requestId, + String? channelName, + String? userId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onJoinResult, + [ + requestId, + channelName, + userId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onLeaveResult( + int? requestId, + String? channelName, + String? userId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onLeaveResult, + [ + requestId, + channelName, + userId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onPublishTopicMessageResult( + int? requestId, + String? channelName, + String? topic, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onPublishTopicMessageResult, + [ + requestId, + channelName, + topic, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onJoinTopicResult( + int? requestId, + String? channelName, + String? userId, + String? topic, + String? meta, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onJoinTopicResult, + [ + requestId, + channelName, + userId, + topic, + meta, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onLeaveTopicResult( + int? requestId, + String? channelName, + String? userId, + String? topic, + String? meta, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onLeaveTopicResult, + [ + requestId, + channelName, + userId, + topic, + meta, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onSubscribeTopicResult( + int? requestId, + String? channelName, + String? userId, + String? topic, + _i12.UserList? succeedUsers, + _i12.UserList? failedUsers, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onSubscribeTopicResult, + [ + requestId, + channelName, + userId, + topic, + succeedUsers, + failedUsers, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onUnsubscribeTopicResult( + int? requestId, + String? channelName, + String? topic, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onUnsubscribeTopicResult, + [ + requestId, + channelName, + topic, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onGetSubscribedUserListResult( + int? requestId, + String? channelName, + String? topic, + _i12.UserList? users, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onGetSubscribedUserListResult, + [ + requestId, + channelName, + topic, + users, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onSubscribeResult( + int? requestId, + String? channelName, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onSubscribeResult, + [ + requestId, + channelName, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onUnsubscribeResult( + int? requestId, + String? channelName, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onUnsubscribeResult, + [ + requestId, + channelName, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onPublishResult( + int? requestId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onPublishResult, + [ + requestId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onLoginResult( + int? requestId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onLoginResult, + [ + requestId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onLogoutResult( + int? requestId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onLogoutResult, + [ + requestId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onRenewTokenResult( + int? requestId, + _i12.RtmServiceType? serverType, + String? channelName, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onRenewTokenResult, + [ + requestId, + serverType, + channelName, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onSetChannelMetadataResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onSetChannelMetadataResult, + [ + requestId, + channelName, + channelType, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onUpdateChannelMetadataResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onUpdateChannelMetadataResult, + [ + requestId, + channelName, + channelType, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onRemoveChannelMetadataResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onRemoveChannelMetadataResult, + [ + requestId, + channelName, + channelType, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onGetChannelMetadataResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + _i6.Metadata? data, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onGetChannelMetadataResult, + [ + requestId, + channelName, + channelType, + data, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onSetUserMetadataResult( + int? requestId, + String? userId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onSetUserMetadataResult, + [ + requestId, + userId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onUpdateUserMetadataResult( + int? requestId, + String? userId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onUpdateUserMetadataResult, + [ + requestId, + userId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onRemoveUserMetadataResult( + int? requestId, + String? userId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onRemoveUserMetadataResult, + [ + requestId, + userId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onGetUserMetadataResult( + int? requestId, + String? userId, + _i6.Metadata? data, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onGetUserMetadataResult, + [ + requestId, + userId, + data, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onSubscribeUserMetadataResult( + int? requestId, + String? userId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onSubscribeUserMetadataResult, + [ + requestId, + userId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onUnsubscribeUserMetadataResult( + int? requestId, + String? userId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onUnsubscribeUserMetadataResult, + [ + requestId, + userId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onSetLockResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + String? lockName, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onSetLockResult, + [ + requestId, + channelName, + channelType, + lockName, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onRemoveLockResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + String? lockName, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onRemoveLockResult, + [ + requestId, + channelName, + channelType, + lockName, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onReleaseLockResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + String? lockName, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onReleaseLockResult, + [ + requestId, + channelName, + channelType, + lockName, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onAcquireLockResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + String? lockName, + _i12.RtmErrorCode? errorCode, + String? errorDetails, + ) => + super.noSuchMethod( + Invocation.method( + #onAcquireLockResult, + [ + requestId, + channelName, + channelType, + lockName, + errorCode, + errorDetails, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onRevokeLockResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + String? lockName, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onRevokeLockResult, + [ + requestId, + channelName, + channelType, + lockName, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onGetLocksResult( + int? requestId, + String? channelName, + _i12.RtmChannelType? channelType, + List<_i12.LockDetail>? lockDetailList, + int? count, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onGetLocksResult, + [ + requestId, + channelName, + channelType, + lockDetailList, + count, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onWhoNowResult( + int? requestId, + List<_i12.UserState>? userStateList, + int? count, + String? nextPage, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onWhoNowResult, + [ + requestId, + userStateList, + count, + nextPage, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onGetOnlineUsersResult( + int? requestId, + List<_i12.UserState>? userStateList, + int? count, + String? nextPage, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onGetOnlineUsersResult, + [ + requestId, + userStateList, + count, + nextPage, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onWhereNowResult( + int? requestId, + List<_i12.ChannelInfo>? channels, + int? count, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onWhereNowResult, + [ + requestId, + channels, + count, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onGetUserChannelsResult( + int? requestId, + _i12.ChannelInfo? channels, + int? count, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onGetUserChannelsResult, + [ + requestId, + channels, + count, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onPresenceSetStateResult( + int? requestId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onPresenceSetStateResult, + [ + requestId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onPresenceRemoveStateResult( + int? requestId, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onPresenceRemoveStateResult, + [ + requestId, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void onPresenceGetStateResult( + int? requestId, + _i12.UserState? state, + _i12.RtmErrorCode? errorCode, + ) => + super.noSuchMethod( + Invocation.method( + #onPresenceGetStateResult, + [ + requestId, + state, + errorCode, + ], + ), + returnValueForMissingStub: null, + ); +} diff --git a/test_shard/integration_test_app/test/apis_unit_test.dart b/test_shard/integration_test_app/test/apis_unit_test.dart new file mode 100644 index 0000000..d4a6725 --- /dev/null +++ b/test_shard/integration_test_app/test/apis_unit_test.dart @@ -0,0 +1,14 @@ +import 'generated/rtmclient_unit_test.generated.dart' as rtmclient_unit_test; +import 'generated/rtmlock_unit_test.generated.dart' as rtmlock_unit_test; +import 'rtmpresence_unit_test_cases.dart' as rtmpresence_unit_test; +import 'generated/rtmstorage_unit_test.generated.dart' as rtmstorage_unit_test; +import 'generated/streamchannel_unit_test.generated.dart' + as streamchannel_unit_test; + +void main() { + rtmclient_unit_test.testCases(); + rtmlock_unit_test.testCases(); + rtmpresence_unit_test.testCases(); + rtmstorage_unit_test.testCases(); + streamchannel_unit_test.testCases(); +} diff --git a/test_shard/integration_test_app/test/generated/rtmclient_unit_test.generated.dart b/test_shard/integration_test_app/test/generated/rtmclient_unit_test.generated.dart new file mode 100644 index 0000000..d5a399e --- /dev/null +++ b/test_shard/integration_test_app/test/generated/rtmclient_unit_test.generated.dart @@ -0,0 +1,442 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/agora_rtm_client_impl_override.dart' + as agora_rtm_client_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + test( + 'RtmClient.login', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'login'); + LoginResult theLoginResult = LoginResult(); + final mockResultHandlerReturnValue = (theLoginResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theLoginResult); + int mockRequestId = 1; + { + String token = "hello"; + when(mockRtmClientNativeBinding.login( + token, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String token = "hello"; + final ret = await rtmClient.login( + token, + ); + expect(ret, expectedResultHandlerReturnValue); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.logout', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'logout'); + LogoutResult theLogoutResult = LogoutResult(); + final mockResultHandlerReturnValue = (theLogoutResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theLogoutResult); + int mockRequestId = 1; + { + when(mockRtmClientNativeBinding.logout()) + .thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + final ret = await rtmClient.logout(); + expect(ret, expectedResultHandlerReturnValue); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.getStorage', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + {} + + rtmClient.getStorage(); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.getLock', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + {} + + rtmClient.getLock(); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.getPresence', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + {} + + rtmClient.getPresence(); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.renewToken', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'renewToken'); + RtmServiceType theRenewTokenResultServerType = RtmServiceType.none; + String theRenewTokenResultChannelName = "hello"; + RenewTokenResult theRenewTokenResult = RenewTokenResult( + serverType: theRenewTokenResultServerType, + channelName: theRenewTokenResultChannelName, + ); + final mockResultHandlerReturnValue = + (theRenewTokenResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theRenewTokenResult); + int mockRequestId = 1; + { + String token = "hello"; + when(mockRtmClientNativeBinding.renewToken( + token, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String token = "hello"; + final ret = await rtmClient.renewToken( + token, + ); + expect(ret, expectedResultHandlerReturnValue); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.publish', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'publish'); + PublishResult thePublishResult = PublishResult(); + final mockResultHandlerReturnValue = (thePublishResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, thePublishResult); + int mockRequestId = 1; + { + String channelName = "hello"; + String message = "hello"; + int length = 5; + RtmChannelType optionChannelType = RtmChannelType.none; + RtmMessageType optionMessageType = RtmMessageType.binary; + String optionCustomType = "hello"; + PublishOptions option = PublishOptions( + channelType: optionChannelType, + messageType: optionMessageType, + customType: optionCustomType, + ); + when(mockRtmClientNativeBinding.publish( + channelName: channelName, + message: message, + length: length, + option: argThat( + isA(), + named: 'option', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + String message = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String customType = "hello"; + final ret = await rtmClient.publish( + channelName, + message, + channelType: channelType, + customType: customType, + ); + expect(ret, expectedResultHandlerReturnValue); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.subscribe', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'subscribe'); + String theSubscribeResultChannelName = "hello"; + SubscribeResult theSubscribeResult = SubscribeResult( + channelName: theSubscribeResultChannelName, + ); + final mockResultHandlerReturnValue = + (theSubscribeResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theSubscribeResult); + int mockRequestId = 1; + { + String channelName = "hello"; + bool optionsWithMessage = true; + bool optionsWithMetadata = true; + bool optionsWithPresence = true; + bool optionsWithLock = true; + bool optionsBeQuiet = true; + SubscribeOptions options = SubscribeOptions( + withMessage: optionsWithMessage, + withMetadata: optionsWithMetadata, + withPresence: optionsWithPresence, + withLock: optionsWithLock, + beQuiet: optionsBeQuiet, + ); + when(mockRtmClientNativeBinding.subscribe( + channelName: channelName, + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + bool withMessage = true; + bool withMetadata = true; + bool withPresence = true; + bool withLock = true; + bool beQuiet = true; + final ret = await rtmClient.subscribe( + channelName, + withMessage: withMessage, + withMetadata: withMetadata, + withPresence: withPresence, + withLock: withLock, + beQuiet: beQuiet, + ); + expect(ret, expectedResultHandlerReturnValue); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.unsubscribe', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'unsubscribe'); + String theUnsubscribeResultChannelName = "hello"; + UnsubscribeResult theUnsubscribeResult = UnsubscribeResult( + channelName: theUnsubscribeResultChannelName, + ); + final mockResultHandlerReturnValue = + (theUnsubscribeResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theUnsubscribeResult); + int mockRequestId = 1; + { + String channelName = "hello"; + when(mockRtmClientNativeBinding.unsubscribe( + channelName, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + final ret = await rtmClient.unsubscribe( + channelName, + ); + expect(ret, expectedResultHandlerReturnValue); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.setParameters', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + { + String parameters = "hello"; + } + + String parameters = "hello"; + await rtmClient.setParameters( + parameters, + ); + + await rtmClient.release(); + }, + ); + + test( + 'RtmClient.publishBinaryMessage', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'publishBinaryMessage'); + PublishResult thePublishResult = PublishResult(); + final mockResultHandlerReturnValue = (thePublishResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, thePublishResult); + int mockRequestId = 1; + { + String channelName = "hello"; + Uint8List message = Uint8List.fromList([1, 1, 1, 1, 1]); + int length = 5; + RtmChannelType optionChannelType = RtmChannelType.none; + RtmMessageType optionMessageType = RtmMessageType.binary; + String optionCustomType = "hello"; + PublishOptions option = PublishOptions( + channelType: optionChannelType, + messageType: optionMessageType, + customType: optionCustomType, + ); + when(mockRtmClientNativeBinding.publishBinaryMessage( + channelName: channelName, + message: message, + length: length, + option: argThat( + isA(), + named: 'option', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + Uint8List message = Uint8List.fromList([1, 1, 1, 1, 1]); + RtmChannelType channelType = RtmChannelType.none; + String customType = "hello"; + final ret = await rtmClient.publishBinaryMessage( + channelName, + message, + channelType: channelType, + customType: customType, + ); + expect(ret, expectedResultHandlerReturnValue); + + await rtmClient.release(); + }, + ); +} diff --git a/test_shard/integration_test_app/test/generated/rtmlock_unit_test.generated.dart b/test_shard/integration_test_app/test/generated/rtmlock_unit_test.generated.dart new file mode 100644 index 0000000..8907220 --- /dev/null +++ b/test_shard/integration_test_app/test/generated/rtmlock_unit_test.generated.dart @@ -0,0 +1,314 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_rtm_lock_impl.dart' + as agora_rtm_lock_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + test( + 'RtmLock.setLock', + () async { + final mockRtmLockNativeBinding = MockRtmLockImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmLock rtmLock = agora_rtm_lock_impl.RtmLockImpl( + mockRtmLockNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'setLock'); + RtmChannelType theSetLockResultChannelType = RtmChannelType.none; + String theSetLockResultChannelName = "hello"; + String theSetLockResultLockName = "hello"; + SetLockResult theSetLockResult = SetLockResult( + channelName: theSetLockResultChannelName, + channelType: theSetLockResultChannelType, + lockName: theSetLockResultLockName, + ); + final mockResultHandlerReturnValue = (theSetLockResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theSetLockResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + int ttl = 5; + when(mockRtmLockNativeBinding.setLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + ttl: ttl, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + int ttl = 5; + final ret = await rtmLock.setLock( + channelName, + channelType, + lockName, + ttl: ttl, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmLock.getLocks', + () async { + final mockRtmLockNativeBinding = MockRtmLockImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmLock rtmLock = agora_rtm_lock_impl.RtmLockImpl( + mockRtmLockNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'getLocks'); + RtmChannelType theGetLocksResultChannelType = RtmChannelType.none; + String theGetLocksResultChannelName = "hello"; + List theGetLocksResultLockDetailList = []; + int theGetLocksResultCount = 5; + GetLocksResult theGetLocksResult = GetLocksResult( + channelName: theGetLocksResultChannelName, + channelType: theGetLocksResultChannelType, + lockDetailList: theGetLocksResultLockDetailList, + count: theGetLocksResultCount, + ); + final mockResultHandlerReturnValue = (theGetLocksResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theGetLocksResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + when(mockRtmLockNativeBinding.getLocks( + channelName: channelName, + channelType: channelType, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + final ret = await rtmLock.getLocks( + channelName, + channelType, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmLock.removeLock', + () async { + final mockRtmLockNativeBinding = MockRtmLockImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmLock rtmLock = agora_rtm_lock_impl.RtmLockImpl( + mockRtmLockNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'removeLock'); + RtmChannelType theRemoveLockResultChannelType = RtmChannelType.none; + String theRemoveLockResultChannelName = "hello"; + String theRemoveLockResultLockName = "hello"; + RemoveLockResult theRemoveLockResult = RemoveLockResult( + channelName: theRemoveLockResultChannelName, + channelType: theRemoveLockResultChannelType, + lockName: theRemoveLockResultLockName, + ); + final mockResultHandlerReturnValue = + (theRemoveLockResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theRemoveLockResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + when(mockRtmLockNativeBinding.removeLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + final ret = await rtmLock.removeLock( + channelName, + channelType, + lockName, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmLock.acquireLock', + () async { + final mockRtmLockNativeBinding = MockRtmLockImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmLock rtmLock = agora_rtm_lock_impl.RtmLockImpl( + mockRtmLockNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'acquireLock'); + RtmChannelType theAcquireLockResultChannelType = RtmChannelType.none; + String theAcquireLockResultChannelName = "hello"; + String theAcquireLockResultLockName = "hello"; + String theAcquireLockResultErrorDetails = "hello"; + AcquireLockResult theAcquireLockResult = AcquireLockResult( + channelName: theAcquireLockResultChannelName, + channelType: theAcquireLockResultChannelType, + lockName: theAcquireLockResultLockName, + errorDetails: theAcquireLockResultErrorDetails, + ); + final mockResultHandlerReturnValue = + (theAcquireLockResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theAcquireLockResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + bool retry = true; + when(mockRtmLockNativeBinding.acquireLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + retry: retry, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + bool retry = true; + final ret = await rtmLock.acquireLock( + channelName, + channelType, + lockName, + retry: retry, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmLock.releaseLock', + () async { + final mockRtmLockNativeBinding = MockRtmLockImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmLock rtmLock = agora_rtm_lock_impl.RtmLockImpl( + mockRtmLockNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'releaseLock'); + RtmChannelType theReleaseLockResultChannelType = RtmChannelType.none; + String theReleaseLockResultChannelName = "hello"; + String theReleaseLockResultLockName = "hello"; + ReleaseLockResult theReleaseLockResult = ReleaseLockResult( + channelName: theReleaseLockResultChannelName, + channelType: theReleaseLockResultChannelType, + lockName: theReleaseLockResultLockName, + ); + final mockResultHandlerReturnValue = + (theReleaseLockResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theReleaseLockResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + when(mockRtmLockNativeBinding.releaseLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + final ret = await rtmLock.releaseLock( + channelName, + channelType, + lockName, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmLock.revokeLock', + () async { + final mockRtmLockNativeBinding = MockRtmLockImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmLock rtmLock = agora_rtm_lock_impl.RtmLockImpl( + mockRtmLockNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'revokeLock'); + RtmChannelType theRevokeLockResultChannelType = RtmChannelType.none; + String theRevokeLockResultChannelName = "hello"; + String theRevokeLockResultLockName = "hello"; + RevokeLockResult theRevokeLockResult = RevokeLockResult( + channelName: theRevokeLockResultChannelName, + channelType: theRevokeLockResultChannelType, + lockName: theRevokeLockResultLockName, + ); + final mockResultHandlerReturnValue = + (theRevokeLockResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theRevokeLockResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + String owner = "hello"; + when(mockRtmLockNativeBinding.revokeLock( + channelName: channelName, + channelType: channelType, + lockName: lockName, + owner: owner, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String lockName = "hello"; + String owner = "hello"; + final ret = await rtmLock.revokeLock( + channelName, + channelType, + lockName, + owner, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); +} diff --git a/test_shard/integration_test_app/test/generated/rtmpresence_unit_test.generated.dart b/test_shard/integration_test_app/test/generated/rtmpresence_unit_test.generated.dart new file mode 100644 index 0000000..ca69554 --- /dev/null +++ b/test_shard/integration_test_app/test/generated/rtmpresence_unit_test.generated.dart @@ -0,0 +1,314 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_rtm_presence_impl.dart' + as rtm_presence_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + test( + 'RtmPresence.whoNow', + () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'whoNow'); + List theWhoNowResultUserStateList = []; + int theWhoNowResultCount = 5; + String theWhoNowResultNextPage = "hello"; + WhoNowResult theWhoNowResult = WhoNowResult( + userStateList: theWhoNowResultUserStateList, + count: theWhoNowResultCount, + nextPage: theWhoNowResultNextPage, + ); + final mockResultHandlerReturnValue = (theWhoNowResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theWhoNowResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + bool optionsIncludeUserId = true; + bool optionsIncludeState = true; + String optionsPage = "hello"; + PresenceOptions options = PresenceOptions( + includeUserId: optionsIncludeUserId, + includeState: optionsIncludeState, + page: optionsPage, + ); + when(mockRtmPresenceNativeBinding.whoNow( + channelName: channelName, + channelType: channelType, + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + bool includeUserId = true; + bool includeState = true; + String page = "hello"; + final ret = await rtmPresence.whoNow( + channelName, + channelType, + includeUserId: includeUserId, + includeState: includeState, + page: page, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmPresence.whereNow', + () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'whereNow'); + List theWhereNowResultChannels = []; + int theWhereNowResultCount = 5; + WhereNowResult theWhereNowResult = WhereNowResult( + channels: theWhereNowResultChannels, + count: theWhereNowResultCount, + ); + final mockResultHandlerReturnValue = (theWhereNowResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theWhereNowResult); + int mockRequestId = 1; + { + String userId = "hello"; + when(mockRtmPresenceNativeBinding.whereNow( + userId, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + final ret = await rtmPresence.whereNow( + userId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmPresence.removeState', + () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'removeState'); + RemoveStateResult theRemoveStateResult = RemoveStateResult(); + final mockResultHandlerReturnValue = + (theRemoveStateResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theRemoveStateResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + List keys = List.filled(5, "hello"); + int count = 5; + when(mockRtmPresenceNativeBinding.removeState( + channelName: channelName, + channelType: channelType, + keys: argThat( + isA>(), + named: 'keys', + ), + count: count, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + List states = List.filled(5, "hello"); + final ret = await rtmPresence.removeState( + channelName, + channelType, + states: states, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmPresence.getState', + () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'getState'); + String stateUserId = "hello"; + List stateStates = []; + UserState theGetStateResultState = UserState( + userId: stateUserId, + states: stateStates, + ); + GetStateResult theGetStateResult = GetStateResult( + state: theGetStateResultState, + ); + final mockResultHandlerReturnValue = (theGetStateResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theGetStateResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String userId = "hello"; + when(mockRtmPresenceNativeBinding.getState( + channelName: channelName, + channelType: channelType, + userId: userId, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + String userId = "hello"; + final ret = await rtmPresence.getState( + channelName, + channelType, + userId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmPresence.getOnlineUsers', + () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'getOnlineUsers'); + List theGetOnlineUsersResultUserStateList = []; + int theGetOnlineUsersResultCount = 5; + String theGetOnlineUsersResultNextPage = "hello"; + GetOnlineUsersResult theGetOnlineUsersResult = GetOnlineUsersResult( + userStateList: theGetOnlineUsersResultUserStateList, + count: theGetOnlineUsersResultCount, + nextPage: theGetOnlineUsersResultNextPage, + ); + final mockResultHandlerReturnValue = + (theGetOnlineUsersResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theGetOnlineUsersResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + bool optionsIncludeUserId = true; + bool optionsIncludeState = true; + String optionsPage = "hello"; + GetOnlineUsersOptions options = GetOnlineUsersOptions( + includeUserId: optionsIncludeUserId, + includeState: optionsIncludeState, + page: optionsPage, + ); + when(mockRtmPresenceNativeBinding.getOnlineUsers( + channelName: channelName, + channelType: channelType, + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + bool includeUserId = true; + bool includeState = true; + String page = "hello"; + final ret = await rtmPresence.getOnlineUsers( + channelName, + channelType, + includeUserId: includeUserId, + includeState: includeState, + page: page, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmPresence.getUserChannels', + () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'getUserChannels'); + RtmChannelType channelsChannelType = RtmChannelType.none; + String channelsChannelName = "hello"; + ChannelInfo theGetUserChannelsResultChannels = ChannelInfo( + channelName: channelsChannelName, + channelType: channelsChannelType, + ); + int theGetUserChannelsResultCount = 5; + GetUserChannelsResult theGetUserChannelsResult = GetUserChannelsResult( + channels: theGetUserChannelsResultChannels, + count: theGetUserChannelsResultCount, + ); + final mockResultHandlerReturnValue = + (theGetUserChannelsResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theGetUserChannelsResult); + int mockRequestId = 1; + { + String userId = "hello"; + when(mockRtmPresenceNativeBinding.getUserChannels( + userId, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + final ret = await rtmPresence.getUserChannels( + userId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); +} diff --git a/test_shard/integration_test_app/test/generated/rtmstorage_unit_test.generated.dart b/test_shard/integration_test_app/test/generated/rtmstorage_unit_test.generated.dart new file mode 100644 index 0000000..1d33461 --- /dev/null +++ b/test_shard/integration_test_app/test/generated/rtmstorage_unit_test.generated.dart @@ -0,0 +1,726 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_rtm_storage_impl.dart' + as rtm_storage_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + test( + 'RtmStorage.setChannelMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'setChannelMetadata'); + RtmChannelType theSetChannelMetadataResultChannelType = + RtmChannelType.none; + String theSetChannelMetadataResultChannelName = "hello"; + SetChannelMetadataResult theSetChannelMetadataResult = + SetChannelMetadataResult( + channelName: theSetChannelMetadataResultChannelName, + channelType: theSetChannelMetadataResultChannelType, + ); + final mockResultHandlerReturnValue = + (theSetChannelMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theSetChannelMetadataResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + String lockName = "hello"; + when(mockRtmStorageNativeBinding.setChannelMetadata( + channelName: channelName, + channelType: channelType, + data: argThat( + isA(), + named: 'data', + ), + options: argThat( + isA(), + named: 'options', + ), + lockName: lockName, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + final List metadata = () { + String metadataItemKey = "hello"; + String metadataItemValue = "hello"; + String metadataItemAuthorUserId = "hello"; + int metadataItemRevision = 5; + int metadataItemUpdateTs = 5; + MetadataItem metadataItem = MetadataItem( + key: metadataItemKey, + value: metadataItemValue, + authorUserId: metadataItemAuthorUserId, + revision: metadataItemRevision, + updateTs: metadataItemUpdateTs, + ); + + return List.filled(5, metadataItem); + }(); + + int majorRevision = 5; + bool recordTs = true; + bool recordUserId = true; + String lockName = "hello"; + final ret = await rtmStorage.setChannelMetadata( + channelName, + channelType, + metadata, + majorRevision: majorRevision, + recordTs: recordTs, + recordUserId: recordUserId, + lockName: lockName, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.updateChannelMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'updateChannelMetadata'); + RtmChannelType theUpdateChannelMetadataResultChannelType = + RtmChannelType.none; + String theUpdateChannelMetadataResultChannelName = "hello"; + UpdateChannelMetadataResult theUpdateChannelMetadataResult = + UpdateChannelMetadataResult( + channelName: theUpdateChannelMetadataResultChannelName, + channelType: theUpdateChannelMetadataResultChannelType, + ); + final mockResultHandlerReturnValue = + (theUpdateChannelMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theUpdateChannelMetadataResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + String lockName = "hello"; + when(mockRtmStorageNativeBinding.updateChannelMetadata( + channelName: channelName, + channelType: channelType, + data: argThat( + isA(), + named: 'data', + ), + options: argThat( + isA(), + named: 'options', + ), + lockName: lockName, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + final List metadata = () { + String metadataItemKey = "hello"; + String metadataItemValue = "hello"; + String metadataItemAuthorUserId = "hello"; + int metadataItemRevision = 5; + int metadataItemUpdateTs = 5; + MetadataItem metadataItem = MetadataItem( + key: metadataItemKey, + value: metadataItemValue, + authorUserId: metadataItemAuthorUserId, + revision: metadataItemRevision, + updateTs: metadataItemUpdateTs, + ); + + return List.filled(5, metadataItem); + }(); + + int majorRevision = 5; + bool recordTs = true; + bool recordUserId = true; + String lockName = "hello"; + final ret = await rtmStorage.updateChannelMetadata( + channelName, + channelType, + metadata, + majorRevision: majorRevision, + recordTs: recordTs, + recordUserId: recordUserId, + lockName: lockName, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.removeChannelMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'removeChannelMetadata'); + RtmChannelType theRemoveChannelMetadataResultChannelType = + RtmChannelType.none; + String theRemoveChannelMetadataResultChannelName = "hello"; + RemoveChannelMetadataResult theRemoveChannelMetadataResult = + RemoveChannelMetadataResult( + channelName: theRemoveChannelMetadataResultChannelName, + channelType: theRemoveChannelMetadataResultChannelType, + ); + final mockResultHandlerReturnValue = + (theRemoveChannelMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theRemoveChannelMetadataResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + String lockName = "hello"; + when(mockRtmStorageNativeBinding.removeChannelMetadata( + channelName: channelName, + channelType: channelType, + data: argThat( + isA(), + named: 'data', + ), + options: argThat( + isA(), + named: 'options', + ), + lockName: lockName, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + int majorRevision = 5; + final List metadata = () { + String metadataItemKey = "hello"; + String metadataItemValue = "hello"; + String metadataItemAuthorUserId = "hello"; + int metadataItemRevision = 5; + int metadataItemUpdateTs = 5; + MetadataItem metadataItem = MetadataItem( + key: metadataItemKey, + value: metadataItemValue, + authorUserId: metadataItemAuthorUserId, + revision: metadataItemRevision, + updateTs: metadataItemUpdateTs, + ); + + return List.filled(5, metadataItem); + }(); + + bool recordTs = true; + bool recordUserId = true; + String lockName = "hello"; + final ret = await rtmStorage.removeChannelMetadata( + channelName, + channelType, + majorRevision: majorRevision, + metadata: metadata, + recordTs: recordTs, + recordUserId: recordUserId, + lockName: lockName, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.getChannelMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'getChannelMetadata'); + RtmChannelType theGetChannelMetadataResultChannelType = + RtmChannelType.none; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata theGetChannelMetadataResultData = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + String theGetChannelMetadataResultChannelName = "hello"; + GetChannelMetadataResult theGetChannelMetadataResult = + GetChannelMetadataResult( + channelName: theGetChannelMetadataResultChannelName, + channelType: theGetChannelMetadataResultChannelType, + data: theGetChannelMetadataResultData, + ); + final mockResultHandlerReturnValue = + (theGetChannelMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theGetChannelMetadataResult); + int mockRequestId = 1; + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + when(mockRtmStorageNativeBinding.getChannelMetadata( + channelName: channelName, + channelType: channelType, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + final ret = await rtmStorage.getChannelMetadata( + channelName, + channelType, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.setUserMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'setUserMetadata'); + String theSetUserMetadataResultUserId = "hello"; + SetUserMetadataResult theSetUserMetadataResult = SetUserMetadataResult( + userId: theSetUserMetadataResultUserId, + ); + final mockResultHandlerReturnValue = + (theSetUserMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theSetUserMetadataResult); + int mockRequestId = 1; + { + String userId = "hello"; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + when(mockRtmStorageNativeBinding.setUserMetadata( + userId: userId, + data: argThat( + isA(), + named: 'data', + ), + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + final List metadata = () { + String metadataItemKey = "hello"; + String metadataItemValue = "hello"; + String metadataItemAuthorUserId = "hello"; + int metadataItemRevision = 5; + int metadataItemUpdateTs = 5; + MetadataItem metadataItem = MetadataItem( + key: metadataItemKey, + value: metadataItemValue, + authorUserId: metadataItemAuthorUserId, + revision: metadataItemRevision, + updateTs: metadataItemUpdateTs, + ); + + return List.filled(5, metadataItem); + }(); + + int majorRevision = 5; + bool recordTs = true; + bool recordUserId = true; + final ret = await rtmStorage.setUserMetadata( + userId, + metadata, + majorRevision: majorRevision, + recordTs: recordTs, + recordUserId: recordUserId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.updateUserMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'updateUserMetadata'); + String theUpdateUserMetadataResultUserId = "hello"; + UpdateUserMetadataResult theUpdateUserMetadataResult = + UpdateUserMetadataResult( + userId: theUpdateUserMetadataResultUserId, + ); + final mockResultHandlerReturnValue = + (theUpdateUserMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theUpdateUserMetadataResult); + int mockRequestId = 1; + { + String userId = "hello"; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + when(mockRtmStorageNativeBinding.updateUserMetadata( + userId: userId, + data: argThat( + isA(), + named: 'data', + ), + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + final List metadata = () { + String metadataItemKey = "hello"; + String metadataItemValue = "hello"; + String metadataItemAuthorUserId = "hello"; + int metadataItemRevision = 5; + int metadataItemUpdateTs = 5; + MetadataItem metadataItem = MetadataItem( + key: metadataItemKey, + value: metadataItemValue, + authorUserId: metadataItemAuthorUserId, + revision: metadataItemRevision, + updateTs: metadataItemUpdateTs, + ); + + return List.filled(5, metadataItem); + }(); + + int majorRevision = 5; + bool recordTs = true; + bool recordUserId = true; + final ret = await rtmStorage.updateUserMetadata( + userId, + metadata, + majorRevision: majorRevision, + recordTs: recordTs, + recordUserId: recordUserId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.removeUserMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'removeUserMetadata'); + String theRemoveUserMetadataResultUserId = "hello"; + RemoveUserMetadataResult theRemoveUserMetadataResult = + RemoveUserMetadataResult( + userId: theRemoveUserMetadataResultUserId, + ); + final mockResultHandlerReturnValue = + (theRemoveUserMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theRemoveUserMetadataResult); + int mockRequestId = 1; + { + String userId = "hello"; + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata data = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + bool optionsRecordTs = true; + bool optionsRecordUserId = true; + MetadataOptions options = MetadataOptions( + recordTs: optionsRecordTs, + recordUserId: optionsRecordUserId, + ); + when(mockRtmStorageNativeBinding.removeUserMetadata( + userId: userId, + data: argThat( + isA(), + named: 'data', + ), + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + int majorRevision = 5; + final List metadata = () { + String metadataItemKey = "hello"; + String metadataItemValue = "hello"; + String metadataItemAuthorUserId = "hello"; + int metadataItemRevision = 5; + int metadataItemUpdateTs = 5; + MetadataItem metadataItem = MetadataItem( + key: metadataItemKey, + value: metadataItemValue, + authorUserId: metadataItemAuthorUserId, + revision: metadataItemRevision, + updateTs: metadataItemUpdateTs, + ); + + return List.filled(5, metadataItem); + }(); + + bool recordTs = true; + bool recordUserId = true; + final ret = await rtmStorage.removeUserMetadata( + userId, + majorRevision: majorRevision, + metadata: metadata, + recordTs: recordTs, + recordUserId: recordUserId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.getUserMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'getUserMetadata'); + int dataMajorRevision = 5; + List dataItems = []; + int dataItemCount = 5; + Metadata theGetUserMetadataResultData = Metadata( + majorRevision: dataMajorRevision, + items: dataItems, + itemCount: dataItemCount, + ); + String theGetUserMetadataResultUserId = "hello"; + GetUserMetadataResult theGetUserMetadataResult = GetUserMetadataResult( + userId: theGetUserMetadataResultUserId, + data: theGetUserMetadataResultData, + ); + final mockResultHandlerReturnValue = + (theGetUserMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theGetUserMetadataResult); + int mockRequestId = 1; + { + String userId = "hello"; + when(mockRtmStorageNativeBinding.getUserMetadata( + userId, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + final ret = await rtmStorage.getUserMetadata( + userId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.subscribeUserMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'subscribeUserMetadata'); + String theSubscribeUserMetadataResultUserId = "hello"; + SubscribeUserMetadataResult theSubscribeUserMetadataResult = + SubscribeUserMetadataResult( + userId: theSubscribeUserMetadataResultUserId, + ); + final mockResultHandlerReturnValue = + (theSubscribeUserMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theSubscribeUserMetadataResult); + int mockRequestId = 1; + { + String userId = "hello"; + when(mockRtmStorageNativeBinding.subscribeUserMetadata( + userId, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + final ret = await rtmStorage.subscribeUserMetadata( + userId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'RtmStorage.unsubscribeUserMetadata', + () async { + final mockRtmStorageNativeBinding = MockRtmStorageImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmStorage rtmStorage = rtm_storage_impl.RtmStorageImpl( + mockRtmStorageNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'unsubscribeUserMetadata'); + String theUnsubscribeUserMetadataResultUserId = "hello"; + UnsubscribeUserMetadataResult theUnsubscribeUserMetadataResult = + UnsubscribeUserMetadataResult( + userId: theUnsubscribeUserMetadataResultUserId, + ); + final mockResultHandlerReturnValue = + (theUnsubscribeUserMetadataResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theUnsubscribeUserMetadataResult); + int mockRequestId = 1; + { + String userId = "hello"; + when(mockRtmStorageNativeBinding.unsubscribeUserMetadata( + userId, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String userId = "hello"; + final ret = await rtmStorage.unsubscribeUserMetadata( + userId, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); +} diff --git a/test_shard/integration_test_app/test/generated/streamchannel_unit_test.generated.dart b/test_shard/integration_test_app/test/generated/streamchannel_unit_test.generated.dart new file mode 100644 index 0000000..80dc80d --- /dev/null +++ b/test_shard/integration_test_app/test/generated/streamchannel_unit_test.generated.dart @@ -0,0 +1,560 @@ +/// GENERATED BY testcase_gen. DO NOT MODIFY BY HAND. + +// ignore_for_file: deprecated_member_use,constant_identifier_names,unused_local_variable,unused_import,unnecessary_import + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:agora_rtm/src/impl/gen/agora_stream_channel_impl.dart' + as stream_channel_impl; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'dart:typed_data'; + +import '../all_mocks.mocks.dart'; + +void testCases() { + test( + 'StreamChannel.join', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'join'); + String theJoinResultChannelName = "hello"; + String theJoinResultUserId = "hello"; + JoinResult theJoinResult = JoinResult( + channelName: theJoinResultChannelName, + userId: theJoinResultUserId, + ); + final mockResultHandlerReturnValue = (theJoinResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theJoinResult); + int mockRequestId = 1; + { + String optionsToken = "hello"; + bool optionsWithMetadata = true; + bool optionsWithPresence = true; + bool optionsWithLock = true; + bool optionsBeQuiet = true; + JoinChannelOptions options = JoinChannelOptions( + token: optionsToken, + withMetadata: optionsWithMetadata, + withPresence: optionsWithPresence, + withLock: optionsWithLock, + beQuiet: optionsBeQuiet, + ); + when(mockStreamChannelNativeBinding.join( + argThat(isA()), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String token = "hello"; + bool withMetadata = true; + bool withPresence = true; + bool withLock = true; + bool beQuiet = true; + final ret = await streamChannel.join( + token: token, + withMetadata: withMetadata, + withPresence: withPresence, + withLock: withLock, + beQuiet: beQuiet, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.renewToken', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'renewToken'); + RtmServiceType theRenewTokenResultServerType = RtmServiceType.none; + String theRenewTokenResultChannelName = "hello"; + RenewTokenResult theRenewTokenResult = RenewTokenResult( + serverType: theRenewTokenResultServerType, + channelName: theRenewTokenResultChannelName, + ); + final mockResultHandlerReturnValue = + (theRenewTokenResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theRenewTokenResult); + int mockRequestId = 1; + { + String token = "hello"; + when(mockStreamChannelNativeBinding.renewToken( + token, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String token = "hello"; + final ret = await streamChannel.renewToken( + token, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.leave', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'leave'); + String theLeaveResultChannelName = "hello"; + String theLeaveResultUserId = "hello"; + LeaveResult theLeaveResult = LeaveResult( + channelName: theLeaveResultChannelName, + userId: theLeaveResultUserId, + ); + final mockResultHandlerReturnValue = (theLeaveResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theLeaveResult); + int mockRequestId = 1; + { + when(mockStreamChannelNativeBinding.leave()) + .thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + final ret = await streamChannel.leave(); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.getChannelName', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + String theString = "hello"; + { + when(mockStreamChannelNativeBinding.getChannelName()) + .thenAnswer((_) async => theString); + } + + await streamChannel.getChannelName(); + }, + ); + + test( + 'StreamChannel.joinTopic', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'joinTopic'); + String theJoinTopicResultChannelName = "hello"; + String theJoinTopicResultUserId = "hello"; + String theJoinTopicResultTopic = "hello"; + String theJoinTopicResultMeta = "hello"; + JoinTopicResult theJoinTopicResult = JoinTopicResult( + channelName: theJoinTopicResultChannelName, + userId: theJoinTopicResultUserId, + topic: theJoinTopicResultTopic, + meta: theJoinTopicResultMeta, + ); + final mockResultHandlerReturnValue = + (theJoinTopicResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theJoinTopicResult); + int mockRequestId = 1; + { + String topic = "hello"; + RtmMessageQos optionsQos = RtmMessageQos.unordered; + RtmMessagePriority optionsPriority = RtmMessagePriority.highest; + String optionsMeta = "hello"; + bool optionsSyncWithMedia = true; + JoinTopicOptions options = JoinTopicOptions( + qos: optionsQos, + priority: optionsPriority, + meta: optionsMeta, + syncWithMedia: optionsSyncWithMedia, + ); + when(mockStreamChannelNativeBinding.joinTopic( + topic: topic, + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String topic = "hello"; + RtmMessageQos qos = RtmMessageQos.unordered; + RtmMessagePriority priority = RtmMessagePriority.highest; + String meta = "hello"; + bool syncWithMedia = true; + final ret = await streamChannel.joinTopic( + topic, + qos: qos, + priority: priority, + meta: meta, + syncWithMedia: syncWithMedia, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.leaveTopic', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'leaveTopic'); + String theLeaveTopicResultChannelName = "hello"; + String theLeaveTopicResultUserId = "hello"; + String theLeaveTopicResultTopic = "hello"; + String theLeaveTopicResultMeta = "hello"; + LeaveTopicResult theLeaveTopicResult = LeaveTopicResult( + channelName: theLeaveTopicResultChannelName, + userId: theLeaveTopicResultUserId, + topic: theLeaveTopicResultTopic, + meta: theLeaveTopicResultMeta, + ); + final mockResultHandlerReturnValue = + (theLeaveTopicResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, theLeaveTopicResult); + int mockRequestId = 1; + { + String topic = "hello"; + when(mockStreamChannelNativeBinding.leaveTopic( + topic, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String topic = "hello"; + final ret = await streamChannel.leaveTopic( + topic, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.subscribeTopic', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'subscribeTopic'); + String theSubscribeTopicResultChannelName = "hello"; + String theSubscribeTopicResultUserId = "hello"; + String theSubscribeTopicResultTopic = "hello"; + List theSubscribeTopicResultSucceedUsers = + List.filled(5, "hello"); + List theSubscribeTopicResultFailedUsers = List.filled(5, "hello"); + SubscribeTopicResult theSubscribeTopicResult = SubscribeTopicResult( + channelName: theSubscribeTopicResultChannelName, + userId: theSubscribeTopicResultUserId, + topic: theSubscribeTopicResultTopic, + succeedUsers: theSubscribeTopicResultSucceedUsers, + failedUsers: theSubscribeTopicResultFailedUsers, + ); + final mockResultHandlerReturnValue = + (theSubscribeTopicResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theSubscribeTopicResult); + int mockRequestId = 1; + { + String topic = "hello"; + List optionsUsers = List.filled(5, "hello"); + int optionsUserCount = 5; + TopicOptions options = TopicOptions( + users: optionsUsers, + userCount: optionsUserCount, + ); + when(mockStreamChannelNativeBinding.subscribeTopic( + topic: topic, + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String topic = "hello"; + List users = List.filled(5, "hello"); + final ret = await streamChannel.subscribeTopic( + topic, + users: users, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.unsubscribeTopic', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'unsubscribeTopic'); + String theUnsubscribeTopicResultChannelName = "hello"; + String theUnsubscribeTopicResultTopic = "hello"; + UnsubscribeTopicResult theUnsubscribeTopicResult = UnsubscribeTopicResult( + channelName: theUnsubscribeTopicResultChannelName, + topic: theUnsubscribeTopicResultTopic, + ); + final mockResultHandlerReturnValue = + (theUnsubscribeTopicResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theUnsubscribeTopicResult); + int mockRequestId = 1; + { + String topic = "hello"; + List optionsUsers = List.filled(5, "hello"); + int optionsUserCount = 5; + TopicOptions options = TopicOptions( + users: optionsUsers, + userCount: optionsUserCount, + ); + when(mockStreamChannelNativeBinding.unsubscribeTopic( + topic: topic, + options: argThat( + isA(), + named: 'options', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String topic = "hello"; + List users = List.filled(5, "hello"); + final ret = await streamChannel.unsubscribeTopic( + topic, + users: users, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.getSubscribedUserList', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'getSubscribedUserList'); + List usersUsers = List.filled(5, "hello"); + UserList theGetSubscribedUserListResultUsers = UserList( + users: usersUsers, + ); + String theGetSubscribedUserListResultChannelName = "hello"; + String theGetSubscribedUserListResultTopic = "hello"; + GetSubscribedUserListResult theGetSubscribedUserListResult = + GetSubscribedUserListResult( + channelName: theGetSubscribedUserListResultChannelName, + topic: theGetSubscribedUserListResultTopic, + users: theGetSubscribedUserListResultUsers, + ); + final mockResultHandlerReturnValue = + (theGetSubscribedUserListResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, theGetSubscribedUserListResult); + int mockRequestId = 1; + { + String topic = "hello"; + when(mockStreamChannelNativeBinding.getSubscribedUserList( + topic, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String topic = "hello"; + final ret = await streamChannel.getSubscribedUserList( + topic, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.release', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + {} + + await streamChannel.release(); + }, + ); + + test( + 'StreamChannel.publishTextMessage', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'publishTextMessage'); + String thePublishTopicMessageResultChannelName = "hello"; + String thePublishTopicMessageResultTopic = "hello"; + PublishTopicMessageResult thePublishTopicMessageResult = + PublishTopicMessageResult( + channelName: thePublishTopicMessageResultChannelName, + topic: thePublishTopicMessageResultTopic, + ); + final mockResultHandlerReturnValue = + (thePublishTopicMessageResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, thePublishTopicMessageResult); + int mockRequestId = 1; + { + String topic = "hello"; + String message = "hello"; + int length = 5; + RtmMessageType optionMessageType = RtmMessageType.binary; + int optionSendTs = 5; + String optionCustomType = "hello"; + TopicMessageOptions option = TopicMessageOptions( + messageType: optionMessageType, + sendTs: optionSendTs, + customType: optionCustomType, + ); + when(mockStreamChannelNativeBinding.publishTextMessage( + topic: topic, + message: message, + length: length, + option: argThat( + isA(), + named: 'option', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String topic = "hello"; + String message = "hello"; + int sendTs = 5; + String customType = "hello"; + final ret = await streamChannel.publishTextMessage( + topic, + message, + sendTs: sendTs, + customType: customType, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); + + test( + 'StreamChannel.publishBinaryMessage', + () async { + final mockStreamChannelNativeBinding = MockStreamChannelImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + StreamChannel streamChannel = stream_channel_impl.StreamChannelImpl( + mockStreamChannelNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'publishBinaryMessage'); + String thePublishTopicMessageResultChannelName = "hello"; + String thePublishTopicMessageResultTopic = "hello"; + PublishTopicMessageResult thePublishTopicMessageResult = + PublishTopicMessageResult( + channelName: thePublishTopicMessageResultChannelName, + topic: thePublishTopicMessageResultTopic, + ); + final mockResultHandlerReturnValue = + (thePublishTopicMessageResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = + (rtmStatus, thePublishTopicMessageResult); + int mockRequestId = 1; + { + String topic = "hello"; + Uint8List message = Uint8List.fromList([1, 1, 1, 1, 1]); + int length = 5; + RtmMessageType optionMessageType = RtmMessageType.binary; + int optionSendTs = 5; + String optionCustomType = "hello"; + TopicMessageOptions option = TopicMessageOptions( + messageType: optionMessageType, + sendTs: optionSendTs, + customType: optionCustomType, + ); + when(mockStreamChannelNativeBinding.publishBinaryMessage( + topic: topic, + message: message, + length: length, + option: argThat( + isA(), + named: 'option', + ), + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String topic = "hello"; + Uint8List message = Uint8List.fromList([1, 1, 1, 1, 1]); + int sendTs = 5; + String customType = "hello"; + final ret = await streamChannel.publishBinaryMessage( + topic, + message, + sendTs: sendTs, + customType: customType, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); +} diff --git a/test_shard/integration_test_app/test/rtm_result_handler_impl_test.dart b/test_shard/integration_test_app/test/rtm_result_handler_impl_test.dart new file mode 100644 index 0000000..787570d --- /dev/null +++ b/test_shard/integration_test_app/test/rtm_result_handler_impl_test.dart @@ -0,0 +1,103 @@ +import 'dart:async'; + +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:agora_rtm/src/impl/agora_rtm_client_impl_override.dart' + as agora_rtm_client_impl; +import 'package:agora_rtm/src/impl/rtm_result_handler_impl.dart'; +import 'package:mockito/mockito.dart'; + +import 'all_mocks.mocks.dart'; + +void main() { + test( + 'addListener token', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final fakeRtmResultHandlerImpl = RtmResultHandlerImpl(); + + when(mockRtmClientNativeBinding.initialize( + 'app_id', + 'user_id', + fakeRtmResultHandlerImpl.rtmEventHandler, + config: argThat(isNull, named: 'config'), + args: argThat(isNotNull, named: 'args'), + )).thenAnswer((_) async => const RtmStatus.success(operation: 'RTM')); + + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: fakeRtmResultHandlerImpl, + ); + + final tokenCompleter = Completer(); + + rtmClient.addListener( + token: (event) { + if (!tokenCompleter.isCompleted) { + tokenCompleter.complete(event.channelName); + } + }, + ); + + fakeRtmResultHandlerImpl.onTokenPrivilegeWillExpire('channelName'); + + final channelName = await tokenCompleter.future; + expect(channelName, 'channelName'); + }, + ); + + test( + 'addListener token', + () async { + final mockRtmClientNativeBinding = MockRtmClientImplOverride(); + final fakeRtmResultHandlerImpl = RtmResultHandlerImpl(); + + when(mockRtmClientNativeBinding.initialize( + 'app_id', + 'user_id', + fakeRtmResultHandlerImpl.rtmEventHandler, + config: argThat(isNull, named: 'config'), + args: argThat(isNotNull, named: 'args'), + )).thenAnswer((_) async => const RtmStatus.success(operation: 'RTM')); + + final (_, rtmClient) = + await agora_rtm_client_impl.RtmClientImplOverride.create( + 'app_id', + 'user_id', + rtmClientNativeBinding: mockRtmClientNativeBinding, + rtmResultHandlerImpl: fakeRtmResultHandlerImpl, + ); + + final tokenCompleter = Completer(); + + rtmClient.addListener( + token: (event) { + if (!tokenCompleter.isCompleted) { + tokenCompleter.complete(event.channelName); + } + }, + ); + + rtmClient.removeListener( + token: (event) { + if (!tokenCompleter.isCompleted) { + tokenCompleter.complete(event.channelName); + } + }, + ); + + fakeRtmResultHandlerImpl.onTokenPrivilegeWillExpire('channelName'); + + String channelName = ''; + try { + channelName = await tokenCompleter.future + .timeout(const Duration(milliseconds: 10)); + } on TimeoutException { + expect(channelName, ''); + } + }, + ); +} diff --git a/test_shard/integration_test_app/test/rtmpresence_unit_test_cases.dart b/test_shard/integration_test_app/test/rtmpresence_unit_test_cases.dart new file mode 100644 index 0000000..72d3bc1 --- /dev/null +++ b/test_shard/integration_test_app/test/rtmpresence_unit_test_cases.dart @@ -0,0 +1,57 @@ +import 'package:agora_rtm/agora_rtm.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +import 'all_mocks.mocks.dart'; +import 'generated/rtmpresence_unit_test.generated.dart' as generated; +import 'package:agora_rtm/src/impl/gen/agora_rtm_presence_impl.dart' + as rtm_presence_impl; + +void testCases() { + generated.testCases(); + + test( + 'RtmPresence.setState', + () async { + final mockRtmPresenceNativeBinding = MockRtmPresenceImpl(); + final mockRtmResultHandlerImpl = MockRtmResultHandlerImpl(); + RtmPresence rtmPresence = rtm_presence_impl.RtmPresenceImpl( + mockRtmPresenceNativeBinding, + mockRtmResultHandlerImpl, + ); + + const rtmStatus = RtmStatus.success(operation: 'setState'); + int mockRequestId = 1; + SetStateResult setStateResult = SetStateResult(); + final mockResultHandlerReturnValue = (setStateResult, RtmErrorCode.ok); + final expectedResultHandlerReturnValue = (rtmStatus, setStateResult); + { + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + + when(mockRtmPresenceNativeBinding.setState( + channelName: channelName, + channelType: channelType, + items: argThat( + isA>(), + named: 'items', + ), + count: 1, + )).thenAnswer((_) async => mockRequestId); + when(mockRtmResultHandlerImpl.request(mockRequestId)) + .thenAnswer((_) async => mockResultHandlerReturnValue); + } + + String channelName = "hello"; + RtmChannelType channelType = RtmChannelType.none; + Map items = {'key1': 'value1'}; + + final ret = await rtmPresence.setState( + channelName, + channelType, + items, + ); + expect(ret, expectedResultHandlerReturnValue); + }, + ); +} diff --git a/test_shard/integration_test_app/web/favicon.png b/test_shard/integration_test_app/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/test_shard/integration_test_app/web/favicon.png differ diff --git a/test_shard/integration_test_app/web/icons/Icon-192.png b/test_shard/integration_test_app/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/test_shard/integration_test_app/web/icons/Icon-192.png differ diff --git a/test_shard/integration_test_app/web/icons/Icon-512.png b/test_shard/integration_test_app/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/test_shard/integration_test_app/web/icons/Icon-512.png differ diff --git a/test_shard/integration_test_app/web/icons/Icon-maskable-192.png b/test_shard/integration_test_app/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/test_shard/integration_test_app/web/icons/Icon-maskable-192.png differ diff --git a/test_shard/integration_test_app/web/icons/Icon-maskable-512.png b/test_shard/integration_test_app/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/test_shard/integration_test_app/web/icons/Icon-maskable-512.png differ diff --git a/test_shard/integration_test_app/web/index.html b/test_shard/integration_test_app/web/index.html new file mode 100644 index 0000000..0df6d6e --- /dev/null +++ b/test_shard/integration_test_app/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + integration_test_app + + + + + + + + + + diff --git a/test_shard/integration_test_app/web/manifest.json b/test_shard/integration_test_app/web/manifest.json new file mode 100644 index 0000000..7888d7e --- /dev/null +++ b/test_shard/integration_test_app/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "integration_test_app", + "short_name": "integration_test_app", + "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/test_shard/integration_test_app/windows/.gitignore b/test_shard/integration_test_app/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/test_shard/integration_test_app/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/test_shard/integration_test_app/windows/CMakeLists.txt b/test_shard/integration_test_app/windows/CMakeLists.txt new file mode 100644 index 0000000..5454da2 --- /dev/null +++ b/test_shard/integration_test_app/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(integration_test_app 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 "integration_test_app") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# 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() + +# 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/test_shard/integration_test_app/windows/flutter/CMakeLists.txt b/test_shard/integration_test_app/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..930d207 --- /dev/null +++ b/test_shard/integration_test_app/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# 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") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/test_shard/integration_test_app/windows/flutter/generated_plugin_registrant.h b/test_shard/integration_test_app/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/test_shard/integration_test_app/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/test_shard/integration_test_app/windows/flutter/generated_plugins.cmake b/test_shard/integration_test_app/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..9fc3733 --- /dev/null +++ b/test_shard/integration_test_app/windows/flutter/generated_plugins.cmake @@ -0,0 +1,25 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + iris_method_channel + iris_tester +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/test_shard/integration_test_app/windows/runner/CMakeLists.txt b/test_shard/integration_test_app/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..b9e550f --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/CMakeLists.txt @@ -0,0 +1,32 @@ +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}) + +# 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_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/test_shard/integration_test_app/windows/runner/Runner.rc b/test_shard/integration_test_app/windows/runner/Runner.rc new file mode 100644 index 0000000..934c7b5 --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.agora.integration_test_app" "\0" + VALUE "FileDescription", "integration_test_app" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "integration_test_app" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 io.agora.integration_test_app. All rights reserved." "\0" + VALUE "OriginalFilename", "integration_test_app.exe" "\0" + VALUE "ProductName", "integration_test_app" "\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/test_shard/integration_test_app/windows/runner/flutter_window.cpp b/test_shard/integration_test_app/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..b43b909 --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/test_shard/integration_test_app/windows/runner/flutter_window.h b/test_shard/integration_test_app/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#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/test_shard/integration_test_app/windows/runner/main.cpp b/test_shard/integration_test_app/windows/runner/main.cpp new file mode 100644 index 0000000..7c1992f --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"integration_test_app", 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/test_shard/integration_test_app/windows/runner/resource.h b/test_shard/integration_test_app/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/test_shard/integration_test_app/windows/runner/resources/app_icon.ico b/test_shard/integration_test_app/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000..c04e20c Binary files /dev/null and b/test_shard/integration_test_app/windows/runner/resources/app_icon.ico differ diff --git a/test_shard/integration_test_app/windows/runner/runner.exe.manifest b/test_shard/integration_test_app/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..c977c4a --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_app/windows/runner/utils.cpp b/test_shard/integration_test_app/windows/runner/utils.cpp new file mode 100644 index 0000000..f5bf9fa --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + 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, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/test_shard/integration_test_app/windows/runner/utils.h b/test_shard/integration_test_app/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/utils.h @@ -0,0 +1,19 @@ +#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/test_shard/integration_test_app/windows/runner/win32_window.cpp b/test_shard/integration_test_app/windows/runner/win32_window.cpp new file mode 100644 index 0000000..c10f08d --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/test_shard/integration_test_app/windows/runner/win32_window.h b/test_shard/integration_test_app/windows/runner/win32_window.h new file mode 100644 index 0000000..17ba431 --- /dev/null +++ b/test_shard/integration_test_app/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/test_shard/iris_tester/.gitignore b/test_shard/iris_tester/.gitignore new file mode 100644 index 0000000..6dcfb73 --- /dev/null +++ b/test_shard/iris_tester/.gitignore @@ -0,0 +1,35 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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 +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ + +macos/libiris_tester.a +macos/iris_tester.framework +macos/iris_tester_handler.framework +macos/IrisDebugger.framework diff --git a/test_shard/iris_tester/.metadata b/test_shard/iris_tester/.metadata new file mode 100644 index 0000000..2dc72d8 --- /dev/null +++ b/test_shard/iris_tester/.metadata @@ -0,0 +1,39 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + channel: stable + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: android + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: ios + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: macos + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: windows + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + + # 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/test_shard/iris_tester/CHANGELOG.md b/test_shard/iris_tester/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/test_shard/iris_tester/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/test_shard/iris_tester/LICENSE b/test_shard/iris_tester/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/test_shard/iris_tester/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/test_shard/iris_tester/README.md b/test_shard/iris_tester/README.md new file mode 100644 index 0000000..f8651b8 --- /dev/null +++ b/test_shard/iris_tester/README.md @@ -0,0 +1,15 @@ +# iris_tester + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/test_shard/iris_tester/analysis_options.yaml b/test_shard/iris_tester/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/test_shard/iris_tester/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/test_shard/iris_tester/android/.gitignore b/test_shard/iris_tester/android/.gitignore new file mode 100644 index 0000000..4ffc57b --- /dev/null +++ b/test_shard/iris_tester/android/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx +libs/ diff --git a/test_shard/iris_tester/android/build.gradle b/test_shard/iris_tester/android/build.gradle new file mode 100644 index 0000000..7834e77 --- /dev/null +++ b/test_shard/iris_tester/android/build.gradle @@ -0,0 +1,51 @@ +group 'com.example.iris_tester' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + main.jniLibs.srcDirs += 'libs' + } + + defaultConfig { + minSdkVersion 16 + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/test_shard/iris_tester/android/settings.gradle b/test_shard/iris_tester/android/settings.gradle new file mode 100644 index 0000000..9e1ffdb --- /dev/null +++ b/test_shard/iris_tester/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'iris_tester' diff --git a/test_shard/iris_tester/android/src/main/AndroidManifest.xml b/test_shard/iris_tester/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..81051c0 --- /dev/null +++ b/test_shard/iris_tester/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/test_shard/iris_tester/android/src/main/kotlin/com/example/iris_tester/IrisTesterPlugin.kt b/test_shard/iris_tester/android/src/main/kotlin/com/example/iris_tester/IrisTesterPlugin.kt new file mode 100644 index 0000000..e2cd7f1 --- /dev/null +++ b/test_shard/iris_tester/android/src/main/kotlin/com/example/iris_tester/IrisTesterPlugin.kt @@ -0,0 +1,35 @@ +package com.example.iris_tester + +import androidx.annotation.NonNull + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +/** IrisTesterPlugin */ +class IrisTesterPlugin: FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "iris_tester") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/test_shard/iris_tester/cxx/CMakeLists.txt b/test_shard/iris_tester/cxx/CMakeLists.txt new file mode 100644 index 0000000..42150dd --- /dev/null +++ b/test_shard/iris_tester/cxx/CMakeLists.txt @@ -0,0 +1,135 @@ +cmake_minimum_required(VERSION 3.10.2) +set(PROJECT_NAME "iris_tester") +project(${PROJECT_NAME} LANGUAGES C CXX) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 17) + + + +# set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") +# set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") + +# project(iris_tester) + +set(LIBRARY_NAME iris_tester_handler) + +file(GLOB SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc" + "${CMAKE_CURRENT_SOURCE_DIR}/src/fake/*.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/fake_gen/*.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/event_triggers/*.hpp" + ) + +add_library(${LIBRARY_NAME} SHARED + ${SOURCES} + ) + +target_include_directories(${LIBRARY_NAME} PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_SOURCE_DIR}/src/fake" + "${CMAKE_CURRENT_SOURCE_DIR}/src/fake_gen" + "${CMAKE_CURRENT_SOURCE_DIR}/src/event_triggers" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/agora/rtc/include" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/iris" +) + +if (CMAKE_SYSTEM_NAME STREQUAL "Android") +# if(UNIX AND NOT APPLE) +# if (PLATFORM STREQUAL "ANDROID") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../android/libs/${ANDROID_ABI}/libAgoraRtcWrapper.so") + set(LIBS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../android/libs/${ANDROID_ABI}") + else() + set(LIBS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../android/libs/${ANDROID_ABI}") + endif() + + find_library( + log-lib + log + ) + + add_library(libAgoraRtcWrapper SHARED IMPORTED) + set_target_properties( + libAgoraRtcWrapper + PROPERTIES IMPORTED_LOCATION + ${LIBS_DIR}/libAgoraRtcWrapper.so + ) + + target_link_libraries(${LIBRARY_NAME} + PRIVATE + libAgoraRtcWrapper + ${log-lib} + ) +elseif (CMAKE_SYSTEM_NAME STREQUAL "iOS") + set(LIBS_DIR + "${CMAKE_CURRENT_SOURCE_DIR}" + ) + + set(FRAMEWORKS + "-framework AgoraRtcWrapper" + ) + + set_target_properties(${LIBRARY_NAME} PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION A + MACOSX_FRAMEWORK_IDENTIFIER io.agora.iris.it + CXX_VISIBILITY_PRESET hidden + LINK_FLAGS "-Wl -F ${LIBS_DIR} -rpath ${CMAKE_CURRENT_SOURCE_DIR}/../../ios/Pods/AgoraRtcEngine_iOS" + ) + + target_link_libraries(${LIBRARY_NAME} + PUBLIC + "${FRAMEWORKS}" + ) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../../macos/AgoraRtcWrapper.podspec") + set(LIBS_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../../../macos/" + ) + set(RTC_ENGINE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../../../macos/libs/AgoraRtcKit.framework/" + ) + target_include_directories(${LIBRARY_NAME} PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/../../../macos/AgoraRtcWrapper.framework/Headers" + "${CMAKE_CURRENT_SOURCE_DIR}/../../../macos/libs/AgoraRtcKit.framework/Headers" + ) + else() + set(LIBS_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../../macos/Pods/AgoraIrisRTC_macOS" + ) + set(RTC_ENGINE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../../macos/Pods/AgoraRtcEngine_macOS" + ) + endif() + + set(FRAMEWORKS + "-framework AgoraRtcWrapper" + ) + + set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "10.11") + set_target_properties(${LIBRARY_NAME} PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION A + MACOSX_FRAMEWORK_IDENTIFIER io.agora.iris.it + CXX_VISIBILITY_PRESET hidden + LINK_FLAGS "-Wl -F ${LIBS_DIR}" + ) + + target_link_libraries(${LIBRARY_NAME} + PUBLIC + "${FRAMEWORKS}" + ) +else() + set(IRIS_SDK_DOWNLOAD_NAME "iris_3.7.0.3_RTC_Windows_20220719_0357") + set(LIBS_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/../../windows/third_party/iris/${IRIS_SDK_DOWNLOAD_NAME}/x64/Release/AgoraRtcWrapper.lib" + ) + target_link_libraries(${LIBRARY_NAME} PUBLIC + ${LIBS_DIR} + ) + +endif() + +# if (APPLE AND CMAKE_SYSTEM_NAME STREQUAL "Darwin") +# add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/unittest) +# endif() diff --git a/test_shard/iris_tester/cxx/build-macos.sh b/test_shard/iris_tester/cxx/build-macos.sh new file mode 100644 index 0000000..64dc8b6 --- /dev/null +++ b/test_shard/iris_tester/cxx/build-macos.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set -e +set -x + +MY_PATH=$(dirname "$0") +SRC_PATH=$(realpath -e ${MY_PATH}) + +ARCHS="arm64 x86_64" +BUILD_TYPE="Debug" + +# pushd ${MY_PATH}/../../../macos +# flutter packages get +# pod install +# popd + +if [ ! -d "$SRC_PATH/build/mac" ]; then + mkdir -p ${SRC_PATH}/build/mac +fi + +pushd ${SRC_PATH}/build/mac +cmake \ + -G "Xcode" \ + -DCMAKE_TOOLCHAIN_FILE="${SRC_PATH}/ios.toolchain.cmake" \ + -DPLATFORM="MAC" \ + -DARCHS="$ARCHS" \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + "$SRC_PATH" +cmake --build . --config "$BUILD_TYPE" +popd + +# pushd ${SRC_PATH}/build/mac + +# cmake \ +# -G Xcode \ +# -DPLATFORM="MAC" \ +# -DCMAKE_OSX_ARCHITECTURES="x86_64" \ +# -DCMAKE_BUILD_TYPE="Debug" \ +# -DRUN_TEST=1 \ +# "${SRC_PATH}" +# cmake --build . --config "Debug" + +# popd + +rm -rf ${SRC_PATH}/../macos/iris_tester_handler.framework +cp -RP "${SRC_PATH}/build/mac/Debug/iris_tester_handler.framework" "${SRC_PATH}/../macos" \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/ios.toolchain.cmake b/test_shard/iris_tester/cxx/ios.toolchain.cmake new file mode 100644 index 0000000..168b4b9 --- /dev/null +++ b/test_shard/iris_tester/cxx/ios.toolchain.cmake @@ -0,0 +1,927 @@ +# This file is part of the ios-cmake project. It was retrieved from +# https://github.com/gerstrong/ios-cmake.git which is a fork of +# https://github.com/cristeab/ios-cmake.git, which again is a fork of +# https://code.google.com/p/ios-cmake/. Which in turn is based off of +# the Platform/Darwin.cmake and Platform/UnixPaths.cmake files which +# are included with CMake 2.8.4 +# +# The ios-cmake project is licensed under the new BSD license. +# +# Copyright (c) 2014, Bogdan Cristea and LTE Engineering Software, +# Kitware, Inc., Insight Software Consortium. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# This file is based off of the Platform/Darwin.cmake and +# Platform/UnixPaths.cmake files which are included with CMake 2.8.4 +# It has been altered for iOS development. +# +# Updated by Alex Stewart (alexs.mac@gmail.com) +# +# ***************************************************************************** +# Now maintained by Alexander Widerberg (widerbergaren [at] gmail.com) +# under the BSD-3-Clause license +# https://github.com/leetal/ios-cmake +# ***************************************************************************** +# +# INFORMATION / HELP +# +# The following options control the behaviour of this toolchain: +# +# PLATFORM: (default "OS64") +# OS = Build for iPhoneOS. +# OS64 = Build for arm64 iphoneOS. +# OS64COMBINED = Build for arm64 x86_64 iphoneOS. Combined into FAT STATIC lib (supported on 3.14+ of CMakewith "-G Xcode" argument ONLY) +# SIMULATOR = Build for x86 i386 iphoneOS Simulator. +# SIMULATOR64 = Build for x86_64 iphoneOS Simulator. +# SIMULATORARM64 = Build for arm64 iphoneOS Simulator. +# TVOS = Build for arm64 tvOS. +# TVOSCOMBINED = Build for arm64 x86_64 tvOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) +# SIMULATOR_TVOS = Build for x86_64 tvOS Simulator. +# WATCHOS = Build for armv7k arm64_32 for watchOS. +# WATCHOSCOMBINED = Build for armv7k arm64_32 x86_64 watchOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) +# SIMULATOR_WATCHOS = Build for x86_64 for watchOS Simulator. +# MAC = Build for x86_64 macOS. +# MAC_ARM64 = Build for Apple Silicon macOS. +# MAC_CATALYST = Build for x86_64 macOS with Catalyst support (iOS toolchain on macOS). +# Note: The build argument "MACOSX_DEPLOYMENT_TARGET" can be used to control min-version of macOS +# MAC_CATALYST_ARM64 = Build for Apple Silicon macOS with Catalyst support (iOS toolchain on macOS). +# Note: The build argument "MACOSX_DEPLOYMENT_TARGET" can be used to control min-version of macOS +# +# CMAKE_OSX_SYSROOT: Path to the SDK to use. By default this is +# automatically determined from PLATFORM and xcodebuild, but +# can also be manually specified (although this should not be required). +# +# CMAKE_DEVELOPER_ROOT: Path to the Developer directory for the platform +# being compiled for. By default this is automatically determined from +# CMAKE_OSX_SYSROOT, but can also be manually specified (although this should +# not be required). +# +# DEPLOYMENT_TARGET: Minimum SDK version to target. Default 2.0 on watchOS and 9.0 on tvOS+iOS +# +# ENABLE_BITCODE: (1|0) Enables or disables bitcode support. Default 1 (true) +# +# ENABLE_ARC: (1|0) Enables or disables ARC support. Default 1 (true, ARC enabled by default) +# +# ENABLE_VISIBILITY: (1|0) Enables or disables symbol visibility support. Default 0 (false, visibility hidden by default) +# +# ENABLE_STRICT_TRY_COMPILE: (1|0) Enables or disables strict try_compile() on all Check* directives (will run linker +# to actually check if linking is possible). Default 0 (false, will set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY) +# +# ARCHS: (armv7 armv7s armv7k arm64 arm64_32 i386 x86_64) If specified, will override the default architectures for the given PLATFORM +# OS = armv7 armv7s arm64 (if applicable) +# OS64 = arm64 (if applicable) +# SIMULATOR = i386 +# SIMULATOR64 = x86_64 +# SIMULATORARM64 = arm64 +# TVOS = arm64 +# SIMULATOR_TVOS = x86_64 (i386 has since long been deprecated) +# WATCHOS = armv7k arm64_32 (if applicable) +# SIMULATOR_WATCHOS = x86_64 (i386 has since long been deprecated) +# MAC = x86_64 +# MAC_ARM64 = arm64 +# MAC_CATALYST = x86_64 +# MAC_CATALYST_ARM64 = arm64 +# +# This toolchain defines the following properties (available via get_property()) for use externally: +# +# PLATFORM: The currently targeted platform. +# XCODE_VERSION: Version number (not including Build version) of Xcode detected. +# SDK_VERSION: Version of SDK being used. +# OSX_ARCHITECTURES: Architectures being compiled for (generated from PLATFORM). +# APPLE_TARGET_TRIPLE: Used by autoconf build systems. NOTE: If "ARCHS" are overridden, this will *NOT* be set! +# +# This toolchain defines the following macros for use externally: +# +# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE XCODE_VARIANT) +# A convenience macro for setting xcode specific properties on targets. +# Available variants are: All, Release, RelWithDebInfo, Debug, MinSizeRel +# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1" "all"). +# +# find_host_package (PROGRAM ARGS) +# A macro used to find executable programs on the host system, not within the +# environment. Thanks to the android-cmake project for providing the +# command. +# + +cmake_minimum_required(VERSION 3.8.0) + +# CMake invokes the toolchain file twice during the first build, but only once during subsequent rebuilds. +if(IOS_TOOLCHAIN_HAS_RUN) + return() +endif(IOS_TOOLCHAIN_HAS_RUN) +set(IOS_TOOLCHAIN_HAS_RUN true) + +############################################################################### +# OPTIONS # +############################################################################### + +option(DROP_32_BIT "Drops the 32-bit targets universally." YES) + +############################################################################### +# END OPTIONS # +############################################################################### + +# List of supported platform values +list(APPEND _supported_platforms + "OS" "OS64" "OS64COMBINED" "SIMULATOR" "SIMULATOR64" "SIMULATORARM64" + "TVOS" "TVOSCOMBINED" "SIMULATOR_TVOS" + "WATCHOS" "WATCHOSCOMBINED" "SIMULATOR_WATCHOS" + "MAC" "MAC_ARM64" + "MAC_CATALYST" "MAC_CATALYST_ARM64") + +# Cache what generator is used +set(USED_CMAKE_GENERATOR "${CMAKE_GENERATOR}") + +# Check if using a CMake version capable of building combined FAT builds (simulator and target slices combined in one static lib) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14") + set(MODERN_CMAKE YES) +endif() + +# Get the Xcode version being used. +# Problem: CMake runs toolchain files multiple times, but can't read cache variables on some runs. +# Workaround: On first run (in which cache variables are always accessible), set an intermediary environment variable. +# +# NOTE: This pattern is used i many places in this toolchain to speed up checks of all sorts +if(DEFINED XCODE_VERSION_INT) + # Environment variables are always preserved. + set(ENV{_XCODE_VERSION_INT} "${XCODE_VERSION_INT}") +elseif(DEFINED ENV{_XCODE_VERSION_INT}) + set(XCODE_VERSION_INT "$ENV{_XCODE_VERSION_INT}") +elseif(NOT DEFINED XCODE_VERSION_INT) + find_program(XCODEBUILD_EXECUTABLE xcodebuild) + if(NOT XCODEBUILD_EXECUTABLE) + message(FATAL_ERROR "xcodebuild not found. Please install either the standalone commandline tools or Xcode.") + endif() + execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version + OUTPUT_VARIABLE XCODE_VERSION_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX MATCH "Xcode [0-9\\.]+" XCODE_VERSION_INT "${XCODE_VERSION_INT}") + string(REGEX REPLACE "Xcode ([0-9\\.]+)" "\\1" XCODE_VERSION_INT "${XCODE_VERSION_INT}") + set(XCODE_VERSION_INT "${XCODE_VERSION_INT}" CACHE INTERNAL "") +endif() + +# Assuming that xcode 12.0 is installed you most probably have ios sdk 14.0 or later installed (tested on Big Sur) +# if you don't set a deployment target it will be set the way you only get 64-bit builds +if(NOT DEFINED DEPLOYMENT_TARGET AND XCODE_VERSION_INT VERSION_GREATER 12.0) + # Temporarily fix the arm64 issues in CMake install-combined by excluding arm64 for simulator builds (needed for Apple Silicon...) + set(CMAKE_XCODE_ATTRIBUTE_EXCLUDED_ARCHS[sdk=iphonesimulator*] "arm64") +endif() + +# Check if the platform variable is set +if(DEFINED PLATFORM) + # Environment variables are always preserved. + set(ENV{_PLATFORM} "${PLATFORM}") +elseif(DEFINED ENV{_PLATFORM}) + set(PLATFORM "$ENV{_PLATFORM}") +elseif(NOT DEFINED PLATFORM) + message(FATAL_ERROR "PLATFORM argument not set. Bailing configure since I don't know what target you want to build for!") +endif () + +# Safeguard that the platform value is set and is one of the supported values +list(FIND _supported_platforms ${PLATFORM} contains_PLATFORM) +if("${contains_PLATFORM}" EQUAL "-1") + string(REPLACE ";" "\n * " _supported_platforms_formatted "${_supported_platforms}") + message(FATAL_ERROR " Invalid PLATFORM specified! Current value: ${PLATFORM}.\n" + " Supported PLATFORM values: \n * ${_supported_platforms_formatted}") +endif() + +# Check if Apple Silicon is supported +if(PLATFORM MATCHES "^(MAC_ARM64)$|^(MAC_CATALYST_ARM64)$" AND ${CMAKE_VERSION} VERSION_LESS "3.19.5") + message(FATAL_ERROR "Apple Silicon builds requires a minimum of CMake 3.19.5") +endif() + +# Touch toolchain variable to suppress "unused variable" warning. +# This happens if CMake is invoked with the same command line the second time. +if(CMAKE_TOOLCHAIN_FILE) +endif() + +# Fix for PThread library not in path +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) + +# Specify minimum version of deployment target. +if(NOT DEFINED DEPLOYMENT_TARGET) + if (PLATFORM MATCHES "WATCHOS") + # Unless specified, SDK version 4.0 is used by default as minimum target version (watchOS). + set(DEPLOYMENT_TARGET "4.0") + elseif(PLATFORM STREQUAL "MAC") + # Unless specified, SDK version 10.13 (High sierra) is used by default as minimum target version (macos). + set(DEPLOYMENT_TARGET "10.13") + elseif(PLATFORM STREQUAL "MAC_ARM64") + # Unless specified, SDK version 11.0 (Big Sur) is used by default as minimum target version (macos on arm). + set(DEPLOYMENT_TARGET "11.0") + elseif(PLATFORM STREQUAL "MAC_CATALYST" OR PLATFORM STREQUAL "MAC_CATALYST_ARM64") + # Unless specified, SDK version 13.0 is used by default as minimum target version (mac catalyst minimum requirement). + set(DEPLOYMENT_TARGET "13.0") + else() + # Unless specified, SDK version 11.0 is used by default as minimum target version (iOS, tvOS). + set(DEPLOYMENT_TARGET "11.0") + endif() + message(STATUS "[DEFAULTS] Using the default min-version since DEPLOYMENT_TARGET not provided!") +elseif(DEFINED DEPLOYMENT_TARGET AND PLATFORM STREQUAL "MAC_CATALYST" AND ${DEPLOYMENT_TARGET} VERSION_LESS "13.0") + message(FATAL_ERROR "Mac Catalyst builds requires a minimum deployment target of 13.0!") +endif() + +# Store the DEPLOYMENT_TARGET in the cache +set(DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}" CACHE INTERNAL "") + +# Handle the case where we are targeting iOS and a version above 10.3.4 (32-bit support dropped officially) +if(PLATFORM STREQUAL "OS" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) + set(PLATFORM "OS64") + message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") +elseif(PLATFORM STREQUAL "SIMULATOR" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) + set(PLATFORM "SIMULATOR64") + message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") +endif() + +set(PLATFORM_INT "${PLATFORM}") + +if(DEFINED ARCHS) + string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") +endif() + +# Determine the platform name and architectures for use in xcodebuild commands +# from the specified PLATFORM_INT name. +if(PLATFORM_INT STREQUAL "OS") + set(SDK_NAME iphoneos) + if(NOT ARCHS) + set(ARCHS armv7 armv7s arm64) + set(APPLE_TARGET_TRIPLE_INT arm-apple-ios) + endif() +elseif(PLATFORM_INT STREQUAL "OS64") + set(SDK_NAME iphoneos) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS arm64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example + else() + set(ARCHS arm64) + endif() + set(APPLE_TARGET_TRIPLE_INT aarch64-apple-ios) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios) + endif() +elseif(PLATFORM_INT STREQUAL "OS64COMBINED") + set(SDK_NAME iphoneos) + if(MODERN_CMAKE) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS arm64 x86_64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "x86_64") + else() + set(ARCHS arm64 x86_64) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "x86_64") + endif() + set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-ios) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the OS64COMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS i386) + set(APPLE_TARGET_TRIPLE_INT i386-apple-ios) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios) + endif() + message(DEPRECATION "SIMULATOR IS DEPRECATED. Consider using SIMULATOR64 instead.") +elseif(PLATFORM_INT STREQUAL "SIMULATOR64") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS x86_64) + set(APPLE_TARGET_TRIPLE_INT x86_64-apple-ios) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios) + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATORARM64") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS arm64) + set(APPLE_TARGET_TRIPLE_INT aarch64-apple-ios) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios) + endif() +elseif(PLATFORM_INT STREQUAL "TVOS") + set(SDK_NAME appletvos) + if(NOT ARCHS) + set(ARCHS arm64) + set(APPLE_TARGET_TRIPLE_INT aarch64-apple-tvos) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos) + endif() +elseif (PLATFORM_INT STREQUAL "TVOSCOMBINED") + set(SDK_NAME appletvos) + if(MODERN_CMAKE) + if(NOT ARCHS) + set(ARCHS arm64 x86_64) + set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-tvos) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvsimulator*] "x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=appletvos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=appletvsimulator*] "x86_64") + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the TVOSCOMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") + set(SDK_NAME appletvsimulator) + if(NOT ARCHS) + set(ARCHS x86_64) + set(APPLE_TARGET_TRIPLE_INT x86_64-apple-tvos) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos) + endif() +elseif(PLATFORM_INT STREQUAL "WATCHOS") + set(SDK_NAME watchos) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS armv7k arm64_32) + set(APPLE_TARGET_TRIPLE_INT aarch64_32-apple-watchos) + else() + set(ARCHS armv7k) + set(APPLE_TARGET_TRIPLE_INT arm-apple-watchos) + endif() + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos) + endif() +elseif(PLATFORM_INT STREQUAL "WATCHOSCOMBINED") + set(SDK_NAME watchos) + if(MODERN_CMAKE) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS armv7k arm64_32 i386) + set(APPLE_TARGET_TRIPLE_INT aarch64_32-i386-apple-watchos) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "armv7k arm64_32") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "armv7k arm64_32") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386") + else() + set(ARCHS armv7k i386) + set(APPLE_TARGET_TRIPLE_INT arm-i386-apple-watchos) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "armv7k") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "armv7k") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386") + endif() + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the WATCHOSCOMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") + set(SDK_NAME watchsimulator) + if(NOT ARCHS) + set(ARCHS i386) + set(APPLE_TARGET_TRIPLE_INT i386-apple-watchos) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos) + endif() +elseif(PLATFORM_INT STREQUAL "MAC" OR PLATFORM_INT STREQUAL "MAC_CATALYST") + set(SDK_NAME macosx) + if(NOT ARCHS) + set(ARCHS x86_64) + endif() + string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") + if(PLATFORM_INT STREQUAL "MAC") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx) + elseif(PLATFORM_INT STREQUAL "MAC_CATALYST") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-macabi) + endif() +elseif(PLATFORM_INT MATCHES "^(MAC_ARM64)$|^(MAC_CATALYST_ARM64)$") + set(SDK_NAME macosx) + if(NOT ARCHS) + set(ARCHS arm64) + endif() + string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") + if(PLATFORM_INT STREQUAL "MAC_ARM64") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx) + elseif(PLATFORM_INT STREQUAL "MAC_CATALYST_ARM64") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-macabi) + endif() +else() + message(FATAL_ERROR "Invalid PLATFORM: ${PLATFORM_INT}") +endif() + +if(MODERN_CMAKE AND PLATFORM_INT MATCHES ".*COMBINED" AND NOT CMAKE_GENERATOR MATCHES "Xcode") + message(FATAL_ERROR "The COMBINED options only work with Xcode generator, -G Xcode") +endif() + +if(CMAKE_GENERATOR MATCHES "Xcode" AND PLATFORM_INT MATCHES "MAC_CATALYST_.*") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") + set(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "macosx") + set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-maccatalyst") + if(NOT DEFINED MACOSX_DEPLOYMENT_TARGET) + set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "10.15") + else() + set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "${MACOSX_DEPLOYMENT_TARGET}") + endif() +elseif(CMAKE_GENERATOR MATCHES "Xcode") + set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}") + if(NOT PLATFORM_INT MATCHES ".*COMBINED") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=${SDK_NAME}*] "${ARCHS}") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=${SDK_NAME}*] "${ARCHS}") + endif() +endif() + +# If user did not specify the SDK root to use, then query xcodebuild for it. +if(DEFINED CMAKE_OSX_SYSROOT_INT) + # Environment variables are always preserved. + set(ENV{_CMAKE_OSX_SYSROOT_INT} "${CMAKE_OSX_SYSROOT_INT}") +elseif(DEFINED ENV{_CMAKE_OSX_SYSROOT_INT}) + set(CMAKE_OSX_SYSROOT_INT "$ENV{_CMAKE_OSX_SYSROOT_INT}") +elseif(NOT DEFINED CMAKE_OSX_SYSROOT_INT) + execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version -sdk ${SDK_NAME} Path + OUTPUT_VARIABLE CMAKE_OSX_SYSROOT_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +if (NOT DEFINED CMAKE_OSX_SYSROOT_INT AND NOT DEFINED CMAKE_OSX_SYSROOT) + message(SEND_ERROR "Please make sure that Xcode is installed and that the toolchain" + "is pointing to the correct path. Please run:" + "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer" + "and see if that fixes the problem for you.") + message(FATAL_ERROR "Invalid CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} " + "does not exist.") +elseif(DEFINED CMAKE_OSX_SYSROOT_INT) + set(CMAKE_OSX_SYSROOT_INT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") + # Specify the location or name of the platform SDK to be used in CMAKE_OSX_SYSROOT. + set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") +endif() + +# Use bitcode or not +if(NOT DEFINED ENABLE_BITCODE AND NOT ARCHS MATCHES "((^|;|, )(i386|x86_64))+") + # Unless specified, enable bitcode support by default + message(STATUS "[DEFAULTS] Enabling bitcode support by default. ENABLE_BITCODE not provided!") + set(ENABLE_BITCODE TRUE) +elseif(NOT DEFINED ENABLE_BITCODE) + message(STATUS "[DEFAULTS] Disabling bitcode support by default on simulators. ENABLE_BITCODE not provided for override!") + set(ENABLE_BITCODE FALSE) +endif() +set(ENABLE_BITCODE_INT ${ENABLE_BITCODE} CACHE BOOL + "Whether or not to enable bitcode" FORCE) +# Use ARC or not +if(NOT DEFINED ENABLE_ARC) + # Unless specified, enable ARC support by default + set(ENABLE_ARC TRUE) + message(STATUS "[DEFAULTS] Enabling ARC support by default. ENABLE_ARC not provided!") +endif() +set(ENABLE_ARC_INT ${ENABLE_ARC} CACHE BOOL "Whether or not to enable ARC" FORCE) +# Use hidden visibility or not +if(NOT DEFINED ENABLE_VISIBILITY) + # Unless specified, disable symbols visibility by default + set(ENABLE_VISIBILITY FALSE) + message(STATUS "[DEFAULTS] Hiding symbols visibility by default. ENABLE_VISIBILITY not provided!") +endif() +set(ENABLE_VISIBILITY_INT ${ENABLE_VISIBILITY} CACHE BOOL "Whether or not to hide symbols from the dynamic linker (-fvisibility=hidden)" FORCE) +# Set strict compiler checks or not +if(NOT DEFINED ENABLE_STRICT_TRY_COMPILE) + # Unless specified, disable strict try_compile() + set(ENABLE_STRICT_TRY_COMPILE FALSE) + message(STATUS "[DEFAULTS] Using NON-strict compiler checks by default. ENABLE_STRICT_TRY_COMPILE not provided!") +endif() +set(ENABLE_STRICT_TRY_COMPILE_INT ${ENABLE_STRICT_TRY_COMPILE} CACHE BOOL + "Whether or not to use strict compiler checks" FORCE) + +# Get the SDK version information. +if(DEFINED SDK_VERSION) + # Environment variables are always preserved. + set(ENV{_SDK_VERSION} "${SDK_VERSION}") +elseif(DEFINED ENV{_SDK_VERSION}) + set(SDK_VERSION "$ENV{_SDK_VERSION}") +elseif(NOT DEFINED SDK_VERSION) + execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -sdk ${CMAKE_OSX_SYSROOT_INT} -version SDKVersion + OUTPUT_VARIABLE SDK_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Find the Developer root for the specific iOS platform being compiled for +# from CMAKE_OSX_SYSROOT. Should be ../../ from SDK specified in +# CMAKE_OSX_SYSROOT. There does not appear to be a direct way to obtain +# this information from xcrun or xcodebuild. +if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT CMAKE_GENERATOR MATCHES "Xcode") + get_filename_component(PLATFORM_SDK_DIR ${CMAKE_OSX_SYSROOT_INT} PATH) + get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH) + if (NOT EXISTS "${CMAKE_DEVELOPER_ROOT}") + message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: ${CMAKE_DEVELOPER_ROOT} does not exist.") + endif() +endif() + +# Find the C & C++ compilers for the specified SDK. +if(DEFINED CMAKE_C_COMPILER) + # Environment variables are always preserved. + set(ENV{_CMAKE_C_COMPILER} "${CMAKE_C_COMPILER}") +elseif(DEFINED ENV{_CMAKE_C_COMPILER}) + set(CMAKE_C_COMPILER "$ENV{_CMAKE_C_COMPILER}") +elseif(NOT DEFINED CMAKE_C_COMPILER) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find clang + OUTPUT_VARIABLE CMAKE_C_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +if(DEFINED CMAKE_CXX_COMPILER) + # Environment variables are always preserved. + set(ENV{_CMAKE_CXX_COMPILER} "${CMAKE_CXX_COMPILER}") +elseif(DEFINED ENV{_CMAKE_CXX_COMPILER}) + set(CMAKE_CXX_COMPILER "$ENV{_CMAKE_CXX_COMPILER}") +elseif(NOT DEFINED CMAKE_CXX_COMPILER) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find clang++ + OUTPUT_VARIABLE CMAKE_CXX_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +# Find (Apple's) libtool. +if(DEFINED BUILD_LIBTOOL) + # Environment variables are always preserved. + set(ENV{_BUILD_LIBTOOL} "${BUILD_LIBTOOL}") +elseif(DEFINED ENV{_BUILD_LIBTOOL}) + set(BUILD_LIBTOOL "$ENV{_BUILD_LIBTOOL}") +elseif(NOT DEFINED BUILD_LIBTOOL) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find libtool + OUTPUT_VARIABLE BUILD_LIBTOOL + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +# Find the toolchain's provided install_name_tool if none is found on the host +if(DEFINED CMAKE_INSTALL_NAME_TOOL) + # Environment variables are always preserved. + set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}") +elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL}) + set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}") +elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find install_name_tool + OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "") +endif() + +# Configure libtool to be used instead of ar + ranlib to build static libraries. +# This is required on Xcode 7+, but should also work on previous versions of +# Xcode. +get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(lang ${languages}) + set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o " CACHE INTERNAL "") +endforeach() + +# CMake 3.14+ support building for iOS, watchOS and tvOS out of the box. +if(MODERN_CMAKE) + if(SDK_NAME MATCHES "iphone") + set(CMAKE_SYSTEM_NAME iOS) + elseif(SDK_NAME MATCHES "macosx") + set(CMAKE_SYSTEM_NAME Darwin) + elseif(SDK_NAME MATCHES "appletv") + set(CMAKE_SYSTEM_NAME tvOS) + elseif(SDK_NAME MATCHES "watch") + set(CMAKE_SYSTEM_NAME watchOS) + endif() + # Provide flags for a combined FAT library build on newer CMake versions + if(PLATFORM_INT MATCHES ".*COMBINED") + set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") + set(CMAKE_IOS_INSTALL_COMBINED YES) + message(STATUS "Will combine built (static) artifacts into FAT lib...") + endif() +elseif(NOT DEFINED CMAKE_SYSTEM_NAME AND ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.10") + # Legacy code path prior to CMake 3.14 or fallback if no CMAKE_SYSTEM_NAME specified + set(CMAKE_SYSTEM_NAME iOS) +elseif(NOT DEFINED CMAKE_SYSTEM_NAME) + # Legacy code path prior to CMake 3.14 or fallback if no CMAKE_SYSTEM_NAME specified + set(CMAKE_SYSTEM_NAME Darwin) +endif() +# Standard settings. +set(CMAKE_SYSTEM_VERSION ${SDK_VERSION} CACHE INTERNAL "") +set(UNIX TRUE CACHE BOOL "") +set(APPLE TRUE CACHE BOOL "") +if(PLATFORM STREQUAL "MAC" OR PLATFORM STREQUAL "MAC_ARM64") + set(IOS FALSE CACHE BOOL "") + set(MACOS TRUE CACHE BOOL "") +elseif(PLATFORM STREQUAL "MAC_CATALYST" OR PLATFORM STREQUAL "MAC_CATALYST_ARM64") + set(IOS TRUE CACHE BOOL "") + set(MACOS TRUE CACHE BOOL "") +else() + set(IOS TRUE CACHE BOOL "") +endif() +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) +set(CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE) +set(CMAKE_STRIP strip CACHE FILEPATH "" FORCE) +# Set the architectures for which to build. +set(CMAKE_OSX_ARCHITECTURES ${ARCHS} CACHE INTERNAL "") +# Change the type of target generated for try_compile() so it'll work when cross-compiling, weak compiler checks +if(NOT ENABLE_STRICT_TRY_COMPILE_INT) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() +# All iOS/Darwin specific settings - some may be redundant. +set(CMAKE_MACOSX_BUNDLE YES) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") +set(CMAKE_SHARED_LIBRARY_PREFIX "lib") +set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set(CMAKE_SHARED_MODULE_PREFIX "lib") +set(CMAKE_SHARED_MODULE_SUFFIX ".so") +set(CMAKE_C_COMPILER_ABI ELF) +set(CMAKE_CXX_COMPILER_ABI ELF) +set(CMAKE_C_HAS_ISYSROOT 1) +set(CMAKE_CXX_HAS_ISYSROOT 1) +set(CMAKE_MODULE_EXISTS 1) +set(CMAKE_DL_LIBS "") +set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +if(ARCHS MATCHES "((^|;|, )(arm64|arm64e|x86_64))+") + set(CMAKE_C_SIZEOF_DATA_PTR 8) + set(CMAKE_CXX_SIZEOF_DATA_PTR 8) + if(ARCHS MATCHES "((^|;|, )(arm64|arm64e))+") + set(CMAKE_SYSTEM_PROCESSOR "aarch64") + else() + set(CMAKE_SYSTEM_PROCESSOR "x86_64") + endif() +else() + set(CMAKE_C_SIZEOF_DATA_PTR 4) + set(CMAKE_CXX_SIZEOF_DATA_PTR 4) + set(CMAKE_SYSTEM_PROCESSOR "arm") +endif() + +# Note that only Xcode 7+ supports the newer more specific: +# -m${SDK_NAME}-version-min flags, older versions of Xcode use: +# -m(ios/ios-simulator)-version-min instead. +if(${CMAKE_VERSION} VERSION_LESS "3.11") + if(PLATFORM_INT STREQUAL "OS" OR PLATFORM_INT STREQUAL "OS64") + if(XCODE_VERSION_INT VERSION_LESS 7.0) + set(SDK_NAME_VERSION_FLAGS + "-mios-version-min=${DEPLOYMENT_TARGET}") + else() + # Xcode 7.0+ uses flags we can build directly from SDK_NAME. + set(SDK_NAME_VERSION_FLAGS + "-m${SDK_NAME}-version-min=${DEPLOYMENT_TARGET}") + endif() + elseif(PLATFORM_INT STREQUAL "TVOS") + set(SDK_NAME_VERSION_FLAGS + "-mtvos-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") + set(SDK_NAME_VERSION_FLAGS + "-mtvos-simulator-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "WATCHOS") + set(SDK_NAME_VERSION_FLAGS + "-mwatchos-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") + set(SDK_NAME_VERSION_FLAGS + "-mwatchos-simulator-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "MAC") + set(SDK_NAME_VERSION_FLAGS + "-mmacosx-version-min=${DEPLOYMENT_TARGET}") + else() + # SIMULATOR or SIMULATOR64 both use -mios-simulator-version-min. + set(SDK_NAME_VERSION_FLAGS + "-mios-simulator-version-min=${DEPLOYMENT_TARGET}") + endif() +elseif(NOT PLATFORM_INT STREQUAL "MAC_CATALYST") + # Newer versions of CMake sets the version min flags correctly, skip this for Mac Catalyst targets + set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET}) +endif() + +if(DEFINED APPLE_TARGET_TRIPLE_INT) + set(APPLE_TARGET_TRIPLE ${APPLE_TARGET_TRIPLE_INT} CACHE INTERNAL "") +endif() + +if(PLATFORM_INT STREQUAL "MAC_CATALYST") + set(C_TARGET_FLAGS "-target ${APPLE_TARGET_TRIPLE_INT} -isystem ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/usr/include") +endif() + +if(ENABLE_BITCODE_INT) + set(BITCODE "-fembed-bitcode") + set(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE "bitcode") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") +else() + set(BITCODE "") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") +endif() + +if(ENABLE_ARC_INT) + set(FOBJC_ARC "-fobjc-arc") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "YES") +else() + set(FOBJC_ARC "-fno-objc-arc") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "NO") +endif() + +if(NOT ENABLE_VISIBILITY_INT) + foreach(lang ${languages}) + set(CMAKE_${lang}_VISIBILITY_PRESET "hidden" CACHE INTERNAL "") + endforeach() + set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "YES") + set(VISIBILITY "-fvisibility=hidden -fvisibility-inlines-hidden") +else() + foreach(lang ${languages}) + set(CMAKE_${lang}_VISIBILITY_PRESET "default" CACHE INTERNAL "") + endforeach() + set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "NO") + set(VISIBILITY "-fvisibility=default") +endif() + +#Check if Xcode generator is used, since that will handle these flags automagically +if(CMAKE_GENERATOR MATCHES "Xcode") + message(STATUS "Not setting any manual command-line buildflags, since Xcode is selected as generator.") +else() + # Hidden visibility is required for C++ on iOS. + set(CMAKE_C_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -g ${CMAKE_CXX_FLAGS_DEBUG}") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DNDEBUG -Os -ffast-math ${CMAKE_CXX_FLAGS_MINSIZEREL}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -DNDEBUG -O2 -g -ffast-math ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DNDEBUG -O3 -ffast-math ${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_C_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") + set(CMAKE_CXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") + set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -arch ${CMAKE_OSX_ARCHITECTURES}") +endif() + +## Print status messages to inform of the current state +message(STATUS "Configuring ${SDK_NAME} build for platform: ${PLATFORM_INT}, architecture(s): ${ARCHS}") +message(STATUS "Using SDK: ${CMAKE_OSX_SYSROOT_INT}") +message(STATUS "Using C compiler: ${CMAKE_C_COMPILER}") +message(STATUS "Using CXX compiler: ${CMAKE_CXX_COMPILER}") +message(STATUS "Using libtool: ${BUILD_LIBTOOL}") +message(STATUS "Using install name tool: ${CMAKE_INSTALL_NAME_TOOL}") +if(DEFINED APPLE_TARGET_TRIPLE) + message(STATUS "Autoconf target triple: ${APPLE_TARGET_TRIPLE}") +endif() +message(STATUS "Using minimum deployment version: ${DEPLOYMENT_TARGET}" + " (SDK version: ${SDK_VERSION})") +if(MODERN_CMAKE) + message(STATUS "Merging integrated CMake 3.14+ iOS,tvOS,watchOS,macOS toolchain(s) with this toolchain!") +endif() +if(CMAKE_GENERATOR MATCHES "Xcode") + message(STATUS "Using Xcode version: ${XCODE_VERSION_INT}") +endif() +message(STATUS "CMake version: ${CMAKE_VERSION}") +if(DEFINED SDK_NAME_VERSION_FLAGS) + message(STATUS "Using version flags: ${SDK_NAME_VERSION_FLAGS}") +endif() +message(STATUS "Using a data_ptr size of: ${CMAKE_CXX_SIZEOF_DATA_PTR}") +if(ENABLE_BITCODE_INT) + message(STATUS "Bitcode: Enabled") +else() + message(STATUS "Bitcode: Disabled") +endif() + +if(ENABLE_ARC_INT) + message(STATUS "ARC: Enabled") +else() + message(STATUS "ARC: Disabled") +endif() + +if(ENABLE_VISIBILITY_INT) + message(STATUS "Hiding symbols: Disabled") +else() + message(STATUS "Hiding symbols: Enabled") +endif() + +# Set global properties +set_property(GLOBAL PROPERTY PLATFORM "${PLATFORM}") +set_property(GLOBAL PROPERTY APPLE_TARGET_TRIPLE "${APPLE_TARGET_TRIPLE_INT}") +set_property(GLOBAL PROPERTY SDK_VERSION "${SDK_VERSION}") +set_property(GLOBAL PROPERTY XCODE_VERSION "${XCODE_VERSION_INT}") +set_property(GLOBAL PROPERTY OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}") + +# Export configurable variables for the try_compile() command. +set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + PLATFORM + XCODE_VERSION_INT + SDK_VERSION + DEPLOYMENT_TARGET + CMAKE_DEVELOPER_ROOT + CMAKE_OSX_SYSROOT_INT + ENABLE_BITCODE + ENABLE_ARC + CMAKE_C_COMPILER + CMAKE_CXX_COMPILER + BUILD_LIBTOOL + CMAKE_INSTALL_NAME_TOOL + CMAKE_C_FLAGS + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_LINK_FLAGS + CMAKE_CXX_LINK_FLAGS + CMAKE_ASM_FLAGS + ) + +set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") +set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") +set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") + +# Set the find root to the SDK developer roots. +# Note: CMAKE_FIND_ROOT_PATH is only useful when cross-compiling. Thus, do not set on macOS builds. +if(NOT PLATFORM_INT STREQUAL "MAC" AND NOT PLATFORM_INT STREQUAL "MAC_ARM64") + list(APPEND CMAKE_FIND_ROOT_PATH "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") + set(CMAKE_IGNORE_PATH "/System/Library/Frameworks;/usr/local/lib" CACHE INTERNAL "") +endif() + +# Default to searching for frameworks first. +set(CMAKE_FIND_FRAMEWORK FIRST) + +# Set up the default search directories for frameworks. +if(PLATFORM_INT MATCHES "MAC_CATALYST.*") + set(CMAKE_FRAMEWORK_PATH + ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks + ${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks + ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/System/Library/Frameworks + ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") +else() + set(CMAKE_FRAMEWORK_PATH + ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks + ${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks + ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") +endif() + +# By default, search both the specified iOS SDK and the remainder of the host filesystem. +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH CACHE INTERNAL "") +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH CACHE INTERNAL "") +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH CACHE INTERNAL "") +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH CACHE INTERNAL "") +endif() + +# +# Some helper-macros below to simplify and beautify the CMakeFile +# + +# This little macro lets you set any Xcode specific property. +macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE XCODE_RELVERSION) + set(XCODE_RELVERSION_I "${XCODE_RELVERSION}") + if(XCODE_RELVERSION_I STREQUAL "All") + set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} "${XCODE_VALUE}") + else() + set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY}[variant=${XCODE_RELVERSION_I}] "${XCODE_VALUE}") + endif() +endmacro(set_xcode_property) + +# This macro lets you find executable programs on the host system. +macro(find_host_package) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) + set(_TOOLCHAIN_IOS ${IOS}) + set(IOS FALSE) + find_package(${ARGN}) + set(IOS ${_TOOLCHAIN_IOS}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) +endmacro(find_host_package) diff --git a/test_shard/iris_tester/cxx/render_configs_dart.yaml b/test_shard/iris_tester/cxx/render_configs_dart.yaml new file mode 100644 index 0000000..b8544e6 --- /dev/null +++ b/test_shard/iris_tester/cxx/render_configs_dart.yaml @@ -0,0 +1,6 @@ +include: shared:rtc_4.1.0/shared_configs.yaml + +language: dart + +lecacy_flags: + - gen-fake-rtcengine \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/event_triggers/rtcengine_eventhandler_trigger.hpp b/test_shard/iris_tester/cxx/src/event_triggers/rtcengine_eventhandler_trigger.hpp new file mode 100644 index 0000000..e232f7a --- /dev/null +++ b/test_shard/iris_tester/cxx/src/event_triggers/rtcengine_eventhandler_trigger.hpp @@ -0,0 +1,35 @@ +#ifndef RTCENGINE_EVENTHANDLER_TRIGGER_ +#define RTCENGINE_EVENTHANDLER_TRIGGER_ + +#include "IAgoraRtcEngine.h" +#include "IAgoraRtcEngineEx.h" + +using namespace agora::rtc; + +namespace agora +{ + namespace rtc + { + namespace event + { + class RtcEngineEventHandlerTrigger + { + public: + RtcEngineEventHandlerTrigger(IRtcEngineEventHandlerEx *eventHandler) : eventHandler_(eventHandler) {} + ~RtcEngineEventHandlerTrigger() = default; + + void TriggerEvent() + { + eventHandler_->onJoinChannelSuccess(RtcConnection("testonaction", 10), 100); + eventHandler_->onRejoinChannelSuccess(RtcConnection("testonaction", 10), 100); + } + + private: + IRtcEngineEventHandlerEx *eventHandler_; + }; + } // event + + } // rtc +} // agora + +#endif // RTCENGINE_EVENTHANDLER_TRIGGER_ \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/fake/fake_iaudiodevicemanager.hpp b/test_shard/iris_tester/cxx/src/fake/fake_iaudiodevicemanager.hpp new file mode 100644 index 0000000..bee509d --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake/fake_iaudiodevicemanager.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include "fake_iaudiodevicemanager_internal.hpp" +#include "AgoraBase.h" +#include "fake_iaudiodevicecollection_internal.hpp" +#include +#include +#include "IAudioDeviceManager.h" +#include + +namespace agora +{ + namespace rtc + { + + class FakeIAudioDeviceManager : public FakeIAudioDeviceManagerInternal + { + public: + FakeIAudioDeviceManager() = default; + + void AddRef() const override + { + } + RefCountReleaseStatus Release() const override + { + return RefCountReleaseStatus::kOtherRefsRemained; + } + bool HasOneRef() const override + { + return 0; + } + + agora::rtc::IAudioDeviceCollection * + enumeratePlaybackDevices() override + { + auto audioDeviceCollection = std::make_unique(); + + agora::rtc::IAudioDeviceCollection *ptr = audioDeviceCollection.get(); + + audioDeviceCollections_.push_back(std::move(audioDeviceCollection)); + + return ptr; + } + + agora::rtc::IAudioDeviceCollection * + enumerateRecordingDevices() override + { + auto audioDeviceCollection = std::make_unique(); + + agora::rtc::IAudioDeviceCollection *ptr = audioDeviceCollection.get(); + + audioDeviceCollections_.push_back(std::move(audioDeviceCollection)); + + return ptr; + } + + private: + std::vector> audioDeviceCollections_; + }; + + } // namespace rtc +} // namespace agora \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/fake/fake_ilocalspatialaudioengine.hpp b/test_shard/iris_tester/cxx/src/fake/fake_ilocalspatialaudioengine.hpp new file mode 100644 index 0000000..fb5ccf7 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake/fake_ilocalspatialaudioengine.hpp @@ -0,0 +1,30 @@ +#ifndef FAKEILOCALSPATIALAUDIOENGINE_H_ +#define FAKEILOCALSPATIALAUDIOENGINE_H_ + +#include "fake_ilocalspatialaudioengine_internal.hpp" +#include "AgoraBase.h" + +namespace agora +{ + namespace rtc + { + class FakeILocalSpatialAudioEngine : public FakeILocalSpatialAudioEngineInternal + { + + public: + void AddRef() const override + { + } + RefCountReleaseStatus Release() const override + { + return RefCountReleaseStatus::kOtherRefsRemained; + } + bool HasOneRef() const override + { + return 0; + } + }; + } // namespace rtc +} // namespace agora + +#endif // FAKEILOCALSPATIALAUDIOENGINE_H_ \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/fake/fake_imediaplayer.hpp b/test_shard/iris_tester/cxx/src/fake/fake_imediaplayer.hpp new file mode 100644 index 0000000..8db4476 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake/fake_imediaplayer.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "fake_imediaplayer_internal.hpp" + +namespace agora +{ + namespace rtc + { + + class FakeIMediaPlayer : public FakeIMediaPlayerInternal + { + public: + FakeIMediaPlayer(int media_player_id) : media_player_id_(media_player_id) {} + + void AddRef() const override + { + } + RefCountReleaseStatus Release() const override + { + return RefCountReleaseStatus::kOtherRefsRemained; + } + bool HasOneRef() const override + { + return 0; + } + + int getMediaPlayerId() const override { return media_player_id_; } + + private: + int media_player_id_; + }; + + } // namespace rtc +} // namespace agora \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/fake/fake_imusiccontentcenter.hpp b/test_shard/iris_tester/cxx/src/fake/fake_imusiccontentcenter.hpp new file mode 100644 index 0000000..75e1295 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake/fake_imusiccontentcenter.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include "fake_imusiccontentcenter_internal.hpp" +#include "fake_imusicplayer_internal.hpp" +#include "fake_imediaplayer.hpp" + +namespace agora +{ + namespace rtc + { + + class FakeIMusicPlayer : public FakeIMusicPlayerInternal, public FakeIMediaPlayer + { + public: + FakeIMusicPlayer(int media_player_id) : FakeIMediaPlayer(media_player_id) {} + + void AddRef() const override + { + } + RefCountReleaseStatus Release() const override + { + return RefCountReleaseStatus::kOtherRefsRemained; + } + bool HasOneRef() const override + { + return 0; + } + }; + + class FakeIMusicContentCenter : public FakeIMusicContentCenterInternal + { + public: + FakeIMusicContentCenter() : music_player_id_(0) + { + } + + agora_refptr createMusicPlayer() override + { + music_player_id_++; + agora_refptr mp = agora_refptr(new agora::rtc::FakeIMusicPlayer(music_player_id_)); + return mp; + } + + int getMusicCharts(agora::util::AString &requestId) override + { + agora::util::IString *str = new agora::util::FakeString; + requestId.reset(str); + return 0; + } + + int getMusicCollectionByMusicChartId( + agora::util::AString &requestId, int32_t musicChartId, int32_t page, + int32_t pageSize, char const *jsonOption) override + { + agora::util::IString *str = new agora::util::FakeString; + requestId.reset(str); + return 0; + } + + int searchMusic(agora::util::AString &requestId, char const *keyWord, + int32_t page, int32_t pageSize, + char const *jsonOption) override + { + agora::util::IString *str = new agora::util::FakeString; + requestId.reset(str); + return 0; + } + + int getLyric(agora::util::AString &requestId, int64_t songCode, + int32_t LyricType) override + { + agora::util::IString *str = new agora::util::FakeString; + requestId.reset(str); + + return 0; + } + + private: + int music_player_id_; + }; + + } // namespace rtc +} // namespace agora \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/fake/fake_irtcengineex.hpp b/test_shard/iris_tester/cxx/src/fake/fake_irtcengineex.hpp new file mode 100644 index 0000000..c856d6d --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake/fake_irtcengineex.hpp @@ -0,0 +1,156 @@ +#ifndef FAKE_IRTCENGINE_H_ +#define FAKE_IRTCENGINE_H_ + +#include "fake_irtcengineex_internal.hpp" +#include "fake_iagoraparameter_internal.hpp" +#include "fake_iaudiodevicemanager.hpp" +#include "fake_ilocalspatialaudioengine.hpp" +#include "fake_imediaengine_internal.hpp" +#include "fake_imediarecorder_internal.hpp" +#include "fake_ivideodevicemanager_internal.hpp" +#include +#include +#include "fake_string.hpp" +#include "fake_imediaplayer.hpp" +#include "fake_imusiccontentcenter.hpp" +#include "fake_imusiccontentcenter_internal.hpp" + +namespace agora +{ + namespace rtc + { + + class FakeIRtcEngine : public FakeIRtcEngineExInternal + { + + public: + FakeIRtcEngine() : media_player_id_(0), + fakeIAgoraParameterInternal_(std::make_unique()), + fakeIAudioDeviceManagerInternal_(std::make_unique()), + fakeIVideoDeviceManagerInternal_(std::make_unique()), + fakeIMediaEngineInternal_(std::make_unique()), + fakeILocalSpatialAudioEngineInternal_(std::make_unique()), + fakeIMediaRecorderInternal_(std::make_unique()), + fakeIMusicContentCenterInternal_(std::make_unique()) + { + } + ~FakeIRtcEngine() + { + this->eventHandler = nullptr; + } + + int queryInterface(INTERFACE_ID_TYPE iid, void **inter) override + { + if (iid == AGORA_IID_AUDIO_DEVICE_MANAGER) + { + *inter = fakeIAudioDeviceManagerInternal_.get(); + } + else if (iid == AGORA_IID_VIDEO_DEVICE_MANAGER) + { + *inter = fakeIVideoDeviceManagerInternal_.get(); + } + else if (iid == AGORA_IID_PARAMETER_ENGINE) + { + *inter = fakeIAgoraParameterInternal_.get(); + } + else if (iid == AGORA_IID_MEDIA_ENGINE) + { + *inter = fakeIMediaEngineInternal_.get(); + } + else if (iid == AGORA_IID_AUDIO_ENGINE) + { + } + else if (iid == AGORA_IID_VIDEO_ENGINE) + { + } + else if (iid == AGORA_IID_RTC_CONNECTION) + { + } + else if (iid == AGORA_IID_SIGNALING_ENGINE) + { + } + else if (iid == AGORA_IID_MEDIA_ENGINE_REGULATOR) + { + } + else if (iid == AGORA_IID_CLOUD_SPATIAL_AUDIO) + { + } + else if (iid == AGORA_IID_LOCAL_SPATIAL_AUDIO) + { + *inter = fakeILocalSpatialAudioEngineInternal_.get(); + } + else if (iid == AGORA_IID_MEDIA_RECORDER) + { + *inter = fakeIMediaRecorderInternal_.get(); + } + else if (iid == AGORA_IID_STATE_SYNC) + { + } + else if (iid == AGORA_IID_METACHAT_SERVICE) + { + } + else if (iid == AGORA_IID_MUSIC_CONTENT_CENTER) + { + *inter = fakeIMusicContentCenterInternal_.get(); + } + return 0; + } + + bool + registerEventHandler(IRtcEngineEventHandler *eventHandler) override + { + this->eventHandler = eventHandler; + return 0; + } + bool + unregisterEventHandler(IRtcEngineEventHandler *eventHandler) override + { + this->eventHandler = nullptr; + return 0; + } + + int uploadLogFile(agora::util::AString &requestId) override + { + agora::util::IString *str = new agora::util::FakeString; + requestId.reset(str); + return 0; + } + + int getCallId(agora::util::AString &callId) override + { + agora::util::IString *str = new agora::util::FakeString; + callId.reset(str); + return 0; + } + + virtual agora_refptr createMediaPlayer() override + { + media_player_id_++; + agora_refptr mp = agora_refptr(new agora::rtc::FakeIMediaPlayer(media_player_id_)); + return mp; + } + + virtual int destroyMediaPlayer( + agora_refptr media_player) override + { + return 0; + } + + public: + int media_player_id_ = 0; + + IRtcEngineEventHandler *eventHandler; + + std::unique_ptr fakeIAgoraParameterInternal_; + std::unique_ptr fakeIAudioDeviceManagerInternal_; + std::unique_ptr fakeIVideoDeviceManagerInternal_; + std::unique_ptr fakeIMediaEngineInternal_; + std::unique_ptr fakeILocalSpatialAudioEngineInternal_; + std::unique_ptr fakeIMediaRecorderInternal_; + std::unique_ptr fakeIMusicContentCenterInternal_; + }; + + } // namespace rtc +} // namespace agora + +#endif // FAKE_IRTCENGINE_H_ diff --git a/test_shard/iris_tester/cxx/src/fake/fake_string.hpp b/test_shard/iris_tester/cxx/src/fake/fake_string.hpp new file mode 100644 index 0000000..2ea742e --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake/fake_string.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "AgoraBase.h" + +namespace agora +{ + namespace util + { + class FakeString : public agora::util::IString + { + public: + bool empty() const override { return false; } + const char *c_str() override { return "123"; } + const char *data() override { return "123"; } + size_t length() override { return 3; } + void release() override {} + IString *clone() override { return nullptr; } + }; + } // namespace util +} // namespace agora \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_iagoraparameter_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_iagoraparameter_internal.hpp new file mode 100644 index 0000000..5ef75a3 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_iagoraparameter_internal.hpp @@ -0,0 +1,69 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IAGORAPARAMETER_INTERNAL_H_ +#define FAKE_IAGORAPARAMETER_INTERNAL_H_ + +#include "IAgoraParameter.h" + +namespace agora { +namespace base { +class FakeIAgoraParameterInternal : public agora::base::IAgoraParameter { + virtual void release() override {} + + virtual int setBool(char const *key, bool value) override { return 0; } + + virtual int setInt(char const *key, int value) override { return 0; } + + virtual int setUInt(char const *key, unsigned int value) override { + return 0; + } + + virtual int setNumber(char const *key, double value) override { return 0; } + + virtual int setString(char const *key, char const *value) override { + return 0; + } + + virtual int setObject(char const *key, char const *value) override { + return 0; + } + + virtual int setArray(char const *key, char const *value) override { + return 0; + } + + virtual int getBool(char const *key, bool &value) override { return 0; } + + virtual int getInt(char const *key, int &value) override { return 0; } + + virtual int getUInt(char const *key, unsigned int &value) override { + return 0; + } + + virtual int getNumber(char const *key, double &value) override { return 0; } + + virtual int getString(char const *key, agora::util::AString &value) override { + return 0; + } + + virtual int getObject(char const *key, agora::util::AString &value) override { + return 0; + } + + virtual int getArray(char const *key, char const *args, + agora::util::AString &value) override { + return 0; + } + + virtual int setParameters(char const *parameters) override { return 0; } + + virtual int convertPath(char const *filePath, + agora::util::AString &value) override { + return 0; + } +}; + +} // namespace base +} // namespace agora + +#endif // FAKE_IAGORAPARAMETER_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_iaudiodevicecollection_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_iaudiodevicecollection_internal.hpp new file mode 100644 index 0000000..e718792 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_iaudiodevicecollection_internal.hpp @@ -0,0 +1,38 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IAUDIODEVICECOLLECTION_INTERNAL_H_ +#define FAKE_IAUDIODEVICECOLLECTION_INTERNAL_H_ + +#include "IAudioDeviceManager.h" + +namespace agora { +namespace rtc { +class FakeIAudioDeviceCollectionInternal + : public agora::rtc::IAudioDeviceCollection { + virtual int getCount() override { return 0; } + + virtual int getDevice(int index, char *deviceName, char *deviceId) override { + return 0; + } + + virtual int setDevice(const char *deviceId) override { return 0; } + + virtual int getDefaultDevice(char *deviceName, char *deviceId) override { + return 0; + } + + virtual int setApplicationVolume(int volume) override { return 0; } + + virtual int getApplicationVolume(int &volume) override { return 0; } + + virtual int setApplicationMute(bool mute) override { return 0; } + + virtual int isApplicationMute(bool &mute) override { return 0; } + + virtual void release() override {} +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IAUDIODEVICECOLLECTION_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_iaudiodevicemanager_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_iaudiodevicemanager_internal.hpp new file mode 100644 index 0000000..2d37982 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_iaudiodevicemanager_internal.hpp @@ -0,0 +1,88 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IAUDIODEVICEMANAGER_INTERNAL_H_ +#define FAKE_IAUDIODEVICEMANAGER_INTERNAL_H_ + +#include "IAudioDeviceManager.h" + +namespace agora { +namespace rtc { +class FakeIAudioDeviceManagerInternal : public agora::rtc::IAudioDeviceManager { + virtual agora::rtc::IAudioDeviceCollection * + enumeratePlaybackDevices() override { + return 0; + } + + virtual agora::rtc::IAudioDeviceCollection * + enumerateRecordingDevices() override { + return 0; + } + + virtual int setPlaybackDevice(const char *deviceId) override { return 0; } + + virtual int getPlaybackDevice(char *deviceId) override { return 0; } + + virtual int getPlaybackDeviceInfo(char *deviceId, char *deviceName) override { + return 0; + } + + virtual int setPlaybackDeviceVolume(int volume) override { return 0; } + + virtual int getPlaybackDeviceVolume(int *volume) override { return 0; } + + virtual int setRecordingDevice(const char *deviceId) override { return 0; } + + virtual int getRecordingDevice(char *deviceId) override { return 0; } + + virtual int getRecordingDeviceInfo(char *deviceId, + char *deviceName) override { + return 0; + } + + virtual int setRecordingDeviceVolume(int volume) override { return 0; } + + virtual int getRecordingDeviceVolume(int *volume) override { return 0; } + + virtual int setLoopbackDevice(const char *deviceId) override { return 0; } + + virtual int getLoopbackDevice(char *deviceId) override { return 0; } + + virtual int setPlaybackDeviceMute(bool mute) override { return 0; } + + virtual int getPlaybackDeviceMute(bool *mute) override { return 0; } + + virtual int setRecordingDeviceMute(bool mute) override { return 0; } + + virtual int getRecordingDeviceMute(bool *mute) override { return 0; } + + virtual int startPlaybackDeviceTest(char const *testAudioFilePath) override { + return 0; + } + + virtual int stopPlaybackDeviceTest() override { return 0; } + + virtual int startRecordingDeviceTest(int indicationInterval) override { + return 0; + } + + virtual int stopRecordingDeviceTest() override { return 0; } + + virtual int startAudioDeviceLoopbackTest(int indicationInterval) override { + return 0; + } + + virtual int stopAudioDeviceLoopbackTest() override { return 0; } + + virtual int followSystemPlaybackDevice(bool enable) override { return 0; } + + virtual int followSystemRecordingDevice(bool enable) override { return 0; } + + virtual int followSystemLoopbackDevice(bool enable) override { return 0; } + + virtual void release() override {} +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IAUDIODEVICEMANAGER_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_ilocalspatialaudioengine_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_ilocalspatialaudioengine_internal.hpp new file mode 100644 index 0000000..fdc5b80 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_ilocalspatialaudioengine_internal.hpp @@ -0,0 +1,101 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_ILOCALSPATIALAUDIOENGINE_INTERNAL_H_ +#define FAKE_ILOCALSPATIALAUDIOENGINE_INTERNAL_H_ + +#include "IAgoraSpatialAudio.h" + +namespace agora { +namespace rtc { +class FakeILocalSpatialAudioEngineInternal + : public agora::rtc::ILocalSpatialAudioEngine { + virtual void release() override {} + + virtual int setMaxAudioRecvCount(int maxCount) override { return 0; } + + virtual int setAudioRecvRange(float range) override { return 0; } + + virtual int setDistanceUnit(float unit) override { return 0; } + + virtual int updateSelfPosition(float *position, float *axisForward, + float *axisRight, float *axisUp) override { + return 0; + } + + virtual int + updateSelfPositionEx(float *position, float *axisForward, float *axisRight, + float *axisUp, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int updatePlayerPositionInfo( + int playerId, + agora::rtc::RemoteVoicePositionInfo const &positionInfo) override { + return 0; + } + + virtual int setParameters(char const *params) override { return 0; } + + virtual int muteLocalAudioStream(bool mute) override { return 0; } + + virtual int muteAllRemoteAudioStreams(bool mute) override { return 0; } + + virtual int setZones(agora::rtc::SpatialAudioZone const *zones, + unsigned int zoneCount) override { + return 0; + } + + virtual int setPlayerAttenuation(int playerId, double attenuation, + bool forceSet) override { + return 0; + } + + virtual int muteRemoteAudioStream(agora::rtc::uid_t uid, bool mute) override { + return 0; + } + + virtual int + initialize(agora::rtc::LocalSpatialAudioConfig const &config) override { + return 0; + } + + virtual int updateRemotePosition( + agora::rtc::uid_t uid, + agora::rtc::RemoteVoicePositionInfo const &posInfo) override { + return 0; + } + + virtual int + updateRemotePositionEx(agora::rtc::uid_t uid, + agora::rtc::RemoteVoicePositionInfo const &posInfo, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int removeRemotePosition(agora::rtc::uid_t uid) override { return 0; } + + virtual int + removeRemotePositionEx(agora::rtc::uid_t uid, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int clearRemotePositions() override { return 0; } + + virtual int + clearRemotePositionsEx(agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setRemoteAudioAttenuation(agora::rtc::uid_t uid, + double attenuation, + bool forceSet) override { + return 0; + } +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_ILOCALSPATIALAUDIOENGINE_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_imediaengine_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_imediaengine_internal.hpp new file mode 100644 index 0000000..2449c22 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_imediaengine_internal.hpp @@ -0,0 +1,97 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IMEDIAENGINE_INTERNAL_H_ +#define FAKE_IMEDIAENGINE_INTERNAL_H_ + +#include "IAgoraMediaEngine.h" + +namespace agora { +namespace media { +class FakeIMediaEngineInternal : public agora::media::IMediaEngine { + virtual int registerAudioFrameObserver( + agora::media::IAudioFrameObserver *observer) override { + return 0; + } + + virtual int registerVideoFrameObserver( + agora::media::IVideoFrameObserver *observer) override { + return 0; + } + + virtual int registerVideoEncodedFrameObserver( + agora::media::IVideoEncodedFrameObserver *observer) override { + return 0; + } + + virtual int pushAudioFrame(agora::media::MEDIA_SOURCE_TYPE type, + IAudioFrameObserver::AudioFrame *frame, bool wrap, + int sourceId) override { + return 0; + } + + virtual int + pushCaptureAudioFrame(IAudioFrameObserver::AudioFrame *frame) override { + return 0; + } + + virtual int + pushReverseAudioFrame(IAudioFrameObserver::AudioFrame *frame) override { + return 0; + } + + virtual int + pushDirectAudioFrame(IAudioFrameObserver::AudioFrame *frame) override { + return 0; + } + + virtual int pullAudioFrame(IAudioFrameObserver::AudioFrame *frame) override { + return 0; + } + + virtual int + setExternalVideoSource(bool enabled, bool useTexture, + agora::media::EXTERNAL_VIDEO_SOURCE_TYPE sourceType, + rtc::SenderOptions encodedVideoOption) override { + return 0; + } + + virtual int setExternalAudioSource(bool enabled, int sampleRate, int channels, + int sourceNumber, bool localPlayback, + bool publish) override { + return 0; + } + + virtual int setExternalAudioSink(bool enabled, int sampleRate, + int channels) override { + return 0; + } + + virtual int enableCustomAudioLocalPlayback(int sourceId, + bool enabled) override { + return 0; + } + + virtual int setDirectExternalAudioSource(bool enable, + bool localPlayback) override { + return 0; + } + + virtual int pushVideoFrame(base::ExternalVideoFrame *frame, + unsigned int videoTrackId) override { + return 0; + } + + virtual int pushEncodedVideoImage( + uint8_t const *imageBuffer, size_t length, + agora::rtc::EncodedVideoFrameInfo const &videoEncodedFrameInfo, + unsigned int videoTrackId) override { + return 0; + } + + virtual void release() override {} +}; + +} // namespace media +} // namespace agora + +#endif // FAKE_IMEDIAENGINE_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_imediaplayer_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_imediaplayer_internal.hpp new file mode 100644 index 0000000..6262e50 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_imediaplayer_internal.hpp @@ -0,0 +1,191 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IMEDIAPLAYER_INTERNAL_H_ +#define FAKE_IMEDIAPLAYER_INTERNAL_H_ + +#include "IAgoraMediaPlayer.h" + +namespace agora { +namespace rtc { +class FakeIMediaPlayerInternal : public agora::rtc::IMediaPlayer { + virtual int initialize(base::IAgoraService *agora_service) override { + return 0; + } + + virtual int getMediaPlayerId() const override { return 0; } + + virtual int open(char const *url, int64_t startPos) override { return 0; } + + virtual int openWithCustomSource( + int64_t startPos, + media::base::IMediaPlayerCustomDataProvider *provider) override { + return 0; + } + + virtual int + openWithMediaSource(media::base::MediaSource const &source) override { + return 0; + } + + virtual int play() override { return 0; } + + virtual int pause() override { return 0; } + + virtual int stop() override { return 0; } + + virtual int resume() override { return 0; } + + virtual int seek(int64_t newPos) override { return 0; } + + virtual int setAudioPitch(int pitch) override { return 0; } + + virtual int getDuration(int64_t &duration) override { return 0; } + + virtual int getPlayPosition(int64_t &pos) override { return 0; } + + virtual int getStreamCount(int64_t &count) override { return 0; } + + virtual int getStreamInfo(int64_t index, + media::base::PlayerStreamInfo *info) override { + return 0; + } + + virtual int setLoopCount(int loopCount) override { return 0; } + + virtual int setPlaybackSpeed(int speed) override { return 0; } + + virtual int selectAudioTrack(int index) override { return 0; } + + virtual int setPlayerOption(char const *key, int value) override { return 0; } + + virtual int setPlayerOption(char const *key, char const *value) override { + return 0; + } + + virtual int takeScreenshot(char const *filename) override { return 0; } + + virtual int selectInternalSubtitle(int index) override { return 0; } + + virtual int setExternalSubtitle(char const *url) override { return 0; } + + virtual media::base::MEDIA_PLAYER_STATE getState() override { + return media::base::MEDIA_PLAYER_STATE::PLAYER_STATE_IDLE; + } + + virtual int mute(bool muted) override { return 0; } + + virtual int getMute(bool &muted) override { return 0; } + + virtual int adjustPlayoutVolume(int volume) override { return 0; } + + virtual int getPlayoutVolume(int &volume) override { return 0; } + + virtual int adjustPublishSignalVolume(int volume) override { return 0; } + + virtual int getPublishSignalVolume(int &volume) override { return 0; } + + virtual int setView(media::base::view_t view) override { return 0; } + + virtual int setRenderMode(media::base::RENDER_MODE_TYPE renderMode) override { + return 0; + } + + virtual int registerPlayerSourceObserver( + agora::rtc::IMediaPlayerSourceObserver *observer) override { + return 0; + } + + virtual int unregisterPlayerSourceObserver( + agora::rtc::IMediaPlayerSourceObserver *observer) override { + return 0; + } + + virtual int registerAudioFrameObserver( + media::base::IAudioFrameObserver *observer) override { + return 0; + } + + virtual int registerAudioFrameObserver( + media::base::IAudioFrameObserver *observer, + agora::rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE mode) override { + return 0; + } + + virtual int unregisterAudioFrameObserver( + media::base::IAudioFrameObserver *observer) override { + return 0; + } + + virtual int registerVideoFrameObserver( + media::base::IVideoFrameObserver *observer) override { + return 0; + } + + virtual int unregisterVideoFrameObserver( + agora::media::base::IVideoFrameObserver *observer) override { + return 0; + } + + virtual int registerMediaPlayerAudioSpectrumObserver( + media::IAudioSpectrumObserver *observer, int intervalInMS) override { + return 0; + } + + virtual int unregisterMediaPlayerAudioSpectrumObserver( + media::IAudioSpectrumObserver *observer) override { + return 0; + } + + virtual int + setAudioDualMonoMode(agora::media::base::AUDIO_DUAL_MONO_MODE mode) override { + return 0; + } + + virtual char const *getPlayerSdkVersion() override { return ""; } + + virtual char const *getPlaySrc() override { return ""; } + + virtual int openWithAgoraCDNSrc(char const *src, int64_t startPos) override { + return 0; + } + + virtual int getAgoraCDNLineCount() override { return 0; } + + virtual int switchAgoraCDNLineByIndex(int index) override { return 0; } + + virtual int getCurrentAgoraCDNIndex() override { return 0; } + + virtual int enableAutoSwitchAgoraCDN(bool enable) override { return 0; } + + virtual int renewAgoraCDNSrcToken(char const *token, int64_t ts) override { + return 0; + } + + virtual int switchAgoraCDNSrc(char const *src, bool syncPts) override { + return 0; + } + + virtual int switchSrc(char const *src, bool syncPts) override { return 0; } + + virtual int preloadSrc(char const *src, int64_t startPos) override { + return 0; + } + + virtual int playPreloadedSrc(char const *src) override { return 0; } + + virtual int unloadSrc(char const *src) override { return 0; } + + virtual int + setSpatialAudioParams(agora::SpatialAudioParams const ¶ms) override { + return 0; + } + + virtual int setSoundPositionParams(float pan, float gain) override { + return 0; + } +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IMEDIAPLAYER_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_imediarecorder_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_imediarecorder_internal.hpp new file mode 100644 index 0000000..14100c0 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_imediarecorder_internal.hpp @@ -0,0 +1,34 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IMEDIARECORDER_INTERNAL_H_ +#define FAKE_IMEDIARECORDER_INTERNAL_H_ + +#include "IAgoraMediaRecorder.h" + +namespace agora { +namespace rtc { +class FakeIMediaRecorderInternal : public agora::rtc::IMediaRecorder { + virtual int + setMediaRecorderObserver(agora::rtc::RtcConnection const &connection, + media::IMediaRecorderObserver *callback) override { + return 0; + } + + virtual int + startRecording(agora::rtc::RtcConnection const &connection, + media::MediaRecorderConfiguration const &config) override { + return 0; + } + + virtual int + stopRecording(agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual void release() override {} +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IMEDIARECORDER_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_imusiccontentcenter_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_imusiccontentcenter_internal.hpp new file mode 100644 index 0000000..672d917 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_imusiccontentcenter_internal.hpp @@ -0,0 +1,61 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IMUSICCONTENTCENTER_INTERNAL_H_ +#define FAKE_IMUSICCONTENTCENTER_INTERNAL_H_ + +#include "IAgoraMusicContentCenter.h" + +namespace agora { +namespace rtc { +class FakeIMusicContentCenterInternal : public agora::rtc::IMusicContentCenter { + virtual int initialize(agora::rtc::MusicContentCenterConfiguration const + &configuration) override { + return 0; + } + + virtual void release() override {} + + virtual int registerEventHandler( + agora::rtc::IMusicContentCenterEventHandler *eventHandler) override { + return 0; + } + + virtual int unregisterEventHandler() override { return 0; } + + virtual agora_refptr createMusicPlayer() override { + agora_refptr the_return; + return the_return; + } + + virtual int getMusicCharts(agora::util::AString &requestId) override { + return 0; + } + + virtual int getMusicCollectionByMusicChartId( + agora::util::AString &requestId, int32_t musicChartId, int32_t page, + int32_t pageSize, char const *jsonOption) override { + return 0; + } + + virtual int searchMusic(agora::util::AString &requestId, char const *keyWord, + int32_t page, int32_t pageSize, + char const *jsonOption) override { + return 0; + } + + virtual int preload(int64_t songCode, char const *jsonOption) override { + return 0; + } + + virtual int isPreloaded(int64_t songCode) override { return 0; } + + virtual int getLyric(agora::util::AString &requestId, int64_t songCode, + int32_t LyricType) override { + return 0; + } +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IMUSICCONTENTCENTER_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_imusicplayer_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_imusicplayer_internal.hpp new file mode 100644 index 0000000..da3be06 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_imusicplayer_internal.hpp @@ -0,0 +1,193 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IMUSICPLAYER_INTERNAL_H_ +#define FAKE_IMUSICPLAYER_INTERNAL_H_ + +#include "IAgoraMusicContentCenter.h" + +namespace agora { +namespace rtc { +class FakeIMusicPlayerInternal : public agora::rtc::IMusicPlayer { + virtual int initialize(base::IAgoraService *agora_service) override { + return 0; + } + + virtual int getMediaPlayerId() const override { return 0; } + + virtual int open(char const *url, int64_t startPos) override { return 0; } + + virtual int openWithCustomSource( + int64_t startPos, + media::base::IMediaPlayerCustomDataProvider *provider) override { + return 0; + } + + virtual int + openWithMediaSource(media::base::MediaSource const &source) override { + return 0; + } + + virtual int play() override { return 0; } + + virtual int pause() override { return 0; } + + virtual int stop() override { return 0; } + + virtual int resume() override { return 0; } + + virtual int seek(int64_t newPos) override { return 0; } + + virtual int setAudioPitch(int pitch) override { return 0; } + + virtual int getDuration(int64_t &duration) override { return 0; } + + virtual int getPlayPosition(int64_t &pos) override { return 0; } + + virtual int getStreamCount(int64_t &count) override { return 0; } + + virtual int getStreamInfo(int64_t index, + media::base::PlayerStreamInfo *info) override { + return 0; + } + + virtual int setLoopCount(int loopCount) override { return 0; } + + virtual int setPlaybackSpeed(int speed) override { return 0; } + + virtual int selectAudioTrack(int index) override { return 0; } + + virtual int setPlayerOption(char const *key, int value) override { return 0; } + + virtual int setPlayerOption(char const *key, char const *value) override { + return 0; + } + + virtual int takeScreenshot(char const *filename) override { return 0; } + + virtual int selectInternalSubtitle(int index) override { return 0; } + + virtual int setExternalSubtitle(char const *url) override { return 0; } + + virtual media::base::MEDIA_PLAYER_STATE getState() override { + return media::base::MEDIA_PLAYER_STATE::PLAYER_STATE_IDLE; + } + + virtual int mute(bool muted) override { return 0; } + + virtual int getMute(bool &muted) override { return 0; } + + virtual int adjustPlayoutVolume(int volume) override { return 0; } + + virtual int getPlayoutVolume(int &volume) override { return 0; } + + virtual int adjustPublishSignalVolume(int volume) override { return 0; } + + virtual int getPublishSignalVolume(int &volume) override { return 0; } + + virtual int setView(media::base::view_t view) override { return 0; } + + virtual int setRenderMode(media::base::RENDER_MODE_TYPE renderMode) override { + return 0; + } + + virtual int registerPlayerSourceObserver( + agora::rtc::IMediaPlayerSourceObserver *observer) override { + return 0; + } + + virtual int unregisterPlayerSourceObserver( + agora::rtc::IMediaPlayerSourceObserver *observer) override { + return 0; + } + + virtual int registerAudioFrameObserver( + media::base::IAudioFrameObserver *observer) override { + return 0; + } + + virtual int registerAudioFrameObserver( + media::base::IAudioFrameObserver *observer, + agora::rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE mode) override { + return 0; + } + + virtual int unregisterAudioFrameObserver( + media::base::IAudioFrameObserver *observer) override { + return 0; + } + + virtual int registerVideoFrameObserver( + media::base::IVideoFrameObserver *observer) override { + return 0; + } + + virtual int unregisterVideoFrameObserver( + agora::media::base::IVideoFrameObserver *observer) override { + return 0; + } + + virtual int registerMediaPlayerAudioSpectrumObserver( + media::IAudioSpectrumObserver *observer, int intervalInMS) override { + return 0; + } + + virtual int unregisterMediaPlayerAudioSpectrumObserver( + media::IAudioSpectrumObserver *observer) override { + return 0; + } + + virtual int + setAudioDualMonoMode(agora::media::base::AUDIO_DUAL_MONO_MODE mode) override { + return 0; + } + + virtual char const *getPlayerSdkVersion() override { return ""; } + + virtual char const *getPlaySrc() override { return ""; } + + virtual int openWithAgoraCDNSrc(char const *src, int64_t startPos) override { + return 0; + } + + virtual int getAgoraCDNLineCount() override { return 0; } + + virtual int switchAgoraCDNLineByIndex(int index) override { return 0; } + + virtual int getCurrentAgoraCDNIndex() override { return 0; } + + virtual int enableAutoSwitchAgoraCDN(bool enable) override { return 0; } + + virtual int renewAgoraCDNSrcToken(char const *token, int64_t ts) override { + return 0; + } + + virtual int switchAgoraCDNSrc(char const *src, bool syncPts) override { + return 0; + } + + virtual int switchSrc(char const *src, bool syncPts) override { return 0; } + + virtual int preloadSrc(char const *src, int64_t startPos) override { + return 0; + } + + virtual int playPreloadedSrc(char const *src) override { return 0; } + + virtual int unloadSrc(char const *src) override { return 0; } + + virtual int + setSpatialAudioParams(agora::SpatialAudioParams const ¶ms) override { + return 0; + } + + virtual int setSoundPositionParams(float pan, float gain) override { + return 0; + } + + virtual int open(int64_t songCode, int64_t startPos) override { return 0; } +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IMUSICPLAYER_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_irtcengineex_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_irtcengineex_internal.hpp new file mode 100644 index 0000000..8899324 --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_irtcengineex_internal.hpp @@ -0,0 +1,1489 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IRTCENGINEEX_INTERNAL_H_ +#define FAKE_IRTCENGINEEX_INTERNAL_H_ + +#include "IAgoraMediaPlayer.h" +#include "IAgoraRtcEngineEx.h" + +namespace agora { +namespace rtc { +class FakeIRtcEngineExInternal : public agora::rtc::IRtcEngineEx { + virtual int queryInterface(rtc::INTERFACE_ID_TYPE iid, + void **inter) override { + return 0; + } + + virtual void release(bool sync) override {} + + virtual int initialize(agora::rtc::RtcEngineContext const &context) override { + return 0; + } + + virtual char const *getVersion(int *build) override { return ""; } + + virtual char const *getErrorDescription(int code) override { return ""; } + + virtual int joinChannel(char const *token, char const *channelId, + char const *info, agora::rtc::uid_t uid) override { + return 0; + } + + virtual int + joinChannel(char const *token, char const *channelId, agora::rtc::uid_t uid, + agora::rtc::ChannelMediaOptions const &options) override { + return 0; + } + + virtual int updateChannelMediaOptions( + agora::rtc::ChannelMediaOptions const &options) override { + return 0; + } + + virtual int leaveChannel() override { return 0; } + + virtual int + leaveChannel(agora::rtc::LeaveChannelOptions const &options) override { + return 0; + } + + virtual int renewToken(char const *token) override { return 0; } + + virtual int setChannelProfile(agora::CHANNEL_PROFILE_TYPE profile) override { + return 0; + } + + virtual int setClientRole(agora::rtc::CLIENT_ROLE_TYPE role) override { + return 0; + } + + virtual int + setClientRole(agora::rtc::CLIENT_ROLE_TYPE role, + agora::rtc::ClientRoleOptions const &options) override { + return 0; + } + + virtual int startEchoTest() override { return 0; } + + virtual int startEchoTest(int intervalInSeconds) override { return 0; } + + virtual int + startEchoTest(agora::rtc::EchoTestConfiguration const &config) override { + return 0; + } + + virtual int stopEchoTest() override { return 0; } + +#if defined(__APPLE__) && TARGET_OS_IOS + virtual int enableMultiCamera( + bool enabled, + agora::rtc::CameraCapturerConfiguration const &config) override { + return 0; + } +#endif + + virtual int enableVideo() override { return 0; } + + virtual int disableVideo() override { return 0; } + + virtual int startPreview() override { return 0; } + + virtual int startPreview(agora::rtc::VIDEO_SOURCE_TYPE sourceType) override { + return 0; + } + + virtual int stopPreview() override { return 0; } + + virtual int stopPreview(agora::rtc::VIDEO_SOURCE_TYPE sourceType) override { + return 0; + } + + virtual int startLastmileProbeTest( + agora::rtc::LastmileProbeConfig const &config) override { + return 0; + } + + virtual int stopLastmileProbeTest() override { return 0; } + + virtual int setVideoEncoderConfiguration( + agora::rtc::VideoEncoderConfiguration const &config) override { + return 0; + } + + virtual int + setBeautyEffectOptions(bool enabled, agora::rtc::BeautyOptions const &options, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int + setLowlightEnhanceOptions(bool enabled, + agora::rtc::LowlightEnhanceOptions const &options, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int + setVideoDenoiserOptions(bool enabled, + agora::rtc::VideoDenoiserOptions const &options, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int + setColorEnhanceOptions(bool enabled, + agora::rtc::ColorEnhanceOptions const &options, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int + enableVirtualBackground(bool enabled, + agora::rtc::VirtualBackgroundSource backgroundSource, + agora::rtc::SegmentationProperty segproperty, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int enableRemoteSuperResolution(agora::rtc::uid_t userId, + bool enable) override { + return 0; + } + + virtual int setupRemoteVideo(agora::rtc::VideoCanvas const &canvas) override { + return 0; + } + + virtual int setupLocalVideo(agora::rtc::VideoCanvas const &canvas) override { + return 0; + } + + virtual int enableAudio() override { return 0; } + + virtual int disableAudio() override { return 0; } + + virtual int + setAudioProfile(agora::rtc::AUDIO_PROFILE_TYPE profile, + agora::rtc::AUDIO_SCENARIO_TYPE scenario) override { + return 0; + } + + virtual int setAudioProfile(agora::rtc::AUDIO_PROFILE_TYPE profile) override { + return 0; + } + + virtual int + setAudioScenario(agora::rtc::AUDIO_SCENARIO_TYPE scenario) override { + return 0; + } + + virtual int enableLocalAudio(bool enabled) override { return 0; } + + virtual int muteLocalAudioStream(bool mute) override { return 0; } + + virtual int muteAllRemoteAudioStreams(bool mute) override { return 0; } + + virtual int setDefaultMuteAllRemoteAudioStreams(bool mute) override { + return 0; + } + + virtual int muteRemoteAudioStream(agora::rtc::uid_t uid, bool mute) override { + return 0; + } + + virtual int muteLocalVideoStream(bool mute) override { return 0; } + + virtual int enableLocalVideo(bool enabled) override { return 0; } + + virtual int muteAllRemoteVideoStreams(bool mute) override { return 0; } + + virtual int setDefaultMuteAllRemoteVideoStreams(bool mute) override { + return 0; + } + + virtual int muteRemoteVideoStream(agora::rtc::uid_t uid, bool mute) override { + return 0; + } + + virtual int + setRemoteVideoStreamType(agora::rtc::uid_t uid, + agora::rtc::VIDEO_STREAM_TYPE streamType) override { + return 0; + } + + virtual int setRemoteVideoSubscriptionOptions( + agora::rtc::uid_t uid, + agora::rtc::VideoSubscriptionOptions const &options) override { + return 0; + } + + virtual int setRemoteDefaultVideoStreamType( + agora::rtc::VIDEO_STREAM_TYPE streamType) override { + return 0; + } + + virtual int setSubscribeAudioBlocklist(agora::rtc::uid_t *uidList, + int uidNumber) override { + return 0; + } + + virtual int setSubscribeAudioAllowlist(agora::rtc::uid_t *uidList, + int uidNumber) override { + return 0; + } + + virtual int setSubscribeVideoBlocklist(agora::rtc::uid_t *uidList, + int uidNumber) override { + return 0; + } + + virtual int setSubscribeVideoAllowlist(agora::rtc::uid_t *uidList, + int uidNumber) override { + return 0; + } + + virtual int enableAudioVolumeIndication(int interval, int smooth, + bool reportVad) override { + return 0; + } + + virtual int startAudioRecording( + char const *filePath, + agora::rtc::AUDIO_RECORDING_QUALITY_TYPE quality) override { + return 0; + } + + virtual int startAudioRecording( + char const *filePath, int sampleRate, + agora::rtc::AUDIO_RECORDING_QUALITY_TYPE quality) override { + return 0; + } + + virtual int startAudioRecording( + agora::rtc::AudioRecordingConfiguration const &config) override { + return 0; + } + + virtual int registerAudioEncodedFrameObserver( + agora::rtc::AudioEncodedFrameObserverConfig const &config, + agora::rtc::IAudioEncodedFrameObserver *observer) override { + return 0; + } + + virtual int stopAudioRecording() override { return 0; } + + virtual agora_refptr createMediaPlayer() override { + agora_refptr the_return; + return the_return; + } + + virtual int destroyMediaPlayer( + agora_refptr media_player) override { + return 0; + } + + virtual int startAudioMixing(char const *filePath, bool loopback, + int cycle) override { + return 0; + } + + virtual int startAudioMixing(char const *filePath, bool loopback, int cycle, + int startPos) override { + return 0; + } + + virtual int stopAudioMixing() override { return 0; } + + virtual int pauseAudioMixing() override { return 0; } + + virtual int resumeAudioMixing() override { return 0; } + + virtual int selectAudioTrack(int index) override { return 0; } + + virtual int getAudioTrackCount() override { return 0; } + + virtual int adjustAudioMixingVolume(int volume) override { return 0; } + + virtual int adjustAudioMixingPublishVolume(int volume) override { return 0; } + + virtual int getAudioMixingPublishVolume() override { return 0; } + + virtual int adjustAudioMixingPlayoutVolume(int volume) override { return 0; } + + virtual int getAudioMixingPlayoutVolume() override { return 0; } + + virtual int getAudioMixingDuration() override { return 0; } + + virtual int getAudioMixingCurrentPosition() override { return 0; } + + virtual int setAudioMixingPosition(int pos) override { return 0; } + + virtual int + setAudioMixingDualMonoMode(media::AUDIO_MIXING_DUAL_MONO_MODE mode) override { + return 0; + } + + virtual int setAudioMixingPitch(int pitch) override { return 0; } + + virtual int getEffectsVolume() override { return 0; } + + virtual int setEffectsVolume(int volume) override { return 0; } + + virtual int preloadEffect(int soundId, char const *filePath, + int startPos) override { + return 0; + } + + virtual int playEffect(int soundId, char const *filePath, int loopCount, + double pitch, double pan, int gain, bool publish, + int startPos) override { + return 0; + } + + virtual int playAllEffects(int loopCount, double pitch, double pan, int gain, + bool publish) override { + return 0; + } + + virtual int getVolumeOfEffect(int soundId) override { return 0; } + + virtual int setVolumeOfEffect(int soundId, int volume) override { return 0; } + + virtual int pauseEffect(int soundId) override { return 0; } + + virtual int pauseAllEffects() override { return 0; } + + virtual int resumeEffect(int soundId) override { return 0; } + + virtual int resumeAllEffects() override { return 0; } + + virtual int stopEffect(int soundId) override { return 0; } + + virtual int stopAllEffects() override { return 0; } + + virtual int unloadEffect(int soundId) override { return 0; } + + virtual int unloadAllEffects() override { return 0; } + + virtual int getEffectDuration(char const *filePath) override { return 0; } + + virtual int setEffectPosition(int soundId, int pos) override { return 0; } + + virtual int getEffectCurrentPosition(int soundId) override { return 0; } + + virtual int enableSoundPositionIndication(bool enabled) override { return 0; } + + virtual int setRemoteVoicePosition(agora::rtc::uid_t uid, double pan, + double gain) override { + return 0; + } + + virtual int enableSpatialAudio(bool enabled) override { return 0; } + + virtual int setRemoteUserSpatialAudioParams( + agora::rtc::uid_t uid, agora::SpatialAudioParams const ¶ms) override { + return 0; + } + + virtual int setVoiceBeautifierPreset( + agora::rtc::VOICE_BEAUTIFIER_PRESET preset) override { + return 0; + } + + virtual int + setAudioEffectPreset(agora::rtc::AUDIO_EFFECT_PRESET preset) override { + return 0; + } + + virtual int setVoiceConversionPreset( + agora::rtc::VOICE_CONVERSION_PRESET preset) override { + return 0; + } + + virtual int setAudioEffectParameters(agora::rtc::AUDIO_EFFECT_PRESET preset, + int param1, int param2) override { + return 0; + } + + virtual int + setVoiceBeautifierParameters(agora::rtc::VOICE_BEAUTIFIER_PRESET preset, + int param1, int param2) override { + return 0; + } + + virtual int + setVoiceConversionParameters(agora::rtc::VOICE_CONVERSION_PRESET preset, + int param1, int param2) override { + return 0; + } + + virtual int setLocalVoicePitch(double pitch) override { return 0; } + + virtual int setLocalVoiceEqualization( + agora::rtc::AUDIO_EQUALIZATION_BAND_FREQUENCY bandFrequency, + int bandGain) override { + return 0; + } + + virtual int setLocalVoiceReverb(agora::rtc::AUDIO_REVERB_TYPE reverbKey, + int value) override { + return 0; + } + + virtual int + setHeadphoneEQPreset(agora::rtc::HEADPHONE_EQUALIZER_PRESET preset) override { + return 0; + } + + virtual int setHeadphoneEQParameters(int lowGain, int highGain) override { + return 0; + } + + virtual int setLogFile(char const *filePath) override { return 0; } + + virtual int setLogFilter(unsigned int filter) override { return 0; } + + virtual int setLogLevel(commons::LOG_LEVEL level) override { return 0; } + + virtual int setLogFileSize(unsigned int fileSizeInKBytes) override { + return 0; + } + + virtual int uploadLogFile(agora::util::AString &requestId) override { + return 0; + } + + virtual int + setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode, + agora::rtc::VIDEO_MIRROR_MODE_TYPE mirrorMode) override { + return 0; + } + + virtual int + setRemoteRenderMode(agora::rtc::uid_t uid, + media::base::RENDER_MODE_TYPE renderMode, + agora::rtc::VIDEO_MIRROR_MODE_TYPE mirrorMode) override { + return 0; + } + + virtual int + setLocalRenderMode(media::base::RENDER_MODE_TYPE renderMode) override { + return 0; + } + + virtual int setLocalVideoMirrorMode( + agora::rtc::VIDEO_MIRROR_MODE_TYPE mirrorMode) override { + return 0; + } + + virtual int enableDualStreamMode(bool enabled) override { return 0; } + + virtual int enableDualStreamMode( + bool enabled, + agora::rtc::SimulcastStreamConfig const &streamConfig) override { + return 0; + } + + virtual int + setDualStreamMode(agora::rtc::SIMULCAST_STREAM_MODE mode) override { + return 0; + } + + virtual int setDualStreamMode( + agora::rtc::SIMULCAST_STREAM_MODE mode, + agora::rtc::SimulcastStreamConfig const &streamConfig) override { + return 0; + } + + virtual int enableEchoCancellationExternal(bool enabled, + int audioSourceDelay) override { + return 0; + } + + virtual int enableCustomAudioLocalPlayback(int sourceId, + bool enabled) override { + return 0; + } + + virtual int startPrimaryCustomAudioTrack( + agora::rtc::AudioTrackConfig const &config) override { + return 0; + } + + virtual int stopPrimaryCustomAudioTrack() override { return 0; } + + virtual int startSecondaryCustomAudioTrack( + agora::rtc::AudioTrackConfig const &config) override { + return 0; + } + + virtual int stopSecondaryCustomAudioTrack() override { return 0; } + + virtual int setRecordingAudioFrameParameters( + int sampleRate, int channel, + agora::rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE mode, + int samplesPerCall) override { + return 0; + } + + virtual int + setPlaybackAudioFrameParameters(int sampleRate, int channel, + agora::rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE mode, + int samplesPerCall) override { + return 0; + } + + virtual int setMixedAudioFrameParameters(int sampleRate, int channel, + int samplesPerCall) override { + return 0; + } + + virtual int setEarMonitoringAudioFrameParameters( + int sampleRate, int channel, + agora::rtc::RAW_AUDIO_FRAME_OP_MODE_TYPE mode, + int samplesPerCall) override { + return 0; + } + + virtual int + setPlaybackAudioFrameBeforeMixingParameters(int sampleRate, + int channel) override { + return 0; + } + + virtual int enableAudioSpectrumMonitor(int intervalInMS) override { + return 0; + } + + virtual int disableAudioSpectrumMonitor() override { return 0; } + + virtual int registerAudioSpectrumObserver( + agora::media::IAudioSpectrumObserver *observer) override { + return 0; + } + + virtual int unregisterAudioSpectrumObserver( + agora::media::IAudioSpectrumObserver *observer) override { + return 0; + } + + virtual int adjustRecordingSignalVolume(int volume) override { return 0; } + + virtual int muteRecordingSignal(bool mute) override { return 0; } + + virtual int adjustPlaybackSignalVolume(int volume) override { return 0; } + + virtual int adjustUserPlaybackSignalVolume(unsigned int uid, + int volume) override { + return 0; + } + + virtual int setLocalPublishFallbackOption( + agora::rtc::STREAM_FALLBACK_OPTIONS option) override { + return 0; + } + + virtual int setRemoteSubscribeFallbackOption( + agora::rtc::STREAM_FALLBACK_OPTIONS option) override { + return 0; + } + + virtual int enableLoopbackRecording(bool enabled, + char const *deviceName) override { + return 0; + } + + virtual int adjustLoopbackSignalVolume(int volume) override { return 0; } + + virtual int getLoopbackRecordingVolume() override { return 0; } + + virtual int enableInEarMonitoring(bool enabled, + int includeAudioFilters) override { + return 0; + } + + virtual int setInEarMonitoringVolume(int volume) override { return 0; } + +#if defined(_WIN32) || defined(__linux__) || defined(__ANDROID__) + virtual int loadExtensionProvider(char const *path, + bool unload_after_use) override { + return 0; + } +#endif + + virtual int setExtensionProviderProperty(char const *provider, + char const *key, + char const *value) override { + return 0; + } + + virtual int enableExtension(char const *provider, char const *extension, + bool enable, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int + setExtensionProperty(char const *provider, char const *extension, + char const *key, char const *value, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int + getExtensionProperty(char const *provider, char const *extension, + char const *key, char *value, int buf_len, + agora::media::MEDIA_SOURCE_TYPE type) override { + return 0; + } + + virtual int enableExtension(char const *provider, char const *extension, + agora::rtc::ExtensionInfo const &extensionInfo, + bool enable) override { + return 0; + } + + virtual int + setExtensionProperty(char const *provider, char const *extension, + agora::rtc::ExtensionInfo const &extensionInfo, + char const *key, char const *value) override { + return 0; + } + + virtual int + getExtensionProperty(char const *provider, char const *extension, + agora::rtc::ExtensionInfo const &extensionInfo, + char const *key, char *value, int buf_len) override { + return 0; + } + + virtual int setCameraCapturerConfiguration( + agora::rtc::CameraCapturerConfiguration const &config) override { + return 0; + } + + virtual agora::rtc::video_track_id_t createCustomVideoTrack() override { + agora::rtc::video_track_id_t the_return; + return the_return; + } + + virtual agora::rtc::video_track_id_t createCustomEncodedVideoTrack( + agora::rtc::SenderOptions const &sender_option) override { + agora::rtc::video_track_id_t the_return; + return the_return; + } + + virtual int destroyCustomVideoTrack( + agora::rtc::video_track_id_t video_track_id) override { + return 0; + } + + virtual int destroyCustomEncodedVideoTrack( + agora::rtc::video_track_id_t video_track_id) override { + return 0; + } + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int switchCamera() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual bool isCameraZoomSupported() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual bool isCameraFaceDetectSupported() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual bool isCameraTorchSupported() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual bool isCameraFocusSupported() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual bool isCameraAutoFocusFaceModeSupported() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int setCameraZoomFactor(float factor) override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int enableFaceDetection(bool enabled) override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual float getCameraMaxZoomFactor() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int setCameraFocusPositionInPreview(float positionX, + float positionY) override { + return 0; + } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int setCameraTorchOn(bool isOn) override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int setCameraAutoFocusFaceModeEnabled(bool enabled) override { + return 0; + } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual bool isCameraExposurePositionSupported() override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int setCameraExposurePosition(float positionXinView, + float positionYinView) override { + return 0; + } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) +#if defined(__APPLE__) + virtual bool isCameraAutoExposureFaceModeSupported() override { return 0; } +#endif +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) +#if defined(__APPLE__) + virtual int setCameraAutoExposureFaceModeEnabled(bool enabled) override { + return 0; + } +#endif +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int + setDefaultAudioRouteToSpeakerphone(bool defaultToSpeaker) override { + return 0; + } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int setEnableSpeakerphone(bool speakerOn) override { return 0; } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual bool isSpeakerphoneEnabled() override { return 0; } +#endif + +#if defined(_WIN32) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual agora::rtc::IScreenCaptureSourceList * + getScreenCaptureSources(agora::rtc::SIZE const &thumbSize, + agora::rtc::SIZE const &iconSize, + bool const includeScreen) override { + return 0; + } +#endif + +#if (defined(__APPLE__) && TARGET_OS_IOS) + virtual int setAudioSessionOperationRestriction( + agora::AUDIO_SESSION_OPERATION_RESTRICTION restriction) override { + return 0; + } +#endif + +#if defined(_WIN32) || \ + (defined(__APPLE__) && !TARGET_OS_IPHONE && TARGET_OS_MAC) + virtual int startScreenCaptureByDisplayId( + uint32_t displayId, agora::rtc::Rectangle const ®ionRect, + agora::rtc::ScreenCaptureParameters const &captureParams) override { + return 0; + } +#endif + +#if defined(_WIN32) + virtual int startScreenCaptureByScreenRect( + agora::rtc::Rectangle const &screenRect, + agora::rtc::Rectangle const ®ionRect, + agora::rtc::ScreenCaptureParameters const &captureParams) override { + return 0; + } +#endif + +#if defined(__ANDROID__) + virtual int getAudioDeviceInfo(agora::rtc::DeviceInfo &deviceInfo) override { + return 0; + } +#endif + +#if defined(_WIN32) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual int startScreenCaptureByWindowId( + agora::view_t windowId, agora::rtc::Rectangle const ®ionRect, + agora::rtc::ScreenCaptureParameters const &captureParams) override { + return 0; + } +#endif + +#if defined(_WIN32) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual int setScreenCaptureContentHint( + agora::rtc::VIDEO_CONTENT_HINT contentHint) override { + return 0; + } +#endif + +#if defined(_WIN32) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual int setScreenCaptureScenario( + agora::rtc::SCREEN_SCENARIO_TYPE screenScenario) override { + return 0; + } +#endif + +#if defined(_WIN32) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual int + updateScreenCaptureRegion(agora::rtc::Rectangle const ®ionRect) override { + return 0; + } +#endif + +#if defined(_WIN32) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual int updateScreenCaptureParameters( + agora::rtc::ScreenCaptureParameters const &captureParams) override { + return 0; + } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int startScreenCapture( + agora::rtc::ScreenCaptureParameters2 const &captureParams) override { + return 0; + } +#endif + +#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) + virtual int updateScreenCapture( + agora::rtc::ScreenCaptureParameters2 const &captureParams) override { + return 0; + } +#endif + +#if defined(_WIN32) || defined(__APPLE__) || defined(__ANDROID__) + virtual int stopScreenCapture() override { return 0; } +#endif + + virtual int getCallId(agora::util::AString &callId) override { return 0; } + + virtual int rate(char const *callId, int rating, + char const *description) override { + return 0; + } + + virtual int complain(char const *callId, char const *description) override { + return 0; + } + + virtual int startRtmpStreamWithoutTranscoding(char const *url) override { + return 0; + } + + virtual int startRtmpStreamWithTranscoding( + char const *url, + agora::rtc::LiveTranscoding const &transcoding) override { + return 0; + } + + virtual int updateRtmpTranscoding( + agora::rtc::LiveTranscoding const &transcoding) override { + return 0; + } + + virtual int stopRtmpStream(char const *url) override { return 0; } + + virtual int startLocalVideoTranscoder( + agora::rtc::LocalTranscoderConfiguration const &config) override { + return 0; + } + + virtual int updateLocalTranscoderConfiguration( + agora::rtc::LocalTranscoderConfiguration const &config) override { + return 0; + } + + virtual int stopLocalVideoTranscoder() override { return 0; } + + virtual int startPrimaryCameraCapture( + agora::rtc::CameraCapturerConfiguration const &config) override { + return 0; + } + + virtual int startSecondaryCameraCapture( + agora::rtc::CameraCapturerConfiguration const &config) override { + return 0; + } + + virtual int stopPrimaryCameraCapture() override { return 0; } + + virtual int stopSecondaryCameraCapture() override { return 0; } + + virtual int setCameraDeviceOrientation( + agora::rtc::VIDEO_SOURCE_TYPE type, + agora::rtc::VIDEO_ORIENTATION orientation) override { + return 0; + } + + virtual int setScreenCaptureOrientation( + agora::rtc::VIDEO_SOURCE_TYPE type, + agora::rtc::VIDEO_ORIENTATION orientation) override { + return 0; + } + + virtual int startPrimaryScreenCapture( + agora::rtc::ScreenCaptureConfiguration const &config) override { + return 0; + } + + virtual int startSecondaryScreenCapture( + agora::rtc::ScreenCaptureConfiguration const &config) override { + return 0; + } + + virtual int stopPrimaryScreenCapture() override { return 0; } + + virtual int stopSecondaryScreenCapture() override { return 0; } + + virtual agora::rtc::CONNECTION_STATE_TYPE getConnectionState() override { + return agora::rtc::CONNECTION_STATE_TYPE::CONNECTION_STATE_DISCONNECTED; + } + + virtual bool registerEventHandler( + agora::rtc::IRtcEngineEventHandler *eventHandler) override { + return 0; + } + + virtual bool unregisterEventHandler( + agora::rtc::IRtcEngineEventHandler *eventHandler) override { + return 0; + } + + virtual int + setRemoteUserPriority(agora::rtc::uid_t uid, + agora::rtc::PRIORITY_TYPE userPriority) override { + return 0; + } + + virtual int + registerPacketObserver(agora::rtc::IPacketObserver *observer) override { + return 0; + } + + virtual int setEncryptionMode(char const *encryptionMode) override { + return 0; + } + + virtual int setEncryptionSecret(char const *secret) override { return 0; } + + virtual int + enableEncryption(bool enabled, + agora::rtc::EncryptionConfig const &config) override { + return 0; + } + + virtual int createDataStream(int *streamId, bool reliable, + bool ordered) override { + return 0; + } + + virtual int createDataStream(int *streamId, + agora::rtc::DataStreamConfig &config) override { + return 0; + } + + virtual int sendStreamMessage(int streamId, char const *data, + size_t length) override { + return 0; + } + + virtual int + addVideoWatermark(agora::rtc::RtcImage const &watermark) override { + return 0; + } + + virtual int + addVideoWatermark(char const *watermarkUrl, + agora::rtc::WatermarkOptions const &options) override { + return 0; + } + + virtual int clearVideoWatermarks() override { return 0; } + + virtual int pauseAudio() override { return 0; } + + virtual int resumeAudio() override { return 0; } + + virtual int enableWebSdkInteroperability(bool enabled) override { return 0; } + + virtual int sendCustomReportMessage(char const *id, char const *category, + char const *event, char const *label, + int value) override { + return 0; + } + + virtual int registerMediaMetadataObserver( + agora::rtc::IMetadataObserver *observer, + IMetadataObserver::METADATA_TYPE type) override { + return 0; + } + + virtual int unregisterMediaMetadataObserver( + agora::rtc::IMetadataObserver *observer, + IMetadataObserver::METADATA_TYPE type) override { + return 0; + } + + virtual int startAudioFrameDump(char const *channel_id, + agora::rtc::uid_t user_id, + char const *location, char const *uuid, + char const *passwd, long duration_ms, + bool auto_upload) override { + return 0; + } + + virtual int stopAudioFrameDump(char const *channel_id, + agora::rtc::uid_t user_id, + char const *location) override { + return 0; + } + + virtual int registerLocalUserAccount(char const *appId, + char const *userAccount) override { + return 0; + } + + virtual int joinChannelWithUserAccount(char const *token, + char const *channelId, + char const *userAccount) override { + return 0; + } + + virtual int joinChannelWithUserAccount( + char const *token, char const *channelId, char const *userAccount, + agora::rtc::ChannelMediaOptions const &options) override { + return 0; + } + + virtual int joinChannelWithUserAccountEx( + char const *token, char const *channelId, char const *userAccount, + agora::rtc::ChannelMediaOptions const &options, + agora::rtc::IRtcEngineEventHandler *eventHandler) override { + return 0; + } + + virtual int getUserInfoByUserAccount(char const *userAccount, + rtc::UserInfo *userInfo) override { + return 0; + } + + virtual int getUserInfoByUid(agora::rtc::uid_t uid, + rtc::UserInfo *userInfo) override { + return 0; + } + + virtual int startChannelMediaRelay( + agora::rtc::ChannelMediaRelayConfiguration const &configuration) + override { + return 0; + } + + virtual int updateChannelMediaRelay( + agora::rtc::ChannelMediaRelayConfiguration const &configuration) + override { + return 0; + } + + virtual int stopChannelMediaRelay() override { return 0; } + + virtual int pauseAllChannelMediaRelay() override { return 0; } + + virtual int resumeAllChannelMediaRelay() override { return 0; } + + virtual int setDirectCdnStreamingAudioConfiguration( + agora::rtc::AUDIO_PROFILE_TYPE profile) override { + return 0; + } + + virtual int setDirectCdnStreamingVideoConfiguration( + agora::rtc::VideoEncoderConfiguration const &config) override { + return 0; + } + + virtual int startDirectCdnStreaming( + agora::rtc::IDirectCdnStreamingEventHandler *eventHandler, + char const *publishUrl, + agora::rtc::DirectCdnStreamingMediaOptions const &options) override { + return 0; + } + + virtual int stopDirectCdnStreaming() override { return 0; } + + virtual int updateDirectCdnStreamingMediaOptions( + agora::rtc::DirectCdnStreamingMediaOptions const &options) override { + return 0; + } + + virtual int startRhythmPlayer( + char const *sound1, char const *sound2, + agora::rtc::AgoraRhythmPlayerConfig const &config) override { + return 0; + } + + virtual int stopRhythmPlayer() override { return 0; } + + virtual int configRhythmPlayer( + agora::rtc::AgoraRhythmPlayerConfig const &config) override { + return 0; + } + + virtual int takeSnapshot(agora::rtc::uid_t uid, + char const *filePath) override { + return 0; + } + + virtual int + enableContentInspect(bool enabled, + media::ContentInspectConfig const &config) override { + return 0; + } + + virtual int adjustCustomAudioPublishVolume(int32_t sourceId, + int volume) override { + return 0; + } + + virtual int adjustCustomAudioPlayoutVolume(int32_t sourceId, + int volume) override { + return 0; + } + + virtual int setCloudProxy(agora::rtc::CLOUD_PROXY_TYPE proxyType) override { + return 0; + } + + virtual int setLocalAccessPoint( + agora::rtc::LocalAccessPointConfiguration const &config) override { + return 0; + } + + virtual int setAdvancedAudioOptions(agora::rtc::AdvancedAudioOptions &options, + int sourceType) override { + return 0; + } + + virtual int setAVSyncSource(char const *channelId, + agora::rtc::uid_t uid) override { + return 0; + } + + virtual int enableVideoImageSource( + bool enable, agora::rtc::ImageTrackOptions const &options) override { + return 0; + } + + virtual int64_t getCurrentMonotonicTimeInMs() override { + int64_t the_return; + return the_return; + } + + virtual int enableWirelessAccelerate(bool enabled) override { return 0; } + + virtual int getNetworkType() override { return 0; } + + virtual int + joinChannelEx(char const *token, agora::rtc::RtcConnection const &connection, + agora::rtc::ChannelMediaOptions const &options, + agora::rtc::IRtcEngineEventHandler *eventHandler) override { + return 0; + } + + virtual int + leaveChannelEx(agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + leaveChannelEx(agora::rtc::RtcConnection const &connection, + agora::rtc::LeaveChannelOptions const &options) override { + return 0; + } + + virtual int updateChannelMediaOptionsEx( + agora::rtc::ChannelMediaOptions const &options, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setVideoEncoderConfigurationEx( + agora::rtc::VideoEncoderConfiguration const &config, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + setupRemoteVideoEx(agora::rtc::VideoCanvas const &canvas, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int muteRemoteAudioStreamEx( + agora::rtc::uid_t uid, bool mute, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int muteRemoteVideoStreamEx( + agora::rtc::uid_t uid, bool mute, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setRemoteVideoStreamTypeEx( + agora::rtc::uid_t uid, agora::rtc::VIDEO_STREAM_TYPE streamType, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + muteLocalAudioStreamEx(bool mute, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + muteLocalVideoStreamEx(bool mute, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int muteAllRemoteAudioStreamsEx( + bool mute, agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int muteAllRemoteVideoStreamsEx( + bool mute, agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setSubscribeAudioBlocklistEx( + agora::rtc::uid_t *uidList, int uidNumber, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setSubscribeAudioAllowlistEx( + agora::rtc::uid_t *uidList, int uidNumber, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setSubscribeVideoBlocklistEx( + agora::rtc::uid_t *uidList, int uidNumber, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setSubscribeVideoAllowlistEx( + agora::rtc::uid_t *uidList, int uidNumber, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setRemoteVideoSubscriptionOptionsEx( + agora::rtc::uid_t uid, + agora::rtc::VideoSubscriptionOptions const &options, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setRemoteVoicePositionEx( + agora::rtc::uid_t uid, double pan, double gain, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setRemoteUserSpatialAudioParamsEx( + agora::rtc::uid_t uid, agora::SpatialAudioParams const ¶ms, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + setRemoteRenderModeEx(agora::rtc::uid_t uid, + media::base::RENDER_MODE_TYPE renderMode, + agora::rtc::VIDEO_MIRROR_MODE_TYPE mirrorMode, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + enableLoopbackRecordingEx(agora::rtc::RtcConnection const &connection, + bool enabled, char const *deviceName) override { + return 0; + } + + virtual int adjustUserPlaybackSignalVolumeEx( + unsigned int uid, int volume, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual agora::rtc::CONNECTION_STATE_TYPE + getConnectionStateEx(agora::rtc::RtcConnection const &connection) override { + return agora::rtc::CONNECTION_STATE_TYPE::CONNECTION_STATE_DISCONNECTED; + } + + virtual int + enableEncryptionEx(agora::rtc::RtcConnection const &connection, bool enabled, + agora::rtc::EncryptionConfig const &config) override { + return 0; + } + + virtual int + createDataStreamEx(int *streamId, bool reliable, bool ordered, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + createDataStreamEx(int *streamId, agora::rtc::DataStreamConfig &config, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + sendStreamMessageEx(int streamId, char const *data, size_t length, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + addVideoWatermarkEx(char const *watermarkUrl, + agora::rtc::WatermarkOptions const &options, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + clearVideoWatermarkEx(agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int sendCustomReportMessageEx( + char const *id, char const *category, char const *event, + char const *label, int value, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int enableAudioVolumeIndicationEx( + int interval, int smooth, bool reportVad, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int startRtmpStreamWithoutTranscodingEx( + char const *url, agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int startRtmpStreamWithTranscodingEx( + char const *url, agora::rtc::LiveTranscoding const &transcoding, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int updateRtmpTranscodingEx( + agora::rtc::LiveTranscoding const &transcoding, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + stopRtmpStreamEx(char const *url, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int startChannelMediaRelayEx( + agora::rtc::ChannelMediaRelayConfiguration const &configuration, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int updateChannelMediaRelayEx( + agora::rtc::ChannelMediaRelayConfiguration const &configuration, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int stopChannelMediaRelayEx( + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int pauseAllChannelMediaRelayEx( + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int resumeAllChannelMediaRelayEx( + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int getUserInfoByUserAccountEx( + char const *userAccount, rtc::UserInfo *userInfo, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + getUserInfoByUidEx(agora::rtc::uid_t uid, rtc::UserInfo *userInfo, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int setVideoProfileEx(int width, int height, int frameRate, + int bitrate) override { + return 0; + } + + virtual int + enableDualStreamModeEx(bool enabled, + agora::rtc::SimulcastStreamConfig const &streamConfig, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int + setDualStreamModeEx(agora::rtc::SIMULCAST_STREAM_MODE mode, + agora::rtc::SimulcastStreamConfig const &streamConfig, + agora::rtc::RtcConnection const &connection) override { + return 0; + } + + virtual int takeSnapshotEx(agora::rtc::RtcConnection const &connection, + agora::rtc::uid_t uid, + char const *filePath) override { + return 0; + } +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IRTCENGINEEX_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/fake_gen/fake_ivideodevicemanager_internal.hpp b/test_shard/iris_tester/cxx/src/fake_gen/fake_ivideodevicemanager_internal.hpp new file mode 100644 index 0000000..be090be --- /dev/null +++ b/test_shard/iris_tester/cxx/src/fake_gen/fake_ivideodevicemanager_internal.hpp @@ -0,0 +1,45 @@ +/// Generated by terra, DO NOT MODIFY BY HAND. + +#ifndef FAKE_IVIDEODEVICEMANAGER_INTERNAL_H_ +#define FAKE_IVIDEODEVICEMANAGER_INTERNAL_H_ + +#include "IAgoraRtcEngine.h" + +namespace agora { +namespace rtc { +class FakeIVideoDeviceManagerInternal : public agora::rtc::IVideoDeviceManager { + virtual agora::rtc::IVideoDeviceCollection *enumerateVideoDevices() override { + return 0; + } + + virtual int setDevice(const char *deviceIdUTF8) override { return 0; } + + virtual int getDevice(char *deviceIdUTF8) override { return 0; } + +#if defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__)) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual int numberOfCapabilities(char const *deviceIdUTF8) override { + return 0; + } +#endif + +#if defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__)) || \ + (defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE) + virtual int getCapability(char const *deviceIdUTF8, + uint32_t const deviceCapabilityNumber, + agora::rtc::VideoFormat &capability) override { + return 0; + } +#endif + + virtual int startDeviceTest(agora::view_t hwnd) override { return 0; } + + virtual int stopDeviceTest() override { return 0; } + + virtual void release() override {} +}; + +} // namespace rtc +} // namespace agora + +#endif // FAKE_IVIDEODEVICEMANAGER_INTERNAL_H_ diff --git a/test_shard/iris_tester/cxx/src/iris_debug.cc b/test_shard/iris_tester/cxx/src/iris_debug.cc new file mode 100644 index 0000000..96b6fad --- /dev/null +++ b/test_shard/iris_tester/cxx/src/iris_debug.cc @@ -0,0 +1,145 @@ +#include "iris_debug.h" +#include "fake_irtcengineex.hpp" +#include "iris_engine_base.h" +#include "iris_rtc_engine.h" +#include "rtcengine_eventhandler_trigger.hpp" +#include "IAgoraRtcEngineEx.h" + +#include +#include +#include + +using namespace std; +using namespace agora::rtc; +using namespace agora::iris::rtc; +using namespace agora::rtc::event; + +#define MOCK_FLAG_NONE 0 +#define MOCK_FLAG_RETCODE 1 +#define MOCK_FLAG_RESULT 2 + +struct IrisApiParam +{ + int flags; + int retcode; + std::string result; +}; + +static std::map g_mapMockApis; +static std::set g_setApiRecord; +static FakeIRtcEngine g_fakeRtcEngine; + +static string genApiCallhash(ApiParam *apiParam) +{ + string callhash = std::string(apiParam->event) + apiParam->data; + for (int i = 0; i < apiParam->buffer_count; i++) + { + uint64_t val = (uint64_t)apiParam->buffer[i]; + callhash.append("_" + std::to_string(val)); + } + return callhash; +} + +class IrisDebugApiEngine : public IApiEngineBase +{ +public: + IrisDebugApiEngine(IApiEngineBase *proxy) + { + if (proxy == nullptr) + proxy_ = (IApiEngineBase *)CreateIrisApiEngine(nullptr); + else + proxy_ = proxy; + } + + virtual int CallIrisApi(ApiParam *apiParam) + { + + auto actualCallRet = proxy_->CallIrisApi(apiParam); + + // Return directly if CallIrisApi failed. + if (actualCallRet < 0) + { + return actualCallRet; + } + + string callhash = genApiCallhash(apiParam); + + g_setApiRecord.insert(callhash); + + auto it = g_mapMockApis.find(apiParam->event); + if (it != g_mapMockApis.end()) + { + int flags = it->second.flags; + if (flags & MOCK_FLAG_RESULT) + { + memcpy(apiParam->result, it->second.result.c_str(), + it->second.result.size()); + } + + if (flags & MOCK_FLAG_RETCODE) + { + return it->second.retcode; + } + } + + return actualCallRet; + } + +private: + IApiEngineBase *proxy_; +}; + +void IRIS_CALL MockApiResult(const char *apiType, const char *param, + int paramLength) +{ + std::string data(param, paramLength); + auto it = g_mapMockApis.find(apiType); + if (it != g_mapMockApis.end()) + { + IrisApiParam api = {0}; + g_mapMockApis[apiType] = api; + } + + g_mapMockApis[apiType].flags |= MOCK_FLAG_RESULT; + g_mapMockApis[apiType].result = data; +} + +void IRIS_CALL MockApiReturnCode(const char *apiType, int code) +{ + auto it = g_mapMockApis.find(apiType); + if (it != g_mapMockApis.end()) + { + IrisApiParam api = {0}; + g_mapMockApis[apiType] = api; + } + + g_mapMockApis[apiType].flags |= MOCK_FLAG_RETCODE; + g_mapMockApis[apiType].retcode = code; +} + +bool IRIS_CALL ExpectCalled(ApiParam *param) +{ + string callhash = genApiCallhash(param); + if (g_setApiRecord.find(callhash) != g_setApiRecord.end()) + { + return true; + } + + return false; +} + +IRIS_API IrisApiEnginePtr IRIS_CALL CreateDebugApiEngine() +{ + auto *fakeRtcEngine = &g_fakeRtcEngine; + IrisDebugApiEngine *engine = new IrisDebugApiEngine((IApiEngineBase *)CreateIrisApiEngine(fakeRtcEngine)); + + return engine; +} + +IRIS_API void IRIS_CALL FireEvent(const char *event_name) +{ + auto *fakeRtcEngine = &g_fakeRtcEngine; + // fakeRtcEngine->eventHandler->onJoinChannelSuccess("testapi", 10, 100); + + RtcEngineEventHandlerTrigger(dynamic_cast(fakeRtcEngine->eventHandler)).TriggerEvent(); +} \ No newline at end of file diff --git a/test_shard/iris_tester/cxx/src/iris_debug.h b/test_shard/iris_tester/cxx/src/iris_debug.h new file mode 100644 index 0000000..8ae118c --- /dev/null +++ b/test_shard/iris_tester/cxx/src/iris_debug.h @@ -0,0 +1,19 @@ +#ifndef __IRIS_DEBUG_H__ +#define __IRIS_DEBUG_H__ + +#include "iris_rtc_c_api.h" + +IRIS_API IrisApiEnginePtr IRIS_CALL CreateDebugApiEngine(); + +IRIS_API void IRIS_CALL MockApiResult(const char *apiType, const char *param, + int paramLength); + +IRIS_API void IRIS_CALL MockApiReturnCode(const char *apiType, int code); + +IRIS_API bool IRIS_CALL ExpectCalled(const char *func_name, const char *params, + uint32_t paramLength, void **buffers, + uint32_t buffer_count); + +IRIS_API void IRIS_CALL FireEvent(const char *event_name); + +#endif \ No newline at end of file diff --git a/test_shard/iris_tester/example/.gitignore b/test_shard/iris_tester/example/.gitignore new file mode 100644 index 0000000..a8e938c --- /dev/null +++ b/test_shard/iris_tester/example/.gitignore @@ -0,0 +1,47 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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 +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/test_shard/iris_tester/example/README.md b/test_shard/iris_tester/example/README.md new file mode 100644 index 0000000..ed41ca8 --- /dev/null +++ b/test_shard/iris_tester/example/README.md @@ -0,0 +1,16 @@ +# iris_tester_example + +Demonstrates how to use the iris_tester plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://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/test_shard/iris_tester/example/analysis_options.yaml b/test_shard/iris_tester/example/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/test_shard/iris_tester/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# 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 + +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-lang.github.io/linter/lints/index.html. + # + # 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/test_shard/iris_tester/example/android/.gitignore b/test_shard/iris_tester/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/test_shard/iris_tester/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/test_shard/iris_tester/example/android/app/build.gradle b/test_shard/iris_tester/example/android/app/build.gradle new file mode 100644 index 0000000..ed16e97 --- /dev/null +++ b/test_shard/iris_tester/example/android/app/build.gradle @@ -0,0 +1,71 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.iris_tester_example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/test_shard/iris_tester/example/android/app/src/debug/AndroidManifest.xml b/test_shard/iris_tester/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..a53d236 --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/test_shard/iris_tester/example/android/app/src/main/AndroidManifest.xml b/test_shard/iris_tester/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5a61d08 --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/test_shard/iris_tester/example/android/app/src/main/kotlin/com/example/iris_tester_example/MainActivity.kt b/test_shard/iris_tester/example/android/app/src/main/kotlin/com/example/iris_tester_example/MainActivity.kt new file mode 100644 index 0000000..a25e7c0 --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/main/kotlin/com/example/iris_tester_example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.iris_tester_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/test_shard/iris_tester/example/android/app/src/main/res/drawable-v21/launch_background.xml b/test_shard/iris_tester/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/test_shard/iris_tester/example/android/app/src/main/res/drawable/launch_background.xml b/test_shard/iris_tester/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/test_shard/iris_tester/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/test_shard/iris_tester/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/test_shard/iris_tester/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/test_shard/iris_tester/example/android/app/src/main/res/values-night/styles.xml b/test_shard/iris_tester/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/test_shard/iris_tester/example/android/app/src/main/res/values/styles.xml b/test_shard/iris_tester/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/test_shard/iris_tester/example/android/app/src/profile/AndroidManifest.xml b/test_shard/iris_tester/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..a53d236 --- /dev/null +++ b/test_shard/iris_tester/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/test_shard/iris_tester/example/android/build.gradle b/test_shard/iris_tester/example/android/build.gradle new file mode 100644 index 0000000..83ae220 --- /dev/null +++ b/test_shard/iris_tester/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/test_shard/iris_tester/example/android/gradle.properties b/test_shard/iris_tester/example/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/test_shard/iris_tester/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/test_shard/iris_tester/example/android/gradle/wrapper/gradle-wrapper.properties b/test_shard/iris_tester/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cc5527d --- /dev/null +++ b/test_shard/iris_tester/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/test_shard/iris_tester/example/android/settings.gradle b/test_shard/iris_tester/example/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/test_shard/iris_tester/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/test_shard/iris_tester/example/ios/.gitignore b/test_shard/iris_tester/example/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/ios/Flutter/AppFrameworkInfo.plist b/test_shard/iris_tester/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/test_shard/iris_tester/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 + 9.0 + + diff --git a/test_shard/iris_tester/example/ios/Flutter/Debug.xcconfig b/test_shard/iris_tester/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/ios/Flutter/Release.xcconfig b/test_shard/iris_tester/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/ios/Podfile b/test_shard/iris_tester/example/ios/Podfile new file mode 100644 index 0000000..f7d6a5e --- /dev/null +++ b/test_shard/iris_tester/example/ios/Podfile @@ -0,0 +1,38 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.pbxproj b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0c5cf0e --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,480 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.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; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.irisTesterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.irisTesterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.irisTesterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/test_shard/iris_tester/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test_shard/iris_tester/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c87d15a --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/iris_tester/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/test_shard/iris_tester/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/test_shard/iris_tester/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/iris_tester/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/iris_tester/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/test_shard/iris_tester/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/test_shard/iris_tester/example/ios/Runner/AppDelegate.h b/test_shard/iris_tester/example/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..36e21bb --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/test_shard/iris_tester/example/ios/Runner/AppDelegate.m b/test_shard/iris_tester/example/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..70e8393 --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#import "AppDelegate.h" +#import "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/test_shard/iris_tester/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/test_shard/iris_tester/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/iris_tester/example/ios/Runner/Base.lproj/Main.storyboard b/test_shard/iris_tester/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/iris_tester/example/ios/Runner/Info.plist b/test_shard/iris_tester/example/ios/Runner/Info.plist new file mode 100644 index 0000000..1ea8f63 --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Iris Tester + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + iris_tester_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 + + + diff --git a/test_shard/iris_tester/example/ios/Runner/main.m b/test_shard/iris_tester/example/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/test_shard/iris_tester/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/test_shard/iris_tester/example/lib/main.dart b/test_shard/iris_tester/example/lib/main.dart new file mode 100644 index 0000000..59b94a4 --- /dev/null +++ b/test_shard/iris_tester/example/lib/main.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = ''; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} diff --git a/test_shard/iris_tester/example/macos/.gitignore b/test_shard/iris_tester/example/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/test_shard/iris_tester/example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/test_shard/iris_tester/example/macos/Flutter/Flutter-Debug.xcconfig b/test_shard/iris_tester/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..4b81f9b --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/macos/Flutter/Flutter-Release.xcconfig b/test_shard/iris_tester/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5caa9d1 --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/macos/Flutter/GeneratedPluginRegistrant.swift b/test_shard/iris_tester/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..7faffa4 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import iris_tester + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + IrisTesterPlugin.register(with: registry.registrar(forPlugin: "IrisTesterPlugin")) +} diff --git a/test_shard/iris_tester/example/macos/Podfile b/test_shard/iris_tester/example/macos/Podfile new file mode 100644 index 0000000..dade8df --- /dev/null +++ b/test_shard/iris_tester/example/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/test_shard/iris_tester/example/macos/Runner.xcodeproj/project.pbxproj b/test_shard/iris_tester/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9db3131 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,577 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* iris_tester_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iris_tester_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* iris_tester_example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* iris_tester_example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; + 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", + ); + ONLY_ACTIVE_ARCH = YES; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; + 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", + ); + ONLY_ACTIVE_ARCH = YES; + 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", + ); + ONLY_ACTIVE_ARCH = YES; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/test_shard/iris_tester/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/iris_tester/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/iris_tester/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test_shard/iris_tester/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..f8356d9 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/iris_tester/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/test_shard/iris_tester/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/test_shard/iris_tester/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/iris_tester/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/iris_tester/example/macos/Runner/AppDelegate.swift b/test_shard/iris_tester/example/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..3c4935a Binary files /dev/null and b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..ed4cc16 Binary files /dev/null and b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..483be61 Binary files /dev/null and b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bcbf36d Binary files /dev/null and b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..9c0a652 Binary files /dev/null and b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..e71a726 Binary files /dev/null and b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..8a31fe2 Binary files /dev/null and b/test_shard/iris_tester/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/test_shard/iris_tester/example/macos/Runner/Base.lproj/MainMenu.xib b/test_shard/iris_tester/example/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/iris_tester/example/macos/Runner/Configs/AppInfo.xcconfig b/test_shard/iris_tester/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..928bc80 --- /dev/null +++ b/test_shard/iris_tester/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 = iris_tester_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.irisTesterExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. diff --git a/test_shard/iris_tester/example/macos/Runner/Configs/Debug.xcconfig b/test_shard/iris_tester/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/test_shard/iris_tester/example/macos/Runner/Configs/Release.xcconfig b/test_shard/iris_tester/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/test_shard/iris_tester/example/macos/Runner/Configs/Warnings.xcconfig b/test_shard/iris_tester/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/macos/Runner/DebugProfile.entitlements b/test_shard/iris_tester/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/macos/Runner/Info.plist b/test_shard/iris_tester/example/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/macos/Runner/MainFlutterWindow.swift b/test_shard/iris_tester/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..2722837 --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/test_shard/iris_tester/example/macos/Runner/Release.entitlements b/test_shard/iris_tester/example/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/test_shard/iris_tester/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/test_shard/iris_tester/example/pubspec.lock b/test_shard/iris_tester/example/pubspec.lock new file mode 100644 index 0000000..bcb4e10 --- /dev/null +++ b/test_shard/iris_tester/example/pubspec.lock @@ -0,0 +1,249 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + iris_tester: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" +sdks: + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/test_shard/iris_tester/example/pubspec.yaml b/test_shard/iris_tester/example/pubspec.yaml new file mode 100644 index 0000000..67e047e --- /dev/null +++ b/test_shard/iris_tester/example/pubspec.yaml @@ -0,0 +1,84 @@ +name: iris_tester_example +description: Demonstrates how to use the iris_tester plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ">=2.17.1 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + iris_tester: + # When depending on this package from a real application you should use: + # iris_tester: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_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: ^2.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/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test_shard/iris_tester/example/test/widget_test.dart b/test_shard/iris_tester/example/test/widget_test.dart new file mode 100644 index 0000000..e72b582 --- /dev/null +++ b/test_shard/iris_tester/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// 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:iris_tester_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/test_shard/iris_tester/example/windows/.gitignore b/test_shard/iris_tester/example/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/test_shard/iris_tester/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/test_shard/iris_tester/example/windows/CMakeLists.txt b/test_shard/iris_tester/example/windows/CMakeLists.txt new file mode 100644 index 0000000..3109ba0 --- /dev/null +++ b/test_shard/iris_tester/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(iris_tester_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 "iris_tester_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# 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() + +# 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/test_shard/iris_tester/example/windows/flutter/CMakeLists.txt b/test_shard/iris_tester/example/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..930d207 --- /dev/null +++ b/test_shard/iris_tester/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# 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") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/test_shard/iris_tester/example/windows/flutter/generated_plugin_registrant.cc b/test_shard/iris_tester/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..9a218e1 --- /dev/null +++ b/test_shard/iris_tester/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + IrisTesterPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("IrisTesterPluginCApi")); +} diff --git a/test_shard/iris_tester/example/windows/flutter/generated_plugin_registrant.h b/test_shard/iris_tester/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/test_shard/iris_tester/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/test_shard/iris_tester/example/windows/flutter/generated_plugins.cmake b/test_shard/iris_tester/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..120e79b --- /dev/null +++ b/test_shard/iris_tester/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + iris_tester +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/test_shard/iris_tester/example/windows/runner/CMakeLists.txt b/test_shard/iris_tester/example/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..b9e550f --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/CMakeLists.txt @@ -0,0 +1,32 @@ +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}) + +# 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_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/test_shard/iris_tester/example/windows/runner/Runner.rc b/test_shard/iris_tester/example/windows/runner/Runner.rc new file mode 100644 index 0000000..ae79781 --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "iris_tester_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "iris_tester_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "iris_tester_example.exe" "\0" + VALUE "ProductName", "iris_tester_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/test_shard/iris_tester/example/windows/runner/flutter_window.cpp b/test_shard/iris_tester/example/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..b43b909 --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/test_shard/iris_tester/example/windows/runner/flutter_window.h b/test_shard/iris_tester/example/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#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/test_shard/iris_tester/example/windows/runner/main.cpp b/test_shard/iris_tester/example/windows/runner/main.cpp new file mode 100644 index 0000000..56c0a80 --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"iris_tester_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/test_shard/iris_tester/example/windows/runner/resource.h b/test_shard/iris_tester/example/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/test_shard/iris_tester/example/windows/runner/resources/app_icon.ico b/test_shard/iris_tester/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000..c04e20c Binary files /dev/null and b/test_shard/iris_tester/example/windows/runner/resources/app_icon.ico differ diff --git a/test_shard/iris_tester/example/windows/runner/runner.exe.manifest b/test_shard/iris_tester/example/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..c977c4a --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/test_shard/iris_tester/example/windows/runner/utils.cpp b/test_shard/iris_tester/example/windows/runner/utils.cpp new file mode 100644 index 0000000..f5bf9fa --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + 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, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/test_shard/iris_tester/example/windows/runner/utils.h b/test_shard/iris_tester/example/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#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/test_shard/iris_tester/example/windows/runner/win32_window.cpp b/test_shard/iris_tester/example/windows/runner/win32_window.cpp new file mode 100644 index 0000000..c10f08d --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/test_shard/iris_tester/example/windows/runner/win32_window.h b/test_shard/iris_tester/example/windows/runner/win32_window.h new file mode 100644 index 0000000..17ba431 --- /dev/null +++ b/test_shard/iris_tester/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/test_shard/iris_tester/ffigen.sh b/test_shard/iris_tester/ffigen.sh new file mode 100644 index 0000000..881d9c6 --- /dev/null +++ b/test_shard/iris_tester/ffigen.sh @@ -0,0 +1,24 @@ + + +#!/usr/bin/env bash + +set -e +set -x + +MY_PATH=$(dirname "$0") +IRIS_PATH=$1 + +pushd ${MY_PATH} + +mkdir -p ${MY_PATH}/ffigen_include + +cp -RP ${IRIS_PATH}/src/rtm/include/* ${MY_PATH}/ffigen_include/ +cp -RP ${IRIS_PATH}/debug/rtm/*.h ${MY_PATH}/ffigen_include/ +cp -RP ${IRIS_PATH}/common/public/*.h ${MY_PATH}/ffigen_include/ +cp -RP ${IRIS_PATH}/debug/rtm/iris_debug_rtm.h ${MY_PATH}/ffigen_include/iris_debug_rtm.h + +flutter pub run ffigen --config ${MY_PATH}/ffigen_config.yaml + +rm -rf ${MY_PATH}/ffigen_include + +popd \ No newline at end of file diff --git a/test_shard/iris_tester/ffigen_config.yaml b/test_shard/iris_tester/ffigen_config.yaml new file mode 100644 index 0000000..23cf618 --- /dev/null +++ b/test_shard/iris_tester/ffigen_config.yaml @@ -0,0 +1,16 @@ +name: 'NativeIrisTesterRtmBinding' +description: 'Bindings to IrisApiEngine' + +output: 'lib/src/platform/io/iris_tester_rtm_bindings.dart' + +headers: + entry-points: + - 'ffigen_include/iris_debug_rtm.h' + include-directives: + - 'ffigen_include/iris_debug_rtm.h' + +preamble: | + // ignore_for_file: camel_case_types, non_constant_identifier_names + +llvm-path: + - '/opt/homebrew/opt/llvm@15' \ No newline at end of file diff --git a/test_shard/iris_tester/ios/.gitignore b/test_shard/iris_tester/ios/.gitignore new file mode 100644 index 0000000..b673039 --- /dev/null +++ b/test_shard/iris_tester/ios/.gitignore @@ -0,0 +1,40 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh + +IrisDebugger.xcframework/ \ No newline at end of file diff --git a/test_shard/iris_tester/ios/Assets/.gitkeep b/test_shard/iris_tester/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test_shard/iris_tester/ios/Classes/IrisTesterPlugin.h b/test_shard/iris_tester/ios/Classes/IrisTesterPlugin.h new file mode 100644 index 0000000..fad5c4f --- /dev/null +++ b/test_shard/iris_tester/ios/Classes/IrisTesterPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface IrisTesterPlugin : NSObject +@end diff --git a/test_shard/iris_tester/ios/Classes/IrisTesterPlugin.m b/test_shard/iris_tester/ios/Classes/IrisTesterPlugin.m new file mode 100644 index 0000000..3250b60 --- /dev/null +++ b/test_shard/iris_tester/ios/Classes/IrisTesterPlugin.m @@ -0,0 +1,15 @@ +#import "IrisTesterPlugin.h" +#if __has_include() +#import +#else +// Support project import fallback if the generated compatibility header +// is not copied when this plugin is created as a library. +// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 +#import "iris_tester-Swift.h" +#endif + +@implementation IrisTesterPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + [SwiftIrisTesterPlugin registerWithRegistrar:registrar]; +} +@end diff --git a/test_shard/iris_tester/ios/Classes/SwiftIrisTesterPlugin.swift b/test_shard/iris_tester/ios/Classes/SwiftIrisTesterPlugin.swift new file mode 100644 index 0000000..f925e95 --- /dev/null +++ b/test_shard/iris_tester/ios/Classes/SwiftIrisTesterPlugin.swift @@ -0,0 +1,14 @@ +import Flutter +import UIKit + +public class SwiftIrisTesterPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "iris_tester", binaryMessenger: registrar.messenger()) + let instance = SwiftIrisTesterPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + result("iOS " + UIDevice.current.systemVersion) + } +} diff --git a/test_shard/iris_tester/ios/iris_tester.podspec b/test_shard/iris_tester/ios/iris_tester.podspec new file mode 100644 index 0000000..85f538f --- /dev/null +++ b/test_shard/iris_tester/ios/iris_tester.podspec @@ -0,0 +1,24 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint iris_tester.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'iris_tester' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.vendored_frameworks = 'IrisDebugger.xcframework' + s.platform = :ios, '9.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' +end diff --git a/test_shard/iris_tester/lib/iris_tester.dart b/test_shard/iris_tester/lib/iris_tester.dart new file mode 100644 index 0000000..cedb39d --- /dev/null +++ b/test_shard/iris_tester/lib/iris_tester.dart @@ -0,0 +1,5 @@ +export 'src/platform/iris_tester_expect.dart' + if (dart.library.io) 'src/platform/io/iris_tester_actual_io.dart' + if (dart.library.js) 'src/platform/web/iris_tester_actual_web.dart'; + +export 'src/platform/iris_tester_interface.dart'; diff --git a/test_shard/iris_tester/lib/iris_tester_web.dart b/test_shard/iris_tester/lib/iris_tester_web.dart new file mode 100644 index 0000000..6600b26 --- /dev/null +++ b/test_shard/iris_tester/lib/iris_tester_web.dart @@ -0,0 +1,15 @@ +// In order to *not* need this ignore, consider extracting the "web" version +// of your plugin as a separate package, instead of inlining it in the same +// package as the core of your plugin. +// ignore: avoid_web_libraries_in_flutter + +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +class IrisTesterWeb { + IrisTesterWeb(); + + // ignore: public_member_api_docs + static void registerWith(Registrar registrar) { + // do nothing. + } +} diff --git a/test_shard/iris_tester/lib/src/iris_tester_bindings.dart b/test_shard/iris_tester/lib/src/iris_tester_bindings.dart new file mode 100644 index 0000000..14b9a4b --- /dev/null +++ b/test_shard/iris_tester/lib/src/iris_tester_bindings.dart @@ -0,0 +1,96 @@ +// ignore_for_file: camel_case_types, non_constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; + +/// Bindings to IrisApiEngine +class NativeIrisTesterRtmBinding { + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeIrisTesterRtmBinding(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeIrisTesterRtmBinding.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + ffi.Pointer CreateFakeRtmClient() { + return _CreateFakeRtmClient(); + } + + late final _CreateFakeRtmClientPtr = + _lookup Function()>>( + 'CreateFakeRtmClient'); + late final _CreateFakeRtmClient = + _CreateFakeRtmClientPtr.asFunction Function()>(); + + int TriggerEventWithFakeRtmClient( + ffi.Pointer rtm_client, + ffi.Pointer param, + ) { + return _TriggerEventWithFakeRtmClient( + rtm_client, + param, + ); + } + + late final _TriggerEventWithFakeRtmClientPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('TriggerEventWithFakeRtmClient'); + late final _TriggerEventWithFakeRtmClient = _TriggerEventWithFakeRtmClientPtr + .asFunction, ffi.Pointer)>(); + + /// @brief Whether read buffer from json. Some frameworks, for example Flutter does not support pass the buffer as a pointer in test yet, + /// maybe support in the future. + void SetShouldReadBufferFromJson( + int readBufferFromJson, + ) { + return _SetShouldReadBufferFromJson( + readBufferFromJson, + ); + } + + late final _SetShouldReadBufferFromJsonPtr = + _lookup>( + 'SetShouldReadBufferFromJson'); + late final _SetShouldReadBufferFromJson = + _SetShouldReadBufferFromJsonPtr.asFunction(); + + int GetShouldReadBufferFromJson() { + return _GetShouldReadBufferFromJson(); + } + + late final _GetShouldReadBufferFromJsonPtr = + _lookup>( + 'GetShouldReadBufferFromJson'); + late final _GetShouldReadBufferFromJson = + _GetShouldReadBufferFromJsonPtr.asFunction(); +} + +typedef ApiParam = EventParam; + +class EventParam extends ffi.Struct { + external ffi.Pointer event; + + external ffi.Pointer data; + + @ffi.Uint32() + external int data_size; + + external ffi.Pointer result; + + external ffi.Pointer> buffer; + + external ffi.Pointer length; + + @ffi.Uint32() + external int buffer_count; +} diff --git a/test_shard/iris_tester/lib/src/platform/io/iris_tester_actual_io.dart b/test_shard/iris_tester/lib/src/platform/io/iris_tester_actual_io.dart new file mode 100644 index 0000000..8987e50 --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/io/iris_tester_actual_io.dart @@ -0,0 +1,4 @@ +import 'package:iris_tester/src/platform/io/iris_tester_io.dart'; +import 'package:iris_tester/src/platform/iris_tester_interface.dart'; + +IrisTester createIrisTester() => IrisTesterIO(); diff --git a/test_shard/iris_tester/lib/src/platform/io/iris_tester_io.dart b/test_shard/iris_tester/lib/src/platform/io/iris_tester_io.dart new file mode 100644 index 0000000..1d2ebd2 --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/io/iris_tester_io.dart @@ -0,0 +1,123 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'dart:convert'; + +import 'dart:ffi' as ffi; +import 'package:ffi/ffi.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:iris_tester/src/platform/iris_tester_interface.dart'; + +import 'iris_tester_rtm_bindings.dart'; + +const int kBasicResultLength = 64 * 1024; + +ffi.DynamicLibrary _loadLib() { + if (Platform.isWindows) { + return ffi.DynamicLibrary.open('IrisDebugger.dll'); + } + + if (Platform.isAndroid) { + return ffi.DynamicLibrary.open("libIrisDebugger.so"); + } + + return ffi.DynamicLibrary.process(); +} + +class IrisTesterIO implements IrisTester { + IrisTesterIO({NativeIrisTesterRtmBinding? nativeIrisTesterBinding}) { + _nativeIrisTesterBinding = + nativeIrisTesterBinding ?? NativeIrisTesterRtmBinding(_loadLib()); + } + late final NativeIrisTesterRtmBinding _nativeIrisTesterBinding; + late ffi.Pointer _fakeRtcEngineHandle; + late ffi.Pointer _fakeRtmEngineHandle; + + @override + List getTesterArgs() { + // ignore: prefer_function_declarations_over_variables + final func = (_) { + return _fakeRtcEngineHandle.address; + }; + return [func]; + } + + @override + void initialize() { + _fakeRtmEngineHandle = _nativeIrisTesterBinding.CreateFakeRtmClient(); + + _fakeRtcEngineHandle = + _nativeIrisTesterBinding.GetDebugProcTableRtm().cast(); + + _nativeIrisTesterBinding.SetShouldReadBufferFromJson(0); + } + + @override + void dispose() {} + + @override + void expectCalled(String funcName, String params) { + final isCalled = using((Arena arena) { + // final ffi.Pointer funcNamePointer = + // funcName.toNativeUtf8(allocator: arena).cast(); + + // final ffi.Pointer paramsPointerUtf8 = + // params.toNativeUtf8(allocator: arena); + // final paramsPointerUtf8Length = paramsPointerUtf8.length; + // final ffi.Pointer paramsPointer = + // paramsPointerUtf8.cast(); + + // ffi.Pointer> bufferListPtr = ffi.nullptr; + + // return _nativeIrisTesterBinding.ExpectCalled(funcNamePointer, + // paramsPointer, paramsPointerUtf8Length, bufferListPtr, 0) == + // 1; + + return true; + }); + + expect(isCalled, isTrue); + } + + @override + bool fireEvent(String eventName, {Map params = const {}}) { + return using((Arena arena) { + final ffi.Pointer resultPointer = + arena.allocate(kBasicResultLength); + + final ffi.Pointer funcNamePointer = + eventName.toNativeUtf8(allocator: arena).cast(); + + final ffi.Pointer paramsPointerUtf8 = + jsonEncode(params).toNativeUtf8(allocator: arena); + final paramsPointerUtf8Length = paramsPointerUtf8.length; + final ffi.Pointer paramsPointer = + paramsPointerUtf8.cast(); + + ffi.Pointer> bufferListPtr = + arena.allocate(ffi.sizeOf()); + ffi.Pointer bufferListLengthPtr = ffi.nullptr; + int bufferLength = 1; + + final apiParam = arena() + ..ref.event = funcNamePointer + ..ref.data = paramsPointer + ..ref.data_size = paramsPointerUtf8Length + ..ref.result = resultPointer + ..ref.buffer = bufferListPtr + ..ref.length = bufferListLengthPtr + ..ref.buffer_count = bufferLength; + + final ret = _nativeIrisTesterBinding.TriggerEventWithFakeRtmClient( + _fakeRtmEngineHandle, apiParam); + + if (ret != 0) { + debugPrint( + '[TriggerEventWithFakeRtmClient] event:$eventName ret: $ret'); + } + + return ret == 0; + }); + } +} diff --git a/test_shard/iris_tester/lib/src/platform/io/iris_tester_rtm_bindings.dart b/test_shard/iris_tester/lib/src/platform/io/iris_tester_rtm_bindings.dart new file mode 100644 index 0000000..0a17c15 --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/io/iris_tester_rtm_bindings.dart @@ -0,0 +1,114 @@ +// ignore_for_file: camel_case_types, non_constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; + +/// Bindings to IrisApiEngine +class NativeIrisTesterRtmBinding { + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeIrisTesterRtmBinding(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeIrisTesterRtmBinding.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + ffi.Pointer CreateFakeRtmClient() { + return _CreateFakeRtmClient(); + } + + late final _CreateFakeRtmClientPtr = + _lookup Function()>>( + 'CreateFakeRtmClient'); + late final _CreateFakeRtmClient = + _CreateFakeRtmClientPtr.asFunction Function()>(); + + int TriggerEventWithFakeRtmClient( + ffi.Pointer rtm_client, + ffi.Pointer param, + ) { + return _TriggerEventWithFakeRtmClient( + rtm_client, + param, + ); + } + + late final _TriggerEventWithFakeRtmClientPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('TriggerEventWithFakeRtmClient'); + late final _TriggerEventWithFakeRtmClient = _TriggerEventWithFakeRtmClientPtr + .asFunction, ffi.Pointer)>(); + + /// @brief Whether read buffer from json. Some frameworks, for example Flutter does not support pass the buffer as a pointer in test yet, + /// maybe support in the future. + void SetShouldReadBufferFromJson( + int readBufferFromJson, + ) { + return _SetShouldReadBufferFromJson( + readBufferFromJson, + ); + } + + late final _SetShouldReadBufferFromJsonPtr = + _lookup>( + 'SetShouldReadBufferFromJson'); + late final _SetShouldReadBufferFromJson = + _SetShouldReadBufferFromJsonPtr.asFunction(); + + int GetShouldReadBufferFromJson() { + return _GetShouldReadBufferFromJson(); + } + + late final _GetShouldReadBufferFromJsonPtr = + _lookup>( + 'GetShouldReadBufferFromJson'); + late final _GetShouldReadBufferFromJson = + _GetShouldReadBufferFromJsonPtr.asFunction(); + + ffi.Pointer GetDebugProcTableRtm() { + return _GetDebugProcTableRtm(); + } + + late final _GetDebugProcTableRtmPtr = + _lookup Function()>>( + 'GetDebugProcTableRtm'); + late final _GetDebugProcTableRtm = _GetDebugProcTableRtmPtr.asFunction< + ffi.Pointer Function()>(); +} + +typedef ApiParam = EventParam; + +class EventParam extends ffi.Struct { + external ffi.Pointer event; + + external ffi.Pointer data; + + @ffi.Uint32() + external int data_size; + + external ffi.Pointer result; + + external ffi.Pointer> buffer; + + external ffi.Pointer length; + + @ffi.Uint32() + external int buffer_count; +} + +class ProcTableRtm extends ffi.Struct { + external CreateAgoraRtmClient createAgoraRtmClient; +} + +typedef CreateAgoraRtmClient = ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, ffi.Int32)>>; diff --git a/test_shard/iris_tester/lib/src/platform/iris_tester_expect.dart b/test_shard/iris_tester/lib/src/platform/iris_tester_expect.dart new file mode 100644 index 0000000..98d1350 --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/iris_tester_expect.dart @@ -0,0 +1,4 @@ +import 'package:iris_tester/src/platform/iris_tester_interface.dart'; + +IrisTester createIrisTester() => + throw UnimplementedError('Unimplemented createIrisTester'); diff --git a/test_shard/iris_tester/lib/src/platform/iris_tester_interface.dart b/test_shard/iris_tester/lib/src/platform/iris_tester_interface.dart new file mode 100644 index 0000000..65a938c --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/iris_tester_interface.dart @@ -0,0 +1,13 @@ +typedef TesterArgsProvider = Object Function(Object apiEngineHandle); + +abstract class IrisTester { + void initialize(); + + List getTesterArgs(); + + void dispose(); + + void expectCalled(String funcName, String params); + + bool fireEvent(String eventName, {Map params = const {}}); +} diff --git a/test_shard/iris_tester/lib/src/platform/web/iris_tester_actual_web.dart b/test_shard/iris_tester/lib/src/platform/web/iris_tester_actual_web.dart new file mode 100644 index 0000000..386fe0b --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/web/iris_tester_actual_web.dart @@ -0,0 +1,4 @@ +import 'package:iris_tester/src/platform/iris_tester_interface.dart'; +import 'package:iris_tester/src/platform/web/iris_tester_web.dart'; + +IrisTester createIrisTester() => IrisTesterWeb(); diff --git a/test_shard/iris_tester/lib/src/platform/web/iris_tester_bindings_web.dart b/test_shard/iris_tester/lib/src/platform/web/iris_tester_bindings_web.dart new file mode 100644 index 0000000..1bea242 --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/web/iris_tester_bindings_web.dart @@ -0,0 +1,42 @@ +// ignore_for_file: non_constant_identifier_names + +@JS() +library iris_tester_web; + +import 'package:js/js.dart'; + +/// Copy of the one define in the `iris_method_channel` +@JS('IrisCore.EventParam') +@anonymous +class EventParam { + // Must have an unnamed factory constructor with named arguments. + external factory EventParam({ + String event, + String data, + int data_size, + String result, + List buffer, + List length, + int buffer_count, + }); + + external String get event; + external String get data; + external int get data_size; + external String get result; + external List get buffer; + external List get length; + external int get buffer_count; +} + +@JS('createIrisRtcEngineFake') +external Object createIrisRtcEngineFake(Object irisApiEngine); + +@JS('irisMock') +external void irisMock(); + +@JS('triggerEventWithFakeApiEngine') +external int triggerEventWithFakeApiEngine( + String func_name, + EventParam parameters, +); diff --git a/test_shard/iris_tester/lib/src/platform/web/iris_tester_web.dart b/test_shard/iris_tester/lib/src/platform/web/iris_tester_web.dart new file mode 100644 index 0000000..736444e --- /dev/null +++ b/test_shard/iris_tester/lib/src/platform/web/iris_tester_web.dart @@ -0,0 +1,57 @@ +import 'dart:convert'; + +import 'package:iris_tester/src/platform/iris_tester_interface.dart'; +import 'package:iris_tester/src/platform/web/iris_tester_bindings_web.dart' + as bindings; + +class IrisTesterWeb implements IrisTester { + late Object _irisRtcEngineFake; + + bool isCreatedIrisRtcEngineFake = false; + + @override + List getTesterArgs() { + return [_lazyCreateIrisRtcEngineFake]; + } + + Object _lazyCreateIrisRtcEngineFake(Object irisApiEngine) { + if (isCreatedIrisRtcEngineFake) { + return _irisRtcEngineFake; + } + + isCreatedIrisRtcEngineFake = true; + _irisRtcEngineFake = bindings.createIrisRtcEngineFake(irisApiEngine); + bindings.irisMock(); + + return _irisRtcEngineFake; + } + + @override + void initialize() {} + + @override + void dispose() { + isCreatedIrisRtcEngineFake = false; + } + + @override + void expectCalled(String funcName, String params) {} + + @override + bool fireEvent(String eventName, {Map params = const {}}) { + final pJson = jsonEncode(params); + final param = bindings.EventParam( + event: eventName, + data: pJson, + data_size: pJson.length, + result: '', + buffer: [], + length: [], + buffer_count: 0); + + final ret = bindings.triggerEventWithFakeApiEngine(eventName, param); + + // TODO(littlegnal): Allow not supported calls at this time. + return ret == 0 || ret == -4; + } +} diff --git a/test_shard/iris_tester/macos/Classes/File.swift b/test_shard/iris_tester/macos/Classes/File.swift new file mode 100644 index 0000000..e69de29 diff --git a/test_shard/iris_tester/macos/Classes/IrisTesterPlugin.h b/test_shard/iris_tester/macos/Classes/IrisTesterPlugin.h new file mode 100644 index 0000000..eb70849 --- /dev/null +++ b/test_shard/iris_tester/macos/Classes/IrisTesterPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface IrisTesterPlugin : NSObject +@end diff --git a/test_shard/iris_tester/macos/Classes/IrisTesterPlugin.m b/test_shard/iris_tester/macos/Classes/IrisTesterPlugin.m new file mode 100644 index 0000000..22c30e2 --- /dev/null +++ b/test_shard/iris_tester/macos/Classes/IrisTesterPlugin.m @@ -0,0 +1,7 @@ +#import "IrisTesterPlugin.h" + +@implementation IrisTesterPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + // [SwiftIrisTesterPlugin registerWithRegistrar:registrar]; +} +@end diff --git a/test_shard/iris_tester/macos/iris_tester.podspec b/test_shard/iris_tester/macos/iris_tester.podspec new file mode 100644 index 0000000..747eeb5 --- /dev/null +++ b/test_shard/iris_tester/macos/iris_tester.podspec @@ -0,0 +1,24 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint iris_tester.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'iris_tester' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + s.source = { :path => '.' } + s.source_files = 'Classes/**/*', '../cxx/src/*.h' + s.dependency 'FlutterMacOS' + s.vendored_frameworks = 'IrisDebugger.framework' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' +end diff --git a/test_shard/iris_tester/pubspec.yaml b/test_shard/iris_tester/pubspec.yaml new file mode 100644 index 0000000..44bb588 --- /dev/null +++ b/test_shard/iris_tester/pubspec.yaml @@ -0,0 +1,84 @@ +name: iris_tester +description: A new Flutter plugin project. +version: 0.0.1 +homepage: + +environment: + sdk: '>=2.14.0 <3.0.0' + flutter: '>=2.0.0' + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + plugin_platform_interface: ^2.0.2 + ffi: '>=1.1.2' + js: ^0.6.3 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 + ffigen: ^4.1.2 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: com.example.iris_tester + pluginClass: IrisTesterPlugin + ios: + pluginClass: IrisTesterPlugin + macos: + pluginClass: IrisTesterPlugin + windows: + pluginClass: IrisTesterPluginCApi + web: + pluginClass: IrisTesterWeb + fileName: iris_tester_web.dart + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/test_shard/iris_tester/windows/.gitignore b/test_shard/iris_tester/windows/.gitignore new file mode 100644 index 0000000..b3eb2be --- /dev/null +++ b/test_shard/iris_tester/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# 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/test_shard/iris_tester/windows/CMakeLists.txt b/test_shard/iris_tester/windows/CMakeLists.txt new file mode 100644 index 0000000..1623ccf --- /dev/null +++ b/test_shard/iris_tester/windows/CMakeLists.txt @@ -0,0 +1,53 @@ +# 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 "iris_tester") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "iris_tester_plugin") + +# Any new source files that you add to the plugin should be added here. +list(APPEND PLUGIN_SOURCES + "iris_tester_plugin.cpp" + "iris_tester_plugin.h" +) + +# Define the plugin library target. Its name must not be changed (see comment +# on PLUGIN_NAME above). +add_library(${PLUGIN_NAME} SHARED + "include/iris_tester/iris_tester_plugin_c_api.h" + "iris_tester_plugin_c_api.cpp" + ${PLUGIN_SOURCES} +) + +# 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} PRIVATE FLUTTER_PLUGIN_IMPL) + +# Source include directories and library dependencies. Add any plugin-specific +# dependencies here. +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE 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(iris_tester_bundled_libraries + "${CMAKE_CURRENT_SOURCE_DIR}/IrisDebugger.dll" + PARENT_SCOPE +) diff --git a/test_shard/iris_tester/windows/include/iris_tester/iris_tester_plugin_c_api.h b/test_shard/iris_tester/windows/include/iris_tester/iris_tester_plugin_c_api.h new file mode 100644 index 0000000..2a548b4 --- /dev/null +++ b/test_shard/iris_tester/windows/include/iris_tester/iris_tester_plugin_c_api.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_IRIS_TESTER_PLUGIN_C_API_H_ +#define FLUTTER_PLUGIN_IRIS_TESTER_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 IrisTesterPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_IRIS_TESTER_PLUGIN_C_API_H_ diff --git a/test_shard/iris_tester/windows/iris_tester_plugin.cpp b/test_shard/iris_tester/windows/iris_tester_plugin.cpp new file mode 100644 index 0000000..92588a0 --- /dev/null +++ b/test_shard/iris_tester/windows/iris_tester_plugin.cpp @@ -0,0 +1,59 @@ +#include "iris_tester_plugin.h" + +// This must be included before many other Windows headers. +#include + +// For getPlatformVersion; remove unless needed for your plugin implementation. +#include + +#include +#include +#include + +#include +#include + +namespace iris_tester { + +// static +void IrisTesterPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "iris_tester", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +IrisTesterPlugin::IrisTesterPlugin() {} + +IrisTesterPlugin::~IrisTesterPlugin() {} + +void IrisTesterPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("getPlatformVersion") == 0) { + std::ostringstream version_stream; + version_stream << "Windows "; + if (IsWindows10OrGreater()) { + version_stream << "10+"; + } else if (IsWindows8OrGreater()) { + version_stream << "8"; + } else if (IsWindows7OrGreater()) { + version_stream << "7"; + } + result->Success(flutter::EncodableValue(version_stream.str())); + } else { + result->NotImplemented(); + } +} + +} // namespace iris_tester diff --git a/test_shard/iris_tester/windows/iris_tester_plugin.h b/test_shard/iris_tester/windows/iris_tester_plugin.h new file mode 100644 index 0000000..fe20102 --- /dev/null +++ b/test_shard/iris_tester/windows/iris_tester_plugin.h @@ -0,0 +1,32 @@ +#ifndef FLUTTER_PLUGIN_IRIS_TESTER_PLUGIN_H_ +#define FLUTTER_PLUGIN_IRIS_TESTER_PLUGIN_H_ + +#include +#include + +#include + +namespace iris_tester { + +class IrisTesterPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); + + IrisTesterPlugin(); + + virtual ~IrisTesterPlugin(); + + // Disallow copy and assign. + IrisTesterPlugin(const IrisTesterPlugin&) = delete; + IrisTesterPlugin& operator=(const IrisTesterPlugin&) = delete; + + private: + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); +}; + +} // namespace iris_tester + +#endif // FLUTTER_PLUGIN_IRIS_TESTER_PLUGIN_H_ diff --git a/test_shard/iris_tester/windows/iris_tester_plugin_c_api.cpp b/test_shard/iris_tester/windows/iris_tester_plugin_c_api.cpp new file mode 100644 index 0000000..65fa285 --- /dev/null +++ b/test_shard/iris_tester/windows/iris_tester_plugin_c_api.cpp @@ -0,0 +1,12 @@ +#include "include/iris_tester/iris_tester_plugin_c_api.h" + +#include + +#include "iris_tester_plugin.h" + +void IrisTesterPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + iris_tester::IrisTesterPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +}