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).
+
+
+
+
+ 
+
+
+
+
+
+
+
+
+
+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